1.1.9 - Add MQTT system trouble, thermostat type, and area alarm activation

This commit is contained in:
Ryan Wagoner 2020-11-05 21:59:30 -05:00
parent 85c549d5dd
commit 1cf77ba179
16 changed files with 331 additions and 103 deletions

View file

@ -0,0 +1,9 @@
namespace OmniLinkBridge.MQTT
{
enum AlarmCommands
{
burglary = 1,
fire = 2,
auxiliary = 3
}
}

View file

@ -1,5 +1,6 @@
using HAI_Shared; using HAI_Shared;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic;
namespace OmniLinkBridge.MQTT namespace OmniLinkBridge.MQTT
{ {
@ -31,24 +32,16 @@ namespace OmniLinkBridge.MQTT
else if (area.ExitTimer > 0) else if (area.ExitTimer > 0)
return "pending"; return "pending";
switch (area.AreaMode) return area.AreaMode switch
{ {
case enuSecurityMode.Night: enuSecurityMode.Night => "armed_night",
return "armed_night"; enuSecurityMode.NightDly => "armed_night_delay",
case enuSecurityMode.NightDly: enuSecurityMode.Day => "armed_home",
return "armed_night_delay"; enuSecurityMode.DayInst => "armed_home_instant",
case enuSecurityMode.Day: enuSecurityMode.Away => "armed_away",
return "armed_home"; enuSecurityMode.Vacation => "armed_vacation",
case enuSecurityMode.DayInst: _ => "disarmed",
return "armed_home_instant"; };
case enuSecurityMode.Away:
return "armed_away";
case enuSecurityMode.Vacation:
return "armed_vacation";
case enuSecurityMode.Off:
default:
return "disarmed";
}
} }
public static string ToBasicState(this clsArea area) public static string ToBasicState(this clsArea area)
@ -196,32 +189,16 @@ namespace OmniLinkBridge.MQTT
temperature_alarm = area.AreaAlarms.IsBitSet(7) temperature_alarm = area.AreaAlarms.IsBitSet(7)
}; };
switch (area.AreaMode) state.mode = area.AreaMode switch
{ {
case enuSecurityMode.Night: enuSecurityMode.Night => "night",
state.mode = "night"; enuSecurityMode.NightDly => "night_delay",
break; enuSecurityMode.Day => "home",
case enuSecurityMode.NightDly: enuSecurityMode.DayInst => "home_instant",
state.mode = "night_delay"; enuSecurityMode.Away => "away",
break; enuSecurityMode.Vacation => "vacation",
case enuSecurityMode.Day: _ => "off",
state.mode = "home"; };
break;
case enuSecurityMode.DayInst:
state.mode = "home_instant";
break;
case enuSecurityMode.Away:
state.mode = "away";
break;
case enuSecurityMode.Vacation:
state.mode = "vacation";
break;
case enuSecurityMode.Off:
default:
state.mode = "off";
break;
}
return JsonConvert.SerializeObject(state); return JsonConvert.SerializeObject(state);
} }
@ -456,9 +433,19 @@ namespace OmniLinkBridge.MQTT
public static Climate ToConfig(this clsThermostat thermostat, enuTempFormat format) public static Climate ToConfig(this clsThermostat thermostat, enuTempFormat format)
{ {
Climate ret = new Climate(); Climate ret = new Climate
{
modes = thermostat.Type switch
{
enuThermostatType.AutoHeatCool => new List<string>(new string[] { "auto", "off", "cool", "heat" }),
enuThermostatType.HeatCool => new List<string>(new string[] { "off", "cool", "heat" }),
enuThermostatType.HeatOnly => new List<string>(new string[] { "off", "heat" }),
enuThermostatType.CoolOnly => new List<string>(new string[] { "off", "cool" }),
_ => new List<string>(new string[] { "off" }),
}
};
if(format == enuTempFormat.Celsius) if (format == enuTempFormat.Celsius)
{ {
ret.min_temp = "7"; ret.min_temp = "7";
ret.max_temp = "35"; ret.max_temp = "35";

View file

@ -72,6 +72,16 @@ namespace OmniLinkBridge.MQTT
log.Debug("SetArea: {id} to {value}", area.Number, 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); OmniLink.SendCommand(AreaMapping[cmd], 0, (ushort)area.Number);
} }
else if (command == Topic.alarm_command && area.Number > 0 && Enum.TryParse(payload, true, out AlarmCommands alarm))
{
log.Debug("SetAreaAlarm: {id} to {value}", area.Number, payload);
OmniLink.Controller.Connection.Send(new clsOL2MsgActivateKeypadEmg(OmniLink.Controller.Connection)
{
Area = (byte)area.Number,
EmgType = (byte)alarm
}, (M, B, Timeout) => { });
}
} }
private static readonly IDictionary<ZoneCommands, enuUnitCommand> ZoneMapping = new Dictionary<ZoneCommands, enuUnitCommand> private static readonly IDictionary<ZoneCommands, enuUnitCommand> ZoneMapping = new Dictionary<ZoneCommands, enuUnitCommand>
@ -169,8 +179,17 @@ namespace OmniLinkBridge.MQTT
} }
else if (command == Topic.mode_command && Enum.TryParse(payload, true, out enuThermostatMode mode)) else if (command == Topic.mode_command && Enum.TryParse(payload, true, out enuThermostatMode mode))
{ {
log.Debug("SetThermostatMode: {id} to {value}", thermostat.Number, payload); if (thermostat.Type == enuThermostatType.AutoHeatCool ||
OmniLink.SendCommand(enuUnitCommand.Mode, BitConverter.GetBytes((int)mode)[0], (ushort)thermostat.Number); (thermostat.Type == enuThermostatType.HeatCool && mode != enuThermostatMode.Auto) ||
(thermostat.Type == enuThermostatType.CoolOnly &&
(mode == enuThermostatMode.Off || mode == enuThermostatMode.Cool)) ||
(thermostat.Type == enuThermostatType.HeatOnly &&
(mode == enuThermostatMode.Off || mode == enuThermostatMode.Heat || mode == enuThermostatMode.E_Heat)) ||
mode == enuThermostatMode.Off)
{
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)) else if (command == Topic.fan_mode_command && Enum.TryParse(payload, true, out enuThermostatFanMode fanMode))
{ {

View file

@ -5,6 +5,7 @@
name, name,
state, state,
command, command,
alarm_command,
basic_state, basic_state,
json_state, json_state,
brightness_state, brightness_state,
@ -27,6 +28,6 @@
fan_mode_state, fan_mode_state,
fan_mode_command, fan_mode_command,
hold_state, hold_state,
hold_command hold_command,
} }
} }

View file

@ -298,7 +298,7 @@ namespace OmniLinkBridge.Modules
VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + e.Type.ToString() + "','" + e.Value + "')"); VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + e.Type.ToString() + "','" + e.Value + "')");
if (Global.verbose_event) if (Global.verbose_event)
log.Verbose("SystemEvent {name} {status}", e.Type.ToString(), e.Value); log.Verbose("SystemEvent {name}, Status: {status}", e.Type.ToString(), e.Value);
if (e.SendNotification) if (e.SendNotification)
Notification.Notify("SystemEvent", e.Type.ToString() + " " + e.Value); Notification.Notify("SystemEvent", e.Type.ToString() + " " + e.Value);

View file

@ -47,6 +47,7 @@ namespace OmniLinkBridge.Modules
OmniLink.OnThermostatStatus += Omnilink_OnThermostatStatus; OmniLink.OnThermostatStatus += Omnilink_OnThermostatStatus;
OmniLink.OnButtonStatus += OmniLink_OnButtonStatus; OmniLink.OnButtonStatus += OmniLink_OnButtonStatus;
OmniLink.OnMessageStatus += OmniLink_OnMessageStatus; OmniLink.OnMessageStatus += OmniLink_OnMessageStatus;
OmniLink.OnSystemStatus += OmniLink_OnSystemStatus;
MessageProcessor = new MessageProcessor(omni); MessageProcessor = new MessageProcessor(omni);
} }
@ -105,6 +106,7 @@ namespace OmniLinkBridge.Modules
List<Topic> toSubscribe = new List<Topic>() List<Topic> toSubscribe = new List<Topic>()
{ {
Topic.command, Topic.command,
Topic.alarm_command,
Topic.brightness_command, Topic.brightness_command,
Topic.scene_command, Topic.scene_command,
Topic.temperature_heat_command, Topic.temperature_heat_command,
@ -117,7 +119,7 @@ namespace OmniLinkBridge.Modules
}; };
toSubscribe.ForEach((command) => MqttClient.SubscribeAsync( toSubscribe.ForEach((command) => MqttClient.SubscribeAsync(
new TopicFilterBuilder().WithTopic($"{Global.mqtt_prefix}/+/{command}").Build())); new MqttTopicFilterBuilder().WithTopic($"{Global.mqtt_prefix}/+/{command}").Build()));
// Wait until shutdown // Wait until shutdown
trigger.WaitOne(); trigger.WaitOne();
@ -156,6 +158,7 @@ namespace OmniLinkBridge.Modules
private void PublishConfig() private void PublishConfig()
{ {
PublishSystem();
PublishAreas(); PublishAreas();
PublishZones(); PublishZones();
PublishUnits(); PublishUnits();
@ -168,6 +171,41 @@ namespace OmniLinkBridge.Modules
PublishAsync($"{Global.mqtt_prefix}/version", OmniLink.Controller.GetVersionText()); PublishAsync($"{Global.mqtt_prefix}/version", OmniLink.Controller.GetVersionText());
} }
private void PublishSystem()
{
log.Debug("Publishing {type}", "system");
PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_phone/config",
JsonConvert.SerializeObject(SystemTroubleConfig("phone", "Phone")));
PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_ac/config",
JsonConvert.SerializeObject(SystemTroubleConfig("ac", "AC")));
PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_battery/config",
JsonConvert.SerializeObject(SystemTroubleConfig("battery", "Battery")));
PublishAsync($"{Global.mqtt_discovery_prefix}/binary_sensor/{Global.mqtt_prefix}/system_dcm/config",
JsonConvert.SerializeObject(SystemTroubleConfig("dcm", "DCM")));
PublishAsync(SystemTroubleTopic("phone"), OmniLink.TroublePhone ? "trouble" : "secure");
PublishAsync(SystemTroubleTopic("ac"), OmniLink.TroubleAC ? "trouble" : "secure");
PublishAsync(SystemTroubleTopic("battery"), OmniLink.TroubleBattery ? "trouble" : "secure");
PublishAsync(SystemTroubleTopic("dcn"), OmniLink.TroubleDCM ? "trouble" : "secure");
}
public string SystemTroubleTopic(string type)
{
return $"{Global.mqtt_prefix}/system/{type}/{Topic.state}";
}
public BinarySensor SystemTroubleConfig(string type, string name)
{
return new BinarySensor
{
unique_id = $"{Global.mqtt_prefix}system{type}",
name = $"{Global.mqtt_discovery_name_prefix} System {name}",
state_topic = SystemTroubleTopic(type),
device_class = BinarySensor.DeviceClass.problem
};
}
private void PublishAreas() private void PublishAreas()
{ {
log.Debug("Publishing {type}", "areas"); log.Debug("Publishing {type}", "areas");
@ -441,6 +479,21 @@ namespace OmniLinkBridge.Modules
PublishMessageState(e.Message); PublishMessageState(e.Message);
} }
private void OmniLink_OnSystemStatus(object sender, SystemStatusEventArgs e)
{
if (!MqttClient.IsConnected)
return;
if(e.Type == SystemEventType.Phone)
PublishAsync(SystemTroubleTopic("phone"), e.Trouble ? "trouble" : "secure");
else if (e.Type == SystemEventType.AC)
PublishAsync(SystemTroubleTopic("ac"), e.Trouble ? "trouble" : "secure");
else if (e.Type == SystemEventType.Button)
PublishAsync(SystemTroubleTopic("battery"), e.Trouble ? "trouble" : "secure");
else if (e.Type == SystemEventType.DCM)
PublishAsync(SystemTroubleTopic("dcm"), e.Trouble ? "trouble" : "secure");
}
private void PublishAreaState(clsArea area) private void PublishAreaState(clsArea area)
{ {
PublishAsync(area.ToTopic(Topic.state), area.ToState()); PublishAsync(area.ToTopic(Topic.state), area.ToState());

View file

@ -21,6 +21,11 @@ namespace OmniLinkBridge.Modules
public clsHAC Controller { get; private set; } public clsHAC Controller { get; private set; }
private DateTime retry = DateTime.MinValue; private DateTime retry = DateTime.MinValue;
public bool TroublePhone { get; set; }
public bool TroubleAC { get; set; }
public bool TroubleBattery { get; set; }
public bool TroubleDCM { get; set; }
// Thermostats // Thermostats
private readonly Dictionary<ushort, DateTime> tstats = new Dictionary<ushort, DateTime>(); private readonly Dictionary<ushort, DateTime> tstats = new Dictionary<ushort, DateTime>();
private readonly System.Timers.Timer tstat_timer = new System.Timers.Timer(); private readonly System.Timers.Timer tstat_timer = new System.Timers.Timer();
@ -254,6 +259,7 @@ namespace OmniLinkBridge.Modules
log.Debug("Retrieving named units"); log.Debug("Retrieving named units");
await GetSystemFormats(); await GetSystemFormats();
await GetSystemTroubles();
await GetNamed(enuObjectType.Area); await GetNamed(enuObjectType.Area);
await GetNamed(enuObjectType.Zone); await GetNamed(enuObjectType.Zone);
await GetNamed(enuObjectType.Thermostat); await GetNamed(enuObjectType.Thermostat);
@ -266,7 +272,20 @@ namespace OmniLinkBridge.Modules
{ {
log.Debug("Waiting for system formats"); log.Debug("Waiting for system formats");
clsOL2MsgRequestSystemFormats MSG = new clsOL2MsgRequestSystemFormats(Controller.Connection); var MSG = new clsOL2MsgRequestSystemFormats(Controller.Connection);
Controller.Connection.Send(MSG, HandleNamedPropertiesResponse);
await Task.Run(() =>
{
nameWait.WaitOne(new TimeSpan(0, 0, 10));
});
}
private async Task GetSystemTroubles()
{
log.Debug("Waiting for system troubles");
var MSG = new clsOL2MsgRequestSystemTroubles(Controller.Connection);
Controller.Connection.Send(MSG, HandleNamedPropertiesResponse); Controller.Connection.Send(MSG, HandleNamedPropertiesResponse);
await Task.Run(() => await Task.Run(() =>
@ -289,7 +308,7 @@ namespace OmniLinkBridge.Modules
private void GetNextNamed(enuObjectType type, int ix) private void GetNextNamed(enuObjectType type, int ix)
{ {
clsOL2MsgRequestProperties MSG = new clsOL2MsgRequestProperties(Controller.Connection) var MSG = new clsOL2MsgRequestProperties(Controller.Connection)
{ {
ObjectType = type, ObjectType = type,
IndexNumber = (UInt16)ix, IndexNumber = (UInt16)ix,
@ -315,16 +334,26 @@ namespace OmniLinkBridge.Modules
nameWait.Set(); nameWait.Set();
break; break;
case enuOmniLink2MessageType.SystemFormats: case enuOmniLink2MessageType.SystemFormats:
clsOL2MsgSystemFormats MSG2 = new clsOL2MsgSystemFormats(Controller.Connection, B); var systemFormats = new clsOL2MsgSystemFormats(Controller.Connection, B);
Controller.DateFormat = MSG2.Date; Controller.DateFormat = systemFormats.Date;
Controller.TimeFormat = MSG2.Time; Controller.TimeFormat = systemFormats.Time;
Controller.TempFormat = MSG2.Temp; Controller.TempFormat = systemFormats.Temp;
using (LogContext.PushProperty("Telemetry", "TemperatureFormat")) using (LogContext.PushProperty("Telemetry", "TemperatureFormat"))
log.Debug("Temperature format is {TemperatureFormat}", log.Debug("Temperature format is {TemperatureFormat}",
(Controller.TempFormat == enuTempFormat.Fahrenheit ? "Fahrenheit" : "Celsius")); (Controller.TempFormat == enuTempFormat.Fahrenheit ? "Fahrenheit" : "Celsius"));
nameWait.Set();
break;
case enuOmniLink2MessageType.SystemTroubles:
var systemTroubles = new clsOL2MsgSystemTroubles(Controller.Connection, B);
TroublePhone = systemTroubles.Contains(enuTroubles.PhoneLine);
TroubleAC = systemTroubles.Contains(enuTroubles.AC);
TroubleBattery = systemTroubles.Contains(enuTroubles.BatteryLow);
TroubleDCM = systemTroubles.Contains(enuTroubles.DCM);
nameWait.Set(); nameWait.Set();
break; break;
case enuOmniLink2MessageType.Properties: case enuOmniLink2MessageType.Properties:
@ -473,7 +502,7 @@ namespace OmniLinkBridge.Modules
if (MSG.SystemEvent >= 1 && MSG.SystemEvent <= 255) if (MSG.SystemEvent >= 1 && MSG.SystemEvent <= 255)
{ {
eventargs.Type = enuEventType.USER_MACRO_BUTTON; eventargs.Type = SystemEventType.Button;
eventargs.Value = ((int)MSG.SystemEvent).ToString() + " " + Controller.Buttons[MSG.SystemEvent].Name; eventargs.Value = ((int)MSG.SystemEvent).ToString() + " " + Controller.Buttons[MSG.SystemEvent].Name;
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
@ -484,87 +513,103 @@ namespace OmniLinkBridge.Modules
Button = Controller.Buttons[MSG.SystemEvent] Button = Controller.Buttons[MSG.SystemEvent]
}); });
} }
else if (MSG.SystemEvent >= 768 && MSG.SystemEvent <= 771) else if (MSG.SystemEvent >= (ushort)enuEventType.PHONE_LINE_DEAD &&
MSG.SystemEvent <= (ushort)enuEventType.PHONE_LINE_ON_HOOK)
{ {
eventargs.Type = enuEventType.PHONE_; eventargs.Type = SystemEventType.Phone;
if (MSG.SystemEvent == 768) if (MSG.SystemEvent == (ushort)enuEventType.PHONE_)
{ {
eventargs.Value = "DEAD"; eventargs.Value = "DEAD";
eventargs.Trouble = true;
eventargs.SendNotification = true; eventargs.SendNotification = true;
} }
else if (MSG.SystemEvent == 769) else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_RING)
eventargs.Value = "RING"; eventargs.Value = "RING";
else if (MSG.SystemEvent == 770) else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_OFF_HOOK)
eventargs.Value = "OFF HOOK"; eventargs.Value = "OFF HOOK";
else if (MSG.SystemEvent == 771) else if (MSG.SystemEvent == (ushort)enuEventType.PHONE_LINE_ON_HOOK)
eventargs.Value = "ON HOOK"; eventargs.Value = "ON HOOK";
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 772 && MSG.SystemEvent <= 773) else if (MSG.SystemEvent >= (ushort)enuEventType.AC_POWER_OFF &&
MSG.SystemEvent <= (ushort)enuEventType.AC_POWER_RESTORED)
{ {
eventargs.Type = enuEventType.AC_POWER_; eventargs.Type = SystemEventType.AC;
eventargs.SendNotification = true; eventargs.SendNotification = true;
if (MSG.SystemEvent == 772) if (MSG.SystemEvent == (ushort)enuEventType.AC_POWER_OFF)
{
eventargs.Value = "OFF"; eventargs.Value = "OFF";
else if (MSG.SystemEvent == 773) eventargs.Trouble = true;
}
else if (MSG.SystemEvent == (ushort)enuEventType.AC_POWER_RESTORED)
eventargs.Value = "RESTORED"; eventargs.Value = "RESTORED";
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 774 && MSG.SystemEvent <= 775) else if (MSG.SystemEvent >= (ushort)enuEventType.BATTERY_LOW &&
MSG.SystemEvent <= (ushort)enuEventType.BATTERY_OK)
{ {
eventargs.Type = enuEventType.BATTERY_; eventargs.Type = SystemEventType.Battery;
eventargs.SendNotification = true; eventargs.SendNotification = true;
if (MSG.SystemEvent == 774) if (MSG.SystemEvent == (ushort)enuEventType.BATTERY_LOW)
{
eventargs.Value = "LOW"; eventargs.Value = "LOW";
else if (MSG.SystemEvent == 775) eventargs.Trouble = true;
}
else if (MSG.SystemEvent == (ushort)enuEventType.BATTERY_OK)
eventargs.Value = "OK"; eventargs.Value = "OK";
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 776 && MSG.SystemEvent <= 777) else if (MSG.SystemEvent >= (ushort)enuEventType.DCM_TROUBLE &&
MSG.SystemEvent <= (ushort)enuEventType.DCM_OK)
{ {
eventargs.Type = enuEventType.DCM_; eventargs.Type = SystemEventType.DCM;
eventargs.SendNotification = true; eventargs.SendNotification = true;
if (MSG.SystemEvent == 776) if (MSG.SystemEvent == (ushort)enuEventType.DCM_TROUBLE)
{
eventargs.Value = "TROUBLE"; eventargs.Value = "TROUBLE";
else if (MSG.SystemEvent == 777) eventargs.Trouble = true;
}
else if (MSG.SystemEvent == (ushort)enuEventType.DCM_OK)
eventargs.Value = "OK"; eventargs.Value = "OK";
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 778 && MSG.SystemEvent <= 781) else if (MSG.SystemEvent >= (ushort)enuEventType.ENERGY_COST_LOW &&
MSG.SystemEvent <= (ushort)enuEventType.ENERGY_COST_CRITICAL)
{ {
eventargs.Type = enuEventType.ENERGY_COST_; eventargs.Type = SystemEventType.EnergyCost;
if (MSG.SystemEvent == 778) if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_LOW)
eventargs.Value = "LOW"; eventargs.Value = "LOW";
else if (MSG.SystemEvent == 779) else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_MID)
eventargs.Value = "MID"; eventargs.Value = "MID";
else if (MSG.SystemEvent == 780) else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_HIGH)
eventargs.Value = "HIGH"; eventargs.Value = "HIGH";
else if (MSG.SystemEvent == 781) else if (MSG.SystemEvent == (ushort)enuEventType.ENERGY_COST_CRITICAL)
eventargs.Value = "CRITICAL"; eventargs.Value = "CRITICAL";
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 782 && MSG.SystemEvent <= 787) else if (MSG.SystemEvent >= 782 && MSG.SystemEvent <= 787)
{ {
eventargs.Type = enuEventType.CAMERA; eventargs.Type = SystemEventType.Camera;
eventargs.Value = (MSG.SystemEvent - 781).ToString(); eventargs.Value = (MSG.SystemEvent - 781).ToString();
OnSystemStatus?.Invoke(this, eventargs); OnSystemStatus?.Invoke(this, eventargs);
} }
else if (MSG.SystemEvent >= 61440 && MSG.SystemEvent <= 64511) else if (MSG.SystemEvent >= 61440 && MSG.SystemEvent <= 64511)
{ {
eventargs.Type = enuEventType.SWITCH_PRESS; eventargs.Type = SystemEventType.SwitchPress;
int state = (int)MSG.Data[1] - 240; int state = MSG.Data[1] - 240;
int id = (int)MSG.Data[2]; int id = MSG.Data[2];
eventargs.Value = "Unit: " + id + ", State: " + state; eventargs.Value = "Unit: " + id + ", State: " + state;
@ -572,9 +617,9 @@ namespace OmniLinkBridge.Modules
} }
else if (MSG.SystemEvent >= 64512 && MSG.SystemEvent <= 65535) else if (MSG.SystemEvent >= 64512 && MSG.SystemEvent <= 65535)
{ {
eventargs.Type = enuEventType.UPB_LINK; eventargs.Type = SystemEventType.UPBLink;
int state = (int)MSG.Data[1] - 252; int state = MSG.Data[1] - 252;
int id = (int)MSG.Data[2]; int id = MSG.Data[2];
eventargs.Value = "Link: " + id + ", State: " + state; eventargs.Value = "Link: " + id + ", State: " + state;
@ -587,7 +632,7 @@ namespace OmniLinkBridge.Modules
sb.Append(MSG.Data[i].ToString() + " "); sb.Append(MSG.Data[i].ToString() + " ");
log.Debug("Unhandled SystemEvent Raw: {raw}, Num: {num}", sb.ToString(), MSG.SystemEvent); log.Debug("Unhandled SystemEvent Raw: {raw}, Num: {num}", sb.ToString(), MSG.SystemEvent);
int num = ((int)MSG.MessageLength - 1) / 2; int num = (MSG.MessageLength - 1) / 2;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
log.Debug("Unhandled SystemEvent: " + log.Debug("Unhandled SystemEvent: " +

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OmniLinkBridge.OmniLink
{
public enum SystemEventType
{
Button,
Phone,
AC,
Battery,
DCM,
EnergyCost,
Camera,
SwitchPress,
UPBLink,
}
}

View file

@ -5,8 +5,9 @@ namespace OmniLinkBridge.OmniLink
{ {
public class SystemStatusEventArgs : EventArgs public class SystemStatusEventArgs : EventArgs
{ {
public enuEventType Type { get; set; } public SystemEventType Type { get; set; }
public string Value { get; set; } public string Value { get; set; }
public bool Trouble { get; set; }
public bool SendNotification { get; set; } public bool SendNotification { get; set; }
} }
} }

View file

@ -24,6 +24,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
@ -34,6 +35,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -44,6 +46,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<NoWarn>IDE1006</NoWarn> <NoWarn>IDE1006</NoWarn>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
@ -54,6 +57,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<NoWarn>IDE1006</NoWarn> <NoWarn>IDE1006</NoWarn>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@ -79,6 +83,7 @@
<Compile Include="CoreServer.cs" /> <Compile Include="CoreServer.cs" />
<Compile Include="Modules\TimeSyncModule.cs" /> <Compile Include="Modules\TimeSyncModule.cs" />
<Compile Include="MQTT\Alarm.cs" /> <Compile Include="MQTT\Alarm.cs" />
<Compile Include="MQTT\AlarmCommands.cs" />
<Compile Include="MQTT\AreaCommands.cs" /> <Compile Include="MQTT\AreaCommands.cs" />
<Compile Include="MQTT\AreaState.cs" /> <Compile Include="MQTT\AreaState.cs" />
<Compile Include="MQTT\BinarySensor.cs" /> <Compile Include="MQTT\BinarySensor.cs" />
@ -103,6 +108,7 @@
<Compile Include="Notifications\PushoverNotification.cs" /> <Compile Include="Notifications\PushoverNotification.cs" />
<Compile Include="OmniLink\ButtonStatusEventArgs.cs" /> <Compile Include="OmniLink\ButtonStatusEventArgs.cs" />
<Compile Include="OmniLink\IOmniLinkII.cs" /> <Compile Include="OmniLink\IOmniLinkII.cs" />
<Compile Include="OmniLink\SystemEventType.cs" />
<Compile Include="OmniLink\UnitStatusEventArgs.cs" /> <Compile Include="OmniLink\UnitStatusEventArgs.cs" />
<Compile Include="OmniLink\ThermostatStatusEventArgs.cs" /> <Compile Include="OmniLink\ThermostatStatusEventArgs.cs" />
<Compile Include="OmniLink\MessageStatusEventArgs.cs" /> <Compile Include="OmniLink\MessageStatusEventArgs.cs" />
@ -167,13 +173,13 @@
<Version>4.5.0</Version> <Version>4.5.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="MQTTnet.Extensions.ManagedClient"> <PackageReference Include="MQTTnet.Extensions.ManagedClient">
<Version>3.0.8</Version> <Version>3.0.13</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json"> <PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version> <Version>12.0.3</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Serilog"> <PackageReference Include="Serilog">
<Version>2.9.0</Version> <Version>2.10.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Serilog.Formatting.Compact"> <PackageReference Include="Serilog.Formatting.Compact">
<Version>1.1.0</Version> <Version>1.1.0</Version>
@ -188,7 +194,7 @@
<Version>4.1.0</Version> <Version>4.1.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Serilog.Sinks.Http"> <PackageReference Include="Serilog.Sinks.Http">
<Version>5.2.0</Version> <Version>7.2.0</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

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.8.0")] [assembly: AssemblyVersion("1.1.9.0")]
[assembly: AssemblyFileVersion("1.1.8.0")] [assembly: AssemblyFileVersion("1.1.9.0")]

View file

@ -133,6 +133,70 @@ namespace OmniLinkBridgeTest
check(1, "50", enuUnitCommand.Level, 50); check(1, "50", enuUnitCommand.Level, 50);
} }
[TestMethod]
public void ThermostatModeCommandInvalid()
{
SendCommandEventArgs actual = null;
omniLink.OnSendCommand += (sender, e) => { actual = e; };
omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatCool;
messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto");
Assert.IsNull(actual);
omniLink.Controller.Thermostats[1].Type = enuThermostatType.CoolOnly;
messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto");
Assert.IsNull(actual);
messageProcessor.Process($"omnilink/thermostat1/mode_command", "heat");
Assert.IsNull(actual);
omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatOnly;
messageProcessor.Process($"omnilink/thermostat1/mode_command", "auto");
Assert.IsNull(actual);
messageProcessor.Process($"omnilink/thermostat1/mode_command", "cool");
Assert.IsNull(actual);
}
[TestMethod]
public void ThermostatModeCommand()
{
void check(ushort id, string payload, enuUnitCommand command, int mode)
{
SendCommandEventArgs actual = null;
omniLink.OnSendCommand += (sender, e) => { actual = e; };
messageProcessor.Process($"omnilink/thermostat{id}/mode_command", payload);
SendCommandEventArgs expected = new SendCommandEventArgs()
{
Cmd = command,
Par = (byte)mode,
Pr2 = id
};
Assert.AreEqual(expected, actual);
}
omniLink.Controller.Thermostats[1].Type = enuThermostatType.AutoHeatCool;
check(1, "auto", enuUnitCommand.Mode, (int)enuThermostatMode.Auto);
check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool);
check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat);
check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off);
omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatCool;
check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool);
check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat);
check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off);
omniLink.Controller.Thermostats[1].Type = enuThermostatType.CoolOnly;
check(1, "cool", enuUnitCommand.Mode, (int)enuThermostatMode.Cool);
check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off);
omniLink.Controller.Thermostats[1].Type = enuThermostatType.HeatOnly;
check(1, "heat", enuUnitCommand.Mode, (int)enuThermostatMode.Heat);
check(1, "off", enuUnitCommand.Mode, (int)enuThermostatMode.Off);
}
[TestMethod] [TestMethod]
public void ButtonCommand() public void ButtonCommand()
{ {

View file

@ -16,9 +16,11 @@ namespace OmniLinkBridgeTest.Mock
public MockOmniLinkII() public MockOmniLinkII()
{ {
Controller = new clsHAC(); Controller = new clsHAC
Controller.Model = enuModel.OMNI_PRO_II; {
Controller.TempFormat = enuTempFormat.Fahrenheit; Model = enuModel.OMNI_PRO_II,
TempFormat = enuTempFormat.Fahrenheit
};
} }
public bool SendCommand(enuUnitCommand Cmd, byte Par, ushort Pr2) public bool SendCommand(enuUnitCommand Cmd, byte Par, ushort Pr2)

View file

@ -15,14 +15,12 @@ namespace OmniLinkBridgeTest.Mock
public override bool Equals(object other) public override bool Equals(object other)
{ {
var toCompareWith = other as SendCommandEventArgs; if (!(other is SendCommandEventArgs toCompareWith))
if (toCompareWith == null)
return false; return false;
return this.Cmd == toCompareWith.Cmd && return Cmd == toCompareWith.Cmd &&
this.Par == toCompareWith.Par && Par == toCompareWith.Par &&
this.Pr2 == toCompareWith.Pr2; Pr2 == toCompareWith.Pr2;
} }
public override int GetHashCode() public override int GetHashCode()

View file

@ -29,6 +29,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@ -37,6 +38,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HAI.Controller, Version=3.11.4.17, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="HAI.Controller, Version=3.11.4.17, Culture=neutral, processorArchitecture=MSIL">

View file

@ -146,6 +146,20 @@ systemctl start omnilinkbridge.service
``` ```
## MQTT ## MQTT
### System
```
SUB omnilink/system/phone/state
string secure, trouble
SUB omnilink/system/ac/state
string secure, trouble
SUB omnilink/system/battery/state
string secure, trouble
SUB omnilink/system/dcm/state
string secure, trouble
```
### Areas ### Areas
``` ```
@ -158,8 +172,14 @@ string triggered, pending, armed_night, armed_night_delay, armed_home, armed_hom
SUB omnilink/areaX/basic_state SUB omnilink/areaX/basic_state
string triggered, pending, armed_night, armed_home, armed_away, disarmed string triggered, pending, armed_night, armed_home, armed_away, disarmed
SUB omnilink/areaX/json_state
string json
PUB omnilink/areaX/command PUB omnilink/areaX/command
string arm_home, arm_away, arm_night, disarm, arm_home_instant, arm_night_delay, arm_vacation string arm_home, arm_away, arm_night, disarm, arm_home_instant, arm_night_delay, arm_vacation
PUB omnilink/areaX/alarm_command
string burglary, fire, auxiliary
``` ```
### Zones ### Zones