mirror of
https://bitbucket.org/myhomie/mycorerepository.git
synced 2025-12-06 09:41:19 +00:00
Meross Service Mqtt publish message toggle works ! Toggle-X plugs + Read current consumption + get abilities..
This commit is contained in:
parent
b9599ec357
commit
0441baa6b2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
61
MyCore/Models/Meross/DevicesAbilities.cs
Normal file
61
MyCore/Models/Meross/DevicesAbilities.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MyCore.Models.Meross
|
||||||
|
{
|
||||||
|
public class DeviceAbilities
|
||||||
|
{
|
||||||
|
# region Common abilities
|
||||||
|
public class Common
|
||||||
|
{
|
||||||
|
public static string ALL = "Appliance.System.All";
|
||||||
|
public static string ABILITY = "Appliance.System.Ability";
|
||||||
|
public static string REPORT = "Appliance.System.Report";
|
||||||
|
public static string ONLINE = "Appliance.System.Online";
|
||||||
|
public static string WIFI_LIST = "Appliance.Config.WifiList";
|
||||||
|
public static string DEBUG = "Appliance.System.Debug";
|
||||||
|
public static string TRACE = "Appliance.Config.Trace";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
# region Power plug/bulbs abilities
|
||||||
|
public class PlugsAndBulbs
|
||||||
|
{
|
||||||
|
public static string TOGGLE = "Appliance.Control.Toggle";
|
||||||
|
public static string TOGGLEX = "Appliance.Control.ToggleX";
|
||||||
|
public static string TRIGGER = "Appliance.Control.Trigger";
|
||||||
|
public static string TRIGGERX = "Appliance.Control.TriggerX";
|
||||||
|
public static string ELECTRICITY = "Appliance.Control.Electricity";
|
||||||
|
public static string CONSUMPTIONX = "Appliance.Control.ConsumptionX";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
# region Hub
|
||||||
|
public class Hub
|
||||||
|
{
|
||||||
|
public static string HUB_TOGGLEX = "Appliance.Hub.ToggleX";
|
||||||
|
public static string HUB_ONLINE = "Appliance.Hub.Online";
|
||||||
|
public static string HUB_MTS100_TEMPERATURE = "Appliance.Hub.Mts100.Temperature";
|
||||||
|
public static string HUB_MTS100_MODE = "Appliance.Hub.Mts100.Mode";
|
||||||
|
public static string HUB_MTS100_ALL = "Appliance.Hub.Mts100.All";
|
||||||
|
public static string HUB_EXCEPTION = "Appliance.Hub.Exception";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
# region Garage opener abilities
|
||||||
|
public class Garage
|
||||||
|
{
|
||||||
|
public static string GARAGE_DOOR_STATE = "Appliance.GarageDoor.State";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Bulbs-only abilities
|
||||||
|
public class Bulb
|
||||||
|
{
|
||||||
|
public static string LIGHT = "Appliance.Control.Light";
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ using System.Net.Http.Headers;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using static MyCore.Models.Meross.DeviceAbilities;
|
||||||
|
|
||||||
namespace MyCore.Services
|
namespace MyCore.Services
|
||||||
{
|
{
|
||||||
@ -31,6 +32,8 @@ namespace MyCore.Services
|
|||||||
|
|
||||||
private static ResultToken resultToken;
|
private static ResultToken resultToken;
|
||||||
|
|
||||||
|
private List<MerossDevice> allDevices;
|
||||||
|
|
||||||
// API MQTT
|
// API MQTT
|
||||||
private const int port = 2001;
|
private const int port = 2001;
|
||||||
private const string domain = "iot.meross.com";
|
private const string domain = "iot.meross.com";
|
||||||
@ -80,7 +83,7 @@ namespace MyCore.Services
|
|||||||
if (deviceTask.Result != "")
|
if (deviceTask.Result != "")
|
||||||
{
|
{
|
||||||
data = ((JObject)JsonConvert.DeserializeObject(deviceTask.Result))["data"];
|
data = ((JObject)JsonConvert.DeserializeObject(deviceTask.Result))["data"];
|
||||||
var test = JsonConvert.DeserializeObject<List<MerossDevice>>(data.ToString());
|
allDevices = JsonConvert.DeserializeObject<List<MerossDevice>>(data.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
var logTask = Task.Run(() => PostURI(new Uri(_logUrl), RequestType.Log));
|
var logTask = Task.Run(() => PostURI(new Uri(_logUrl), RequestType.Log));
|
||||||
@ -189,6 +192,74 @@ namespace MyCore.Services
|
|||||||
return nonceString.ToString();
|
return nonceString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RequestMQTT
|
||||||
|
{
|
||||||
|
public HeaderMqtt header;
|
||||||
|
public Object payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HeaderMqtt
|
||||||
|
{
|
||||||
|
public string from;
|
||||||
|
public string messageId;
|
||||||
|
public string method;
|
||||||
|
public string Namespace;
|
||||||
|
public int payloadVersion;
|
||||||
|
public string sign;
|
||||||
|
public long timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PayLoadToggle
|
||||||
|
{
|
||||||
|
// as it's not a multi-socket
|
||||||
|
public int channel = 0;
|
||||||
|
public Toggle toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Toggle
|
||||||
|
{
|
||||||
|
// 1 = on or 0 = off
|
||||||
|
public int onoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PayLoadToggleX
|
||||||
|
{
|
||||||
|
public ToggleX togglex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ToggleX
|
||||||
|
{
|
||||||
|
// 1 = on or 0 = off
|
||||||
|
public int onoff;
|
||||||
|
// number of the socket of the multi-socket
|
||||||
|
public int channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Create ability class related to device
|
||||||
|
public enum CommandMqtt
|
||||||
|
{
|
||||||
|
ABILITY,
|
||||||
|
CONSUMPTIONX,
|
||||||
|
ELECTRICITY,
|
||||||
|
TOGGLE,
|
||||||
|
TOGGLEX
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Method
|
||||||
|
{
|
||||||
|
GET,
|
||||||
|
SET
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ToggleStatus
|
||||||
|
{
|
||||||
|
OFF,
|
||||||
|
ON
|
||||||
|
}
|
||||||
|
|
||||||
|
public string clientId = "";
|
||||||
|
public string appId = "";
|
||||||
|
|
||||||
private void ConnexionToMqtt()
|
private void ConnexionToMqtt()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -199,8 +270,13 @@ namespace MyCore.Services
|
|||||||
string stringForMD5 = resultToken.userid + resultToken.key;
|
string stringForMD5 = resultToken.userid + resultToken.key;
|
||||||
string hashed_password = CreateMD5(stringForMD5).ToLower();
|
string hashed_password = CreateMD5(stringForMD5).ToLower();
|
||||||
|
|
||||||
|
// Check if we can hardcode it -> seems ok 20/01/2020
|
||||||
|
var uuid = "01df8092-d790-4377-8481-5b73aef045c6";
|
||||||
|
|
||||||
|
GenerateClientAndAppId(uuid);
|
||||||
|
|
||||||
_options = new MqttClientOptionsBuilder()
|
_options = new MqttClientOptionsBuilder()
|
||||||
.WithClientId("app:ddddd9eb14407b666f559af5af9c1840")
|
.WithClientId(clientId)
|
||||||
.WithTcpServer(domain, port)
|
.WithTcpServer(domain, port)
|
||||||
.WithCredentials(resultToken.userid, hashed_password)
|
.WithCredentials(resultToken.userid, hashed_password)
|
||||||
.WithCleanSession()
|
.WithCleanSession()
|
||||||
@ -239,8 +315,37 @@ namespace MyCore.Services
|
|||||||
{
|
{
|
||||||
Console.WriteLine("### CONNECTED WITH SERVER ###");
|
Console.WriteLine("### CONNECTED WITH SERVER ###");
|
||||||
|
|
||||||
// Subscribe to a topic
|
var topic = $"/app/{resultToken.userid}-{appId.ToLower()}/subscribe";
|
||||||
await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic("#").Build());
|
await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||||
|
|
||||||
|
/*foreach (var device in allDevices)
|
||||||
|
{
|
||||||
|
topic = $"/appliance/{device.uuid}/subscribe";
|
||||||
|
// Not really neccessary as the GETACK exists
|
||||||
|
//await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
MerossDevice multisocket = allDevices.Where(d => d.onlineStatus == 1 && d.deviceType == "mss425f").FirstOrDefault();
|
||||||
|
|
||||||
|
/*if (multisocket != null && multisocket.onlineStatus == 1)
|
||||||
|
{
|
||||||
|
//As multisocket channel 0 is all the sockets, skip 0
|
||||||
|
int i = 0;
|
||||||
|
foreach (var channel in multisocket.channels)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
ExecuteCommand(multisocket.uuid, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
ExecuteCommand(multisocket.uuid, Method.GET, CommandMqtt.ABILITY);
|
||||||
|
|
||||||
|
foreach (var device in allDevices.Where(d => d.deviceType == "mss310" && d.onlineStatus == 1).ToList())
|
||||||
|
{
|
||||||
|
ExecuteCommand(device.uuid, Method.GET, CommandMqtt.ABILITY);
|
||||||
|
ExecuteCommand(device.uuid, Method.GET, CommandMqtt.ELECTRICITY);
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine("### SUBSCRIBED ###");
|
Console.WriteLine("### SUBSCRIBED ###");
|
||||||
});
|
});
|
||||||
@ -254,7 +359,30 @@ namespace MyCore.Services
|
|||||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
|
// TODO GETACK and writing db + get current consumption + reliability of changing status of a plug + get current temp in bathroom + etc
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* Example :
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Toggle channel 0 multiprise
|
||||||
|
* topic = /appliance/1909206725106690802148e1e95243fd/subscribe
|
||||||
|
*
|
||||||
|
* payload =
|
||||||
|
* {"header": {"from": "/app/174744-2c2c67eab37f04625d60dc678e5a2349/subscribe",
|
||||||
|
* "messageId": "5e2ac8700552ef089da05f8543993319",
|
||||||
|
* "method": "SET",
|
||||||
|
* "namespace": "Appliance.Control.ToggleX",
|
||||||
|
* "payloadVersion": 1,
|
||||||
|
* "sign": "18469fb032695f3144f060e65f3fad26",
|
||||||
|
* "timestamp": 1579540738},
|
||||||
|
* "payload":
|
||||||
|
* {"togglex":
|
||||||
|
* {"onoff": 1, "channel": 0}
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -262,53 +390,110 @@ namespace MyCore.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CertificateValidationCallBack(
|
private void GenerateClientAndAppId(string uuid)
|
||||||
object sender,
|
{
|
||||||
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
|
var md5Result = CreateMD5($"API{uuid}");
|
||||||
System.Security.Cryptography.X509Certificates.X509Chain chain,
|
appId = md5Result;
|
||||||
System.Net.Security.SslPolicyErrors sslPolicyErrors)
|
clientId = $"app:{md5Result}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteCommand(string uuid, Method method, CommandMqtt command, string payload = null, string callback = null, ToggleStatus toggleStatus = 0,int channelChosen = 0)
|
||||||
|
{
|
||||||
|
var topic = $"/appliance/{uuid}/subscribe";
|
||||||
|
var namespaceVar = "";
|
||||||
|
var payloadVar = payload;
|
||||||
|
bool multisocket = false;
|
||||||
|
|
||||||
|
// TODO Check device type.. and add switch case
|
||||||
|
switch (command)
|
||||||
{
|
{
|
||||||
// If the certificate is a valid, signed certificate, return true.
|
case CommandMqtt.ABILITY:
|
||||||
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
namespaceVar = Common.ABILITY;
|
||||||
{
|
break;
|
||||||
return true;
|
case CommandMqtt.CONSUMPTIONX:
|
||||||
}
|
namespaceVar = PlugsAndBulbs.TOGGLEX;
|
||||||
|
break;
|
||||||
// If there are errors in the certificate chain, look at each error to determine the cause.
|
case CommandMqtt.TOGGLE:
|
||||||
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
|
namespaceVar = PlugsAndBulbs.TOGGLE;
|
||||||
{
|
break;
|
||||||
if (chain != null && chain.ChainStatus != null)
|
case CommandMqtt.TOGGLEX:
|
||||||
{
|
multisocket = true;
|
||||||
foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
|
namespaceVar = PlugsAndBulbs.TOGGLEX;
|
||||||
{
|
break;
|
||||||
if ((certificate.Subject == certificate.Issuer) &&
|
case CommandMqtt.ELECTRICITY:
|
||||||
(status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
|
namespaceVar = PlugsAndBulbs.ELECTRICITY;
|
||||||
{
|
break;
|
||||||
// Self-signed certificates with an untrusted root are valid.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
|
|
||||||
{
|
|
||||||
// If there are any other errors in the certificate chain, the certificate is invalid,
|
|
||||||
// so the method returns false.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When processing reaches this line, the only errors in the certificate chain are
|
|
||||||
// untrusted root errors for self-signed certificates. These certificates are valid
|
|
||||||
// for default Exchange server installations, so return true.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// In all other cases, return false.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if a plug
|
||||||
|
// and if a toggleX ability
|
||||||
|
if (method == Method.SET && multisocket)
|
||||||
|
{
|
||||||
|
PayLoadToggleX payLoadToggleX = new PayLoadToggleX();
|
||||||
|
ToggleX toggleX = new ToggleX();
|
||||||
|
toggleX.onoff = toggleStatus.GetHashCode();
|
||||||
|
toggleX.channel = channelChosen;
|
||||||
|
payLoadToggleX.togglex = toggleX;
|
||||||
|
|
||||||
|
payloadVar = JsonConvert.SerializeObject(payLoadToggleX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a plug
|
||||||
|
// and if hasn't toggleX ability
|
||||||
|
if (method == Method.SET && !multisocket)
|
||||||
|
{
|
||||||
|
PayLoadToggle payLoadToggle= new PayLoadToggle();
|
||||||
|
Toggle toggle = new Toggle();
|
||||||
|
toggle.onoff = toggleStatus.GetHashCode();
|
||||||
|
payLoadToggle.toggle = toggle;
|
||||||
|
|
||||||
|
payloadVar = JsonConvert.SerializeObject(payLoadToggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = BuildMQTTMessage(method, namespaceVar, payloadVar);
|
||||||
|
|
||||||
|
PublishMessage(topic, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildMQTTMessage(Method method, string namespaceVar, string payloadVar)
|
||||||
|
{
|
||||||
|
HeaderMqtt headerMqtt = new HeaderMqtt();
|
||||||
|
|
||||||
|
headerMqtt.from = $"/app/{resultToken.userid}-{appId.ToLower()}/subscribe";
|
||||||
|
headerMqtt.messageId = CreateMD5(GenerateNonce(16).ToUpper()).ToLower();
|
||||||
|
headerMqtt.method = method.ToString();
|
||||||
|
headerMqtt.Namespace = namespaceVar;
|
||||||
|
headerMqtt.payloadVersion = 1;
|
||||||
|
headerMqtt.timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
|
||||||
|
headerMqtt.sign = CreateMD5($"{headerMqtt.messageId}{resultToken.key}{(headerMqtt.timestamp).ToString()}").ToLower();
|
||||||
|
|
||||||
|
|
||||||
|
RequestMQTT requestMQTT = new RequestMQTT();
|
||||||
|
requestMQTT.header = headerMqtt;
|
||||||
|
if (payloadVar != null)
|
||||||
|
requestMQTT.payload = JsonConvert.DeserializeObject(payloadVar);
|
||||||
|
else
|
||||||
|
requestMQTT.payload = new Object();
|
||||||
|
|
||||||
|
var data = JsonConvert.SerializeObject(requestMQTT);
|
||||||
|
|
||||||
|
// Change Namespace to namespace
|
||||||
|
data = data.Replace("Namespace", "namespace");
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PublishMessage(string topic, string message)
|
||||||
|
{
|
||||||
|
var mqttMessage = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic(topic)
|
||||||
|
.WithPayload(message)
|
||||||
|
.WithExactlyOnceQoS()
|
||||||
|
.WithRetainFlag()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
if (_client.IsConnected)
|
||||||
|
_client.PublishAsync(mqttMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user