mirror of
https://github.com/excaliburpartners/OmniLinkBridge
synced 2024-12-22 10:42:24 +00:00
Implement MQTT thermostat offline status
This commit is contained in:
parent
1ce5e3dab9
commit
a016e1cd64
7
OmniLinkBridge/MQTT/Availability.cs
Normal file
7
OmniLinkBridge/MQTT/Availability.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace OmniLinkBridge.MQTT
|
||||
{
|
||||
public class Availability
|
||||
{
|
||||
public string topic { get; set; } = $"{Global.mqtt_prefix}/status";
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ namespace OmniLinkBridge.MQTT
|
|||
{
|
||||
public class Climate : Device
|
||||
{
|
||||
public string status { get; set; }
|
||||
|
||||
public string action_topic { get; set; }
|
||||
public string current_temperature_topic { get; set; }
|
||||
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using OmniLinkBridge.Modules;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OmniLinkBridge.MQTT
|
||||
{
|
||||
public class Device
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum AvailabilityMode
|
||||
{
|
||||
all,
|
||||
any,
|
||||
latest
|
||||
}
|
||||
|
||||
public string unique_id { get; set; }
|
||||
|
||||
public string name { get; set; }
|
||||
|
@ -12,8 +22,15 @@ namespace OmniLinkBridge.MQTT
|
|||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string state_topic { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string availability_topic { get; set; } = $"{Global.mqtt_prefix}/status";
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<Availability> availability { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public AvailabilityMode? availability_mode { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public DeviceRegistry device { get; set; } = MQTTModule.MqttDeviceRegistry;
|
||||
}
|
||||
|
|
|
@ -484,6 +484,17 @@ namespace OmniLinkBridge.MQTT
|
|||
{
|
||||
Climate ret = new Climate
|
||||
{
|
||||
unique_id = $"{Global.mqtt_prefix}thermostat{thermostat.Number}",
|
||||
name = Global.mqtt_discovery_name_prefix + thermostat.Name,
|
||||
|
||||
availability_topic = null,
|
||||
availability_mode = Device.AvailabilityMode.all,
|
||||
availability = new List<Availability>()
|
||||
{
|
||||
new Availability(),
|
||||
new Availability() { topic = thermostat.ToTopic(Topic.status) }
|
||||
},
|
||||
|
||||
modes = thermostat.Type switch
|
||||
{
|
||||
enuThermostatType.AutoHeatCool => new List<string>(new string[] { "auto", "off", "cool", "heat" }),
|
||||
|
@ -491,7 +502,25 @@ namespace OmniLinkBridge.MQTT
|
|||
enuThermostatType.HeatOnly => new List<string>(new string[] { "off", "heat" }),
|
||||
enuThermostatType.CoolOnly => new List<string>(new string[] { "off", "cool" }),
|
||||
_ => new List<string>(new string[] { "off" }),
|
||||
}
|
||||
},
|
||||
|
||||
action_topic = thermostat.ToTopic(Topic.current_operation),
|
||||
current_temperature_topic = thermostat.ToTopic(Topic.current_temperature),
|
||||
|
||||
temperature_low_state_topic = thermostat.ToTopic(Topic.temperature_heat_state),
|
||||
temperature_low_command_topic = thermostat.ToTopic(Topic.temperature_heat_command),
|
||||
|
||||
temperature_high_state_topic = thermostat.ToTopic(Topic.temperature_cool_state),
|
||||
temperature_high_command_topic = thermostat.ToTopic(Topic.temperature_cool_command),
|
||||
|
||||
mode_state_topic = thermostat.ToTopic(Topic.mode_basic_state),
|
||||
mode_command_topic = thermostat.ToTopic(Topic.mode_command),
|
||||
|
||||
fan_mode_state_topic = thermostat.ToTopic(Topic.fan_mode_state),
|
||||
fan_mode_command_topic = thermostat.ToTopic(Topic.fan_mode_command),
|
||||
|
||||
preset_mode_state_topic = thermostat.ToTopic(Topic.hold_state),
|
||||
preset_mode_command_topic = thermostat.ToTopic(Topic.hold_command)
|
||||
};
|
||||
|
||||
if (format == enuTempFormat.Celsius)
|
||||
|
@ -500,26 +529,6 @@ namespace OmniLinkBridge.MQTT
|
|||
ret.max_temp = "35";
|
||||
}
|
||||
|
||||
ret.unique_id = $"{Global.mqtt_prefix}thermostat{thermostat.Number}";
|
||||
ret.name = Global.mqtt_discovery_name_prefix + thermostat.Name;
|
||||
|
||||
ret.action_topic = thermostat.ToTopic(Topic.current_operation);
|
||||
ret.current_temperature_topic = thermostat.ToTopic(Topic.current_temperature);
|
||||
|
||||
ret.temperature_low_state_topic = thermostat.ToTopic(Topic.temperature_heat_state);
|
||||
ret.temperature_low_command_topic = thermostat.ToTopic(Topic.temperature_heat_command);
|
||||
|
||||
ret.temperature_high_state_topic = thermostat.ToTopic(Topic.temperature_cool_state);
|
||||
ret.temperature_high_command_topic = thermostat.ToTopic(Topic.temperature_cool_command);
|
||||
|
||||
ret.mode_state_topic = thermostat.ToTopic(Topic.mode_basic_state);
|
||||
ret.mode_command_topic = thermostat.ToTopic(Topic.mode_command);
|
||||
|
||||
ret.fan_mode_state_topic = thermostat.ToTopic(Topic.fan_mode_state);
|
||||
ret.fan_mode_command_topic = thermostat.ToTopic(Topic.fan_mode_command);
|
||||
|
||||
ret.preset_mode_state_topic = thermostat.ToTopic(Topic.hold_state);
|
||||
ret.preset_mode_command_topic = thermostat.ToTopic(Topic.hold_command);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
public enum Topic
|
||||
{
|
||||
name,
|
||||
status,
|
||||
state,
|
||||
command,
|
||||
alarm_command,
|
||||
|
|
|
@ -246,6 +246,10 @@ namespace OmniLinkBridge.Modules
|
|||
humidity + "','" + humidify + "','" + dehumidify + "','" +
|
||||
e.Thermostat.ModeText() + "','" + e.Thermostat.FanModeText() + "','" + e.Thermostat.HoldStatusText() + "')");
|
||||
|
||||
if (e.Offline)
|
||||
log.Warning("Unknown temp for Thermostat {thermostatName}, verify thermostat is online",
|
||||
e.Thermostat.Name);
|
||||
|
||||
// Ignore events fired by thermostat polling
|
||||
if (!e.EventTimer && Global.verbose_thermostat)
|
||||
log.Verbose("ThermostatStatus {id} {name}, Status: {temp} {status}, " +
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace OmniLinkBridge.Modules
|
|||
private void PublishControllerStatus(string status)
|
||||
{
|
||||
log.Information("Publishing controller {status}", status);
|
||||
PublishAsync($"{Global.mqtt_prefix}/status", status);
|
||||
PublishAsync($"{Global.mqtt_prefix}/{Topic.status}", status);
|
||||
}
|
||||
|
||||
private void PublishConfig()
|
||||
|
@ -357,6 +357,7 @@ namespace OmniLinkBridge.Modules
|
|||
PublishThermostatState(thermostat);
|
||||
|
||||
PublishAsync(thermostat.ToTopic(Topic.name), thermostat.Name);
|
||||
PublishAsync(thermostat.ToTopic(Topic.status), ONLINE);
|
||||
PublishAsync($"{Global.mqtt_discovery_prefix}/climate/{Global.mqtt_prefix}/thermostat{i}/config",
|
||||
JsonConvert.SerializeObject(thermostat.ToConfig(OmniLink.Controller.TempFormat)));
|
||||
PublishAsync($"{Global.mqtt_discovery_prefix}/number/{Global.mqtt_prefix}/thermostat{i}humidify/config",
|
||||
|
@ -470,8 +471,17 @@ namespace OmniLinkBridge.Modules
|
|||
return;
|
||||
|
||||
// Ignore events fired by thermostat polling
|
||||
if (!e.EventTimer)
|
||||
PublishThermostatState(e.Thermostat);
|
||||
if (e.EventTimer)
|
||||
return;
|
||||
|
||||
if (e.Offline)
|
||||
{
|
||||
PublishAsync(e.Thermostat.ToTopic(Topic.status), OFFLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
PublishAsync(e.Thermostat.ToTopic(Topic.status), ONLINE);
|
||||
PublishThermostatState(e.Thermostat);
|
||||
}
|
||||
|
||||
private async void OmniLink_OnButtonStatus(object sender, ButtonStatusEventArgs e)
|
||||
|
|
|
@ -636,19 +636,13 @@ namespace OmniLinkBridge.Modules
|
|||
{
|
||||
Controller.Thermostats[MSG.ObjectNumber(i)].CopyExtendedStatus(MSG, i);
|
||||
|
||||
// Don't fire event when invalid temperature of 0 is sometimes received
|
||||
if (Controller.Thermostats[MSG.ObjectNumber(i)].Temp > 0)
|
||||
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
|
||||
{
|
||||
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
|
||||
{
|
||||
ID = MSG.ObjectNumber(i),
|
||||
Thermostat = Controller.Thermostats[MSG.ObjectNumber(i)],
|
||||
EventTimer = false
|
||||
});
|
||||
}
|
||||
else if (Global.verbose_thermostat_timer)
|
||||
log.Debug("Ignoring unsolicited unknown temp for Thermostat {thermostatName}",
|
||||
Controller.Thermostats[MSG.ObjectNumber(i)].Name);
|
||||
ID = MSG.ObjectNumber(i),
|
||||
Thermostat = Controller.Thermostats[MSG.ObjectNumber(i)],
|
||||
Offline = Controller.Thermostats[MSG.ObjectNumber(i)].Temp == 0,
|
||||
EventTimer = false
|
||||
});
|
||||
|
||||
if (!tstats.ContainsKey(MSG.ObjectNumber(i)))
|
||||
tstats.Add(MSG.ObjectNumber(i), DateTime.Now);
|
||||
|
@ -732,19 +726,13 @@ namespace OmniLinkBridge.Modules
|
|||
(Controller.Connection.ConnectionState == enuOmniLinkConnectionState.Online ||
|
||||
Controller.Connection.ConnectionState == enuOmniLinkConnectionState.OnlineSecure))
|
||||
{
|
||||
// Don't fire event when invalid temperature of 0 is sometimes received
|
||||
if (Controller.Thermostats[tstat.Key].Temp > 0)
|
||||
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
|
||||
{
|
||||
OnThermostatStatus?.Invoke(this, new ThermostatStatusEventArgs()
|
||||
{
|
||||
ID = tstat.Key,
|
||||
Thermostat = Controller.Thermostats[tstat.Key],
|
||||
EventTimer = true
|
||||
});
|
||||
}
|
||||
else if (Global.verbose_thermostat_timer)
|
||||
log.Warning("Ignoring unknown temp for Thermostat {thermostatName}",
|
||||
Controller.Thermostats[tstat.Key].Name);
|
||||
ID = tstat.Key,
|
||||
Thermostat = Controller.Thermostats[tstat.Key],
|
||||
Offline = Controller.Thermostats[tstat.Key].Temp == 0,
|
||||
EventTimer = true
|
||||
});
|
||||
}
|
||||
else if (Global.verbose_thermostat_timer)
|
||||
log.Warning("Not logging out of date status for Thermostat {thermostatName}",
|
||||
|
|
|
@ -8,6 +8,11 @@ namespace OmniLinkBridge.OmniLink
|
|||
public ushort ID { get; set; }
|
||||
public clsThermostat Thermostat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true when thermostat is offline, indicated by a temperature of 0
|
||||
/// </summary>
|
||||
public bool Offline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true when fired by thermostat polling
|
||||
/// </summary>
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
<Compile Include="MQTT\AreaCommandCode.cs" />
|
||||
<Compile Include="MQTT\AreaCommands.cs" />
|
||||
<Compile Include="MQTT\AreaState.cs" />
|
||||
<Compile Include="MQTT\Availability.cs" />
|
||||
<Compile Include="MQTT\BinarySensor.cs" />
|
||||
<Compile Include="MQTT\CommandTypes.cs" />
|
||||
<Compile Include="MQTT\Device.cs" />
|
||||
|
|
16
README.md
16
README.md
|
@ -15,7 +15,7 @@ OmniLink Bridge is divided into the following modules and configurable settings.
|
|||
- Maintains connection to the OmniLink controller
|
||||
- Thermostats
|
||||
- If no status update has been received after 4 minutes a request is issued
|
||||
- A status update containing a temperature of 0 is ignored
|
||||
- A status update containing a temperature of 0 marks the thermostat offline
|
||||
- This can occur when a ZigBee thermostat has lost communication
|
||||
- Time Sync: time_
|
||||
- Controller time is checked and compared to the local computer time disregarding time zones
|
||||
|
@ -146,6 +146,17 @@ systemctl start omnilinkbridge.service
|
|||
```
|
||||
|
||||
## MQTT
|
||||
```
|
||||
SUB omnilink/status
|
||||
string online, offline
|
||||
|
||||
SUB omnilink/model
|
||||
string Controller model
|
||||
|
||||
SUB omnilink/version
|
||||
string Controller version
|
||||
```
|
||||
|
||||
### System
|
||||
```
|
||||
SUB omnilink/system/phone/state
|
||||
|
@ -231,6 +242,9 @@ string A-L
|
|||
SUB omnilink/thermostatX/name
|
||||
string Thermostat name
|
||||
|
||||
SUB omnilink/thermostatX/status
|
||||
string online, offline
|
||||
|
||||
SUB omnilink/thermostatX/current_operation
|
||||
string idle, cooling, heating
|
||||
|
||||
|
|
Loading…
Reference in a new issue