-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from sergey-brutsky/xiaomi-gateway-3-support
Xiaomi gateway 3 support
- Loading branch information
Showing
104 changed files
with
5,758 additions
and
1,876 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,21 @@ | ||
using System; | ||
using MiHomeLib; | ||
|
||
namespace MiHomeConsole | ||
namespace MiHomeConsole; | ||
public class Program | ||
{ | ||
public class Program | ||
public static void Main() | ||
{ | ||
public static void Main(string[] args) | ||
using var gw3 = new XiaomiGateway3("<gateway ip>", "<gateway token>"); | ||
{ | ||
//Action<ILoggingBuilder> loggingBuilder = | ||
// builder => builder.AddConsole(x => | ||
// { | ||
// x.DisableColors = true; | ||
// x.Format = ConsoleLoggerFormat.Systemd; | ||
// x.TimestampFormat = " yyyy-MM-d [HH:mm:ss] - "; | ||
// }); | ||
|
||
//MiHome.LoggerFactory = LoggerFactory.Create(loggingBuilder); | ||
//MiHome.LogRawCommands = true; | ||
|
||
// pwd of your gateway (optional, needed only to send commands to your devices) | ||
// and sid of your gateway (optional, use only when you have 2 gateways in your LAN) | ||
//using var miHome = new MiHome("pwd", "sid") | ||
using var miHome = new MiHome(); | ||
|
||
miHome.OnAnyDevice += (_, device) => | ||
gw3.OnDeviceDiscovered += gw3SubDevice => | ||
{ | ||
Console.WriteLine($"{device.Sid}, {device.GetType()}, {device}"); // all discovered devices | ||
Console.WriteLine(gw3SubDevice.ToString()); | ||
}; | ||
|
||
Console.ReadLine(); | ||
gw3.DiscoverDevices(); | ||
} | ||
|
||
Console.ReadLine(); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
MiHomeLib/ActionProcessors/AsyncBleEventMethodProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Nodes; | ||
using Microsoft.Extensions.Logging; | ||
using MiHomeLib.DevicesV3; | ||
|
||
namespace MiHomeLib.ActionProcessors; | ||
|
||
public class AsyncBleEventMethodProcessor(Dictionary<string, XiaomiGateway3SubDevice> devices, ILoggerFactory loggerFactory) : IActionProcessor | ||
{ | ||
public const string ACTION = "_async.ble_event"; | ||
private readonly Dictionary<string, XiaomiGateway3SubDevice> _devices = devices; | ||
private readonly ILogger _logger = loggerFactory.CreateLogger<AsyncBleEventMethodProcessor>(); | ||
|
||
public void ProcessMessage(JsonNode json) | ||
{ | ||
if (!json.AsObject().ContainsKey("params") || !json["params"].AsObject().ContainsKey("dev")) | ||
{ | ||
_logger.LogWarning($"Json string --> '{json}' is not valid for ble parsing"); | ||
return; | ||
} | ||
|
||
var parms = json["params"].AsObject(); | ||
var dev = parms["dev"].AsObject(); | ||
|
||
if (!dev.ContainsKey("did")) | ||
{ | ||
_logger.LogWarning($"json --> {json} has no 'did' property. Futher processing is impossible"); | ||
return; | ||
} | ||
|
||
var did = dev["did"].ToString(); | ||
|
||
if (!_devices.ContainsKey(did)) | ||
{ | ||
_logger.LogWarning($"Device with did '{did}' is unknown. Processing is skipped"); | ||
return; | ||
} | ||
|
||
_devices[did].LastTimeMessageReceived = parms["gwts"].GetValue<double>().UnixSecondsToDateTime(); | ||
_devices[did].ParseData(parms.ToString()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using System.Text.Json.Nodes; | ||
|
||
namespace MiHomeLib.ActionProcessors; | ||
|
||
public interface IActionProcessor | ||
{ | ||
void ProcessMessage(JsonNode json); | ||
} |
45 changes: 45 additions & 0 deletions
45
MiHomeLib/ActionProcessors/ZigbeeHeartBeatCommandProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json; | ||
using System.Text.Json.Nodes; | ||
using Microsoft.Extensions.Logging; | ||
using MiHomeLib.DevicesV3; | ||
|
||
namespace MiHomeLib.ActionProcessors; | ||
|
||
public class ZigbeeHeartBeatCommandProcessor(Dictionary<string, XiaomiGateway3SubDevice> devices, ILoggerFactory loggerFactory) : IActionProcessor | ||
{ | ||
public const string ACTION = "heartbeat"; | ||
private readonly Dictionary<string, XiaomiGateway3SubDevice> _devices = devices; | ||
private readonly ILogger _logger = loggerFactory.CreateLogger<ZigbeeHeartBeatCommandProcessor>(); | ||
|
||
public void ProcessMessage(JsonNode json) | ||
{ | ||
var data = json["params"].Deserialize<List<Dictionary<string, JsonElement>>>(); | ||
|
||
if (data.Count != 1) | ||
{ | ||
_logger.LogWarning($"Wrong structure of heartbeat message --> '{data}'." + | ||
"Processing of such structure is not supported"); | ||
return; | ||
} | ||
|
||
if (!data[0].ContainsKey("did")) | ||
{ | ||
_logger.LogWarning("Heartbeat message doesn't contain 'did'." + | ||
"Processing of such structure is not supported"); | ||
return; | ||
} | ||
|
||
var did = data[0]["did"].GetString(); | ||
|
||
if (_devices.ContainsKey(did)) | ||
{ | ||
_devices[did].LastTimeMessageReceived = data[0]["time"].GetDouble().UnixMilliSecondsToDateTime(); | ||
(_devices[did] as ZigBeeDevice).ParseData(data[0]["res_list"].ToString()); | ||
} | ||
else | ||
{ | ||
_logger.LogWarning($"Did '{did}' is unknown. Cannot process '{ACTION}' command for this device."); | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
MiHomeLib/ActionProcessors/ZigbeeReportCommandProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Nodes; | ||
using Microsoft.Extensions.Logging; | ||
using MiHomeLib.DevicesV3; | ||
|
||
namespace MiHomeLib.ActionProcessors; | ||
|
||
public class ZigbeeReportCommandProcessor: IActionProcessor | ||
{ | ||
public const string ACTION = "report"; | ||
private readonly Dictionary<string, XiaomiGateway3SubDevice> _devices; | ||
private readonly ILogger _logger; | ||
|
||
public ZigbeeReportCommandProcessor(Dictionary<string, XiaomiGateway3SubDevice> devices, ILoggerFactory loggerFactory) | ||
{ | ||
_devices = devices; | ||
_logger = loggerFactory.CreateLogger(GetType()); | ||
} | ||
|
||
public void ProcessMessage(JsonNode json) | ||
{ | ||
var did = json["did"].ToString(); | ||
|
||
if (_devices.ContainsKey(did)) | ||
{ | ||
_devices[did].LastTimeMessageReceived = json["time"].GetValue<double>().UnixMilliSecondsToDateTime(); | ||
(_devices[did] as ZigBeeDevice).ParseData((json["mi_spec"] is not null ? json["mi_spec"] : json["params"]).ToString()); | ||
} | ||
else | ||
{ | ||
_logger.LogWarning($"Did '{did}' is unknown. Cannot process '{ACTION}' command for this device."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace MiHomeLib.DevicesV3; | ||
|
||
// This sensor works exactly as XiaomiDoorWindowSensor, no need to repeat all stuff here | ||
public class AqaraDoorWindowSensor(string did, ILoggerFactory loggerFactory) | ||
: XiaomiDoorWindowSensor(did, loggerFactory) | ||
{ | ||
public new const string MARKET_MODEL = "MCCGQ11LM"; | ||
public new const string MODEL = "lumi.sensor_magnet.aq2"; | ||
public override string ToString() => GetBaseInfo(MARKET_MODEL, MODEL) + $"Contact: {Contact}, " + GetBaseToString(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text.Json; | ||
using System.Text.Json.Nodes; | ||
using Microsoft.Extensions.Logging; | ||
using MiHomeLib.Transport; | ||
|
||
namespace MiHomeLib.DevicesV3; | ||
|
||
public class AqaraOneChannelRelayEu : ZigBeeManageableDevice | ||
{ | ||
public enum RelayState | ||
{ | ||
Unknown = -1, | ||
On = 1, | ||
Off = 0, | ||
} | ||
public enum PowerMemoryState | ||
{ | ||
PowerOff = 0, | ||
Previous = 1, | ||
} | ||
public enum PowerMode | ||
{ | ||
Momentary = 1, | ||
Toggle = 2, | ||
} | ||
public const string MARKET_MODEL = "SSM-U01"; | ||
public const string MODEL = "lumi.switch.n0agl1"; | ||
private (int siid, int piid) STATE_RES = (2, 1); | ||
private (int siid, int piid) LOAD_POWER_RES = (3, 2); | ||
private (int siid, int piid) POWER_CONSUMPTION_RES = (3, 1); | ||
private (int siid, int piid) POWER_MEMORY_RES = (5, 1); | ||
private (int siid, int piid) POWER_MODE_RES = (7, 2); | ||
private (int siid, int piid) POWER_OVERLOAD_RES = (5, 6); | ||
private readonly Dictionary<(int siid, int piid), Action<JsonNode>> _actions; | ||
public AqaraOneChannelRelayEu(string did, IMqttTransport mqttTransport, ILoggerFactory loggerFactory) : base(did, mqttTransport, loggerFactory) | ||
{ | ||
_actions = new() | ||
{ | ||
{LOAD_POWER_RES, x => // load power changed | ||
{ | ||
var oldValue = LoadPower; | ||
LoadPower = x.GetValue<float>(); | ||
OnLoadPowerChange?.Invoke(oldValue); | ||
} | ||
}, | ||
{STATE_RES, x => // channel state changed | ||
{ | ||
var state = x.GetValue<bool>() ? 1 : 0; | ||
|
||
if(state == (int)State) return; // No need to emit event when state is already actual | ||
|
||
State = state == 1 ? RelayState.On : RelayState.Off; | ||
OnStateChange?.Invoke(); | ||
} | ||
}, | ||
{POWER_CONSUMPTION_RES, x => // electricity consumption changed | ||
{ | ||
var oldValue = PowerConsumption; | ||
PowerConsumption = x.GetValue<float>(); | ||
OnPowerConsumptionChange?.Invoke(oldValue); | ||
} | ||
}, | ||
}; | ||
} | ||
public float LoadPower { get; internal set; } | ||
public float PowerConsumption { get; internal set; } | ||
public RelayState State { get; internal set; } = RelayState.Unknown; | ||
public event Action OnStateChange; | ||
/// <summary> | ||
/// Old value passed as an argument in W | ||
/// </summary> | ||
public event Action<float> OnLoadPowerChange; | ||
/// <summary> | ||
/// Old value passed as an argument in kWh | ||
/// </summary> | ||
public event Action<float> OnPowerConsumptionChange; | ||
protected internal override void ParseData(string data) | ||
{ | ||
var listProps = JsonSerializer.Deserialize<List<JsonNode>>(data); | ||
|
||
foreach (var prop in listProps) | ||
{ | ||
var key = (prop["siid"].GetValue<int>(), prop["piid"].GetValue<int>()); | ||
|
||
if(_actions.ContainsKey(key)) | ||
_actions[key](prop["value"]); | ||
} | ||
} | ||
public void PowerOn() => SendWriteCommand(STATE_RES, 1); | ||
public void PowerOff() => SendWriteCommand(STATE_RES, 0); | ||
public void ToggleState() | ||
{ | ||
switch (State) | ||
{ | ||
case RelayState.Off: | ||
SendWriteCommand(STATE_RES, 1); | ||
break; | ||
case RelayState.On: | ||
SendWriteCommand(STATE_RES, 0); | ||
break; | ||
}; | ||
} | ||
public void SetPowerMemoryState(PowerMemoryState state) => SendWriteCommand(POWER_MEMORY_RES, (int)state); | ||
public void SetPowerMode(PowerMode mode) => SendWriteCommand(POWER_MODE_RES, (int)mode); | ||
/// <summary> | ||
/// Warning ! Be very careful with this function. | ||
/// If you set low threshold and it's reached, device will fall into "protection" mode and stop working | ||
/// To reset this mode you need an external intervention (press button on the device or on external switch) | ||
/// </summary> | ||
public void SetPowerOverloadThreshold(int threshold) | ||
{ | ||
if(threshold < 0 || threshold > 2200) | ||
throw new ArgumentOutOfRangeException(nameof(threshold), threshold, $"Power overload threshold should be within range 1-2200 watt"); | ||
|
||
SendWriteCommand(POWER_OVERLOAD_RES, threshold); | ||
} | ||
public override string ToString() | ||
{ | ||
return GetBaseInfo(MARKET_MODEL, MODEL) + | ||
$"Load Power: {LoadPower}W, " + | ||
$"Channel State: {State}"; | ||
} | ||
} |
Oops, something went wrong.