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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static MyCore.Models.Meross.DeviceAbilities;
|
||||
|
||||
namespace MyCore.Services
|
||||
{
|
||||
@ -31,6 +32,8 @@ namespace MyCore.Services
|
||||
|
||||
private static ResultToken resultToken;
|
||||
|
||||
private List<MerossDevice> allDevices;
|
||||
|
||||
// API MQTT
|
||||
private const int port = 2001;
|
||||
private const string domain = "iot.meross.com";
|
||||
@ -80,7 +83,7 @@ namespace MyCore.Services
|
||||
if (deviceTask.Result != "")
|
||||
{
|
||||
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));
|
||||
@ -189,6 +192,74 @@ namespace MyCore.Services
|
||||
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()
|
||||
{
|
||||
try
|
||||
@ -199,8 +270,13 @@ namespace MyCore.Services
|
||||
string stringForMD5 = resultToken.userid + resultToken.key;
|
||||
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()
|
||||
.WithClientId("app:ddddd9eb14407b666f559af5af9c1840")
|
||||
.WithClientId(clientId)
|
||||
.WithTcpServer(domain, port)
|
||||
.WithCredentials(resultToken.userid, hashed_password)
|
||||
.WithCleanSession()
|
||||
@ -239,8 +315,37 @@ namespace MyCore.Services
|
||||
{
|
||||
Console.WriteLine("### CONNECTED WITH SERVER ###");
|
||||
|
||||
// Subscribe to a topic
|
||||
await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic("#").Build());
|
||||
var topic = $"/app/{resultToken.userid}-{appId.ToLower()}/subscribe";
|
||||
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 ###");
|
||||
});
|
||||
@ -254,7 +359,30 @@ namespace MyCore.Services
|
||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||
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)
|
||||
{
|
||||
@ -262,53 +390,110 @@ namespace MyCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CertificateValidationCallBack(
|
||||
object sender,
|
||||
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
|
||||
System.Security.Cryptography.X509Certificates.X509Chain chain,
|
||||
System.Net.Security.SslPolicyErrors sslPolicyErrors)
|
||||
private void GenerateClientAndAppId(string uuid)
|
||||
{
|
||||
// If the certificate is a valid, signed certificate, return true.
|
||||
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||||
{
|
||||
return true;
|
||||
var md5Result = CreateMD5($"API{uuid}");
|
||||
appId = md5Result;
|
||||
clientId = $"app:{md5Result}";
|
||||
}
|
||||
|
||||
// If there are errors in the certificate chain, look at each error to determine the cause.
|
||||
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
|
||||
private void ExecuteCommand(string uuid, Method method, CommandMqtt command, string payload = null, string callback = null, ToggleStatus toggleStatus = 0,int channelChosen = 0)
|
||||
{
|
||||
if (chain != null && chain.ChainStatus != null)
|
||||
var topic = $"/appliance/{uuid}/subscribe";
|
||||
var namespaceVar = "";
|
||||
var payloadVar = payload;
|
||||
bool multisocket = false;
|
||||
|
||||
// TODO Check device type.. and add switch case
|
||||
switch (command)
|
||||
{
|
||||
foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
|
||||
{
|
||||
if ((certificate.Subject == certificate.Issuer) &&
|
||||
(status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
case CommandMqtt.ABILITY:
|
||||
namespaceVar = Common.ABILITY;
|
||||
break;
|
||||
case CommandMqtt.CONSUMPTIONX:
|
||||
namespaceVar = PlugsAndBulbs.TOGGLEX;
|
||||
break;
|
||||
case CommandMqtt.TOGGLE:
|
||||
namespaceVar = PlugsAndBulbs.TOGGLE;
|
||||
break;
|
||||
case CommandMqtt.TOGGLEX:
|
||||
multisocket = true;
|
||||
namespaceVar = PlugsAndBulbs.TOGGLEX;
|
||||
break;
|
||||
case CommandMqtt.ELECTRICITY:
|
||||
namespaceVar = PlugsAndBulbs.ELECTRICITY;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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
|
||||
// if a plug
|
||||
// and if a toggleX ability
|
||||
if (method == Method.SET && multisocket)
|
||||
{
|
||||
// In all other cases, return false.
|
||||
return false;
|
||||
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