mirror of
https://github.com/excaliburpartners/OmniLinkBridge
synced 2025-05-06 17:13:11 +00:00
250 lines
12 KiB
C#
250 lines
12 KiB
C#
using HAI_Shared;
|
|
using OmniLinkBridge.OmniLink;
|
|
using Serilog;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace OmniLinkBridge.MQTT
|
|
{
|
|
public class MessageProcessor
|
|
{
|
|
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);
|
|
|
|
private IOmniLinkII OmniLink { get; set; }
|
|
|
|
public MessageProcessor(IOmniLinkII omni)
|
|
{
|
|
OmniLink = omni;
|
|
}
|
|
|
|
public void Process(string messageTopic, string payload)
|
|
{
|
|
Match match = regexTopic.Match(messageTopic);
|
|
|
|
if (!match.Success)
|
|
return;
|
|
|
|
if (!Enum.TryParse(match.Groups[1].Value, true, out CommandTypes type)
|
|
|| !Enum.TryParse(match.Groups[3].Value, true, out Topic topic)
|
|
|| !ushort.TryParse(match.Groups[2].Value, out ushort id))
|
|
return;
|
|
|
|
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);
|
|
else if (type == CommandTypes.zone && id <= OmniLink.Controller.Zones.Count)
|
|
ProcessZoneReceived(OmniLink.Controller.Zones[id], topic, payload);
|
|
else if (type == CommandTypes.unit && id > 0 && id <= OmniLink.Controller.Units.Count)
|
|
ProcessUnitReceived(OmniLink.Controller.Units[id], topic, payload);
|
|
else if (type == CommandTypes.thermostat && id > 0 && id <= OmniLink.Controller.Thermostats.Count)
|
|
ProcessThermostatReceived(OmniLink.Controller.Thermostats[id], topic, payload);
|
|
else if (type == CommandTypes.button && id > 0 && id <= OmniLink.Controller.Buttons.Count)
|
|
ProcessButtonReceived(OmniLink.Controller.Buttons[id], topic, payload);
|
|
else if (type == CommandTypes.message && id > 0 && id <= OmniLink.Controller.Messages.Count)
|
|
ProcessMessageReceived(OmniLink.Controller.Messages[id], topic, payload);
|
|
}
|
|
|
|
private static readonly IDictionary<AreaCommands, enuUnitCommand> AreaMapping = new Dictionary<AreaCommands, enuUnitCommand>
|
|
{
|
|
{ AreaCommands.disarm, enuUnitCommand.SecurityOff },
|
|
{ AreaCommands.arm_home, enuUnitCommand.SecurityDay },
|
|
{ AreaCommands.arm_away, enuUnitCommand.SecurityAway },
|
|
{ AreaCommands.arm_night, enuUnitCommand.SecurityNight },
|
|
{ AreaCommands.arm_vacation, enuUnitCommand.SecurityVac },
|
|
// The below aren't supported by Home Assistant
|
|
{ AreaCommands.arm_home_instant, enuUnitCommand.SecurityDyi },
|
|
{ AreaCommands.arm_night_delay, enuUnitCommand.SecurityNtd }
|
|
};
|
|
|
|
private void ProcessAreaReceived(clsArea area, Topic command, string payload)
|
|
{
|
|
int code;
|
|
(payload, code) = payload.ToCommandCode();
|
|
|
|
if (command == Topic.command && Enum.TryParse(payload, true, out AreaCommands cmd))
|
|
{
|
|
if (area.Number == 0)
|
|
log.Debug("SetArea: 0 implies all areas will be changed");
|
|
|
|
log.Debug("SetArea: {id} to {value}", area.Number, cmd.ToString().Replace("arm_", "").Replace("_", " "));
|
|
OmniLink.SendCommand(AreaMapping[cmd], (byte)code, (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>
|
|
{
|
|
{ ZoneCommands.restore, enuUnitCommand.Restore },
|
|
{ ZoneCommands.bypass, enuUnitCommand.Bypass },
|
|
};
|
|
|
|
private void ProcessZoneReceived(clsZone zone, Topic command, string payload)
|
|
{
|
|
int code;
|
|
(payload, code) = payload.ToCommandCode();
|
|
|
|
if (command == Topic.command && Enum.TryParse(payload, true, out ZoneCommands cmd) &&
|
|
!(zone.Number == 0 && cmd == ZoneCommands.bypass))
|
|
{
|
|
if (zone.Number == 0)
|
|
log.Debug("SetZone: 0 implies all zones will be restored");
|
|
|
|
log.Debug("SetZone: {id} to {value}", zone.Number, payload);
|
|
OmniLink.SendCommand(ZoneMapping[cmd], (byte)code, (ushort)zone.Number);
|
|
}
|
|
}
|
|
|
|
private static readonly IDictionary<UnitCommands, enuUnitCommand> UnitMapping = new Dictionary<UnitCommands, enuUnitCommand>
|
|
{
|
|
{ UnitCommands.OFF, enuUnitCommand.Off },
|
|
{ UnitCommands.ON, enuUnitCommand.On }
|
|
};
|
|
|
|
private void ProcessUnitReceived(clsUnit unit, Topic command, string payload)
|
|
{
|
|
if (command == Topic.command && Enum.TryParse(payload, true, out UnitCommands cmd))
|
|
{
|
|
if (string.Compare(unit.ToState(), cmd.ToString()) != 0)
|
|
{
|
|
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: {id} to {value}%", unit.Number, payload);
|
|
|
|
OmniLink.SendCommand(enuUnitCommand.Level, BitConverter.GetBytes(unitValue)[0], (ushort)unit.Number);
|
|
|
|
// Force status change instead of waiting on controller to update
|
|
// Home Assistant sends brightness immediately followed by ON,
|
|
// which will cause light to go to 100% brightness
|
|
unit.Status = (byte)(100 + unitValue);
|
|
}
|
|
else if (command == Topic.scene_command && char.TryParse(payload, out char scene))
|
|
{
|
|
log.Debug("SetUnit: {id} to {value}", unit.Number, payload);
|
|
|
|
OmniLink.SendCommand(enuUnitCommand.Compose, (byte)(scene - 63), (ushort)unit.Number);
|
|
}
|
|
}
|
|
|
|
private void ProcessThermostatReceived(clsThermostat thermostat, Topic command, string payload)
|
|
{
|
|
if (command == Topic.temperature_heat_command && double.TryParse(payload, out double tempLow))
|
|
{
|
|
string tempUnit = "C";
|
|
if (OmniLink.Controller.TempFormat == enuTempFormat.Fahrenheit)
|
|
{
|
|
tempLow = tempLow.ToCelsius();
|
|
tempUnit = "F";
|
|
}
|
|
|
|
int temp = tempLow.ToOmniTemp();
|
|
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))
|
|
{
|
|
string tempUnit = "C";
|
|
if (OmniLink.Controller.TempFormat == enuTempFormat.Fahrenheit)
|
|
{
|
|
tempHigh = tempHigh.ToCelsius();
|
|
tempUnit = "F";
|
|
}
|
|
|
|
int temp = tempHigh.ToOmniTemp();
|
|
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: {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: {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))
|
|
{
|
|
if (thermostat.Type == enuThermostatType.AutoHeatCool ||
|
|
(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))
|
|
{
|
|
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: {id} to {value}", thermostat.Number, payload);
|
|
OmniLink.SendCommand(enuUnitCommand.Hold, BitConverter.GetBytes((int)holdMode)[0], (ushort)thermostat.Number);
|
|
}
|
|
}
|
|
|
|
private void ProcessButtonReceived(clsButton button, Topic command, string payload)
|
|
{
|
|
if (command == Topic.command && Enum.TryParse(payload, true, out UnitCommands cmd) && cmd == UnitCommands.ON)
|
|
{
|
|
log.Debug("PushButton: {id}", button.Number);
|
|
OmniLink.SendCommand(enuUnitCommand.Button, 0, (ushort)button.Number);
|
|
}
|
|
}
|
|
|
|
private static readonly IDictionary<MessageCommands, enuUnitCommand> MessageMapping = new Dictionary<MessageCommands, enuUnitCommand>
|
|
{
|
|
{ MessageCommands.show, enuUnitCommand.ShowMsgWBeep },
|
|
{ MessageCommands.show_no_beep, enuUnitCommand.ShowMsgNoBeep },
|
|
{ MessageCommands.show_no_beep_or_led, enuUnitCommand.ShowMsgNoBeep },
|
|
{ MessageCommands.clear, enuUnitCommand.ClearMsg },
|
|
};
|
|
|
|
private void ProcessMessageReceived(clsMessage message, Topic command, string payload)
|
|
{
|
|
if (command == Topic.command && Enum.TryParse(payload, true, out MessageCommands cmd))
|
|
{
|
|
log.Debug("SetMessage: {id} to {value}", message.Number, cmd.ToString().Replace("_", " "));
|
|
|
|
byte par = 0;
|
|
if (cmd == MessageCommands.show_no_beep)
|
|
par = 1;
|
|
else if (cmd == MessageCommands.show_no_beep_or_led)
|
|
par = 2;
|
|
|
|
OmniLink.SendCommand(MessageMapping[cmd], par, (ushort)message.Number);
|
|
}
|
|
}
|
|
}
|
|
}
|