diff --git a/.vs/MyCore/DesignTimeBuild/.dtbcache b/.vs/MyCore/DesignTimeBuild/.dtbcache index 94c1a51..e9d0d9b 100644 Binary files a/.vs/MyCore/DesignTimeBuild/.dtbcache and b/.vs/MyCore/DesignTimeBuild/.dtbcache differ diff --git a/.vs/MyCore/v15/Server/sqlite3/storage.ide b/.vs/MyCore/v15/Server/sqlite3/storage.ide index ca14c19..a5369d2 100644 Binary files a/.vs/MyCore/v15/Server/sqlite3/storage.ide and b/.vs/MyCore/v15/Server/sqlite3/storage.ide differ diff --git a/.vs/MyCore/v15/Server/sqlite3/storage.ide-shm b/.vs/MyCore/v15/Server/sqlite3/storage.ide-shm index 8360d2b..6716a8b 100644 Binary files a/.vs/MyCore/v15/Server/sqlite3/storage.ide-shm and b/.vs/MyCore/v15/Server/sqlite3/storage.ide-shm differ diff --git a/.vs/MyCore/v15/Server/sqlite3/storage.ide-wal b/.vs/MyCore/v15/Server/sqlite3/storage.ide-wal index 049a2ce..9b3cb90 100644 Binary files a/.vs/MyCore/v15/Server/sqlite3/storage.ide-wal and b/.vs/MyCore/v15/Server/sqlite3/storage.ide-wal differ diff --git a/MyCore/Models/Meross/MerossDevice.cs b/MyCore/Models/Meross/MerossDevice.cs new file mode 100644 index 0000000..37ff9f7 --- /dev/null +++ b/MyCore/Models/Meross/MerossDevice.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Models.Meross +{ + public class MerossDevice + { + public string uuid { get; set; } + public int onlineStatus { get; set; } + public string devName { get; set; } + public string devIconId { get; set; } + public long bindTime { get; set; } + public string deviceType { get; set; } + public string subType { get; set; } + public List channels { get; set; } + public string region { get; set; } + public string firmwareVersion { get; set; } + public string hdwareVersion { get; set; } + public string userDevIcon { get; set; } + public int iconType { get; set; } + public string skillNumber { get; set; } + public string domain { get; set; } + public string reservedDomain { get; set; } + } + + public class Channel + { + public string type { get; set; } + public string devName { get; set; } + public string devIconId { get; set; } + } +} diff --git a/MyCore/Services/MQTTService.cs b/MyCore/Services/MQTTService.cs index b8f208a..573a261 100644 --- a/MyCore/Services/MQTTService.cs +++ b/MyCore/Services/MQTTService.cs @@ -17,7 +17,6 @@ namespace MyCore.Services { public class MQTTService { - private IMqttClient _client; private IMqttClientOptions _options; private string _mqttServer = "192.168.31.140"; @@ -112,9 +111,11 @@ namespace MyCore.Services // If so retrieve the device in database and his Type - // Retrieve the object + // Retrieve the device as an object (We have a defined list of devices. That's not a problem, it's a choice as we want reliability) - // Check in the list of automations if we have something to do with the message + // Check in the list of automations if we have something to do with the message (Check in mqtt input section) + + // Automation = id, name, // Load everydevice in cache.. ? Performance ? diff --git a/MyCore/Services/MerossService.cs b/MyCore/Services/MerossService.cs new file mode 100644 index 0000000..c3021db --- /dev/null +++ b/MyCore/Services/MerossService.cs @@ -0,0 +1,269 @@ +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Client.Options; +using MQTTnet.Formatter; +using MQTTnet.Implementations; +using MyCore.Models.Meross; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MyCore.Services +{ + public class MerossService + { + // API HTTP + private const string _merossUrl = "https://iot.meross.com"; + private static string _secret = "23x17ahWarFH6w29"; + private string _loginUrl = $"{_merossUrl}/v1/Auth/Login"; + private string _logUrl = $"{_merossUrl}/v1/log/user"; + private string _devList = $"{_merossUrl}/v1/Device/devList"; + + private static string username = "thomas.fransolet@hotmail.be"; + private static string password = "Coconuts07"; + + private static ResultToken resultToken; + + // API MQTT + private const int port = 2001; + private const string domain = "iot.meross.com"; + + private IMqttClient _client; + private IMqttClientOptions _options; + private string _userMQTT; + private string _passwordMQTT; + + public class Credential + { + public string email; + public string password; + } + + public class ResultToken + { + public string userid; + public string email; + public string token; + public string key; + } + + public enum RequestType + { + Login, + Log, + DeviceList + } + + public MerossService() + { + + try + { + var loginTask = Task.Run(() => PostURI(new Uri(_loginUrl), RequestType.Login)); + loginTask.Wait(); + + if (loginTask.Result != "") + { + var data = ((JObject)JsonConvert.DeserializeObject(loginTask.Result))["data"]; + resultToken = JsonConvert.DeserializeObject(data.ToString()); + + var deviceTask = Task.Run(() => PostURI(new Uri(_devList), RequestType.DeviceList)); + deviceTask.Wait(); + + if (deviceTask.Result != "") + { + data = ((JObject)JsonConvert.DeserializeObject(deviceTask.Result))["data"]; + var test = JsonConvert.DeserializeObject>(data.ToString()); + } + + var logTask = Task.Run(() => PostURI(new Uri(_logUrl), RequestType.Log)); + logTask.Wait(); + + if (logTask.Result != "") + { + data = ((JObject)JsonConvert.DeserializeObject(logTask.Result))["data"]; + } + + //MQTT CONNEXION + ConnexionToMqtt(); + } + } + catch (Exception e) + { + resultToken = null; + } + } + + static async Task PostURI(Uri u, RequestType requestType) + { + var response = string.Empty; + var requestParam = ""; + string nonce = GenerateNonce(16).ToUpper(); + var timeStamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); + + switch (requestType) + { + case RequestType.Login: + requestParam = Base64Encode(JsonConvert.SerializeObject(new Credential { email = username, password = password }).ToString()); + break; + case RequestType.Log: + requestParam = Base64Encode("{'extra': { }, 'model': 'Android,Android SDK built for x86_64', 'system': 'Android','uuid': '493dd9174941ed58waitForOpenWifi', 'vendor': 'Meross', 'version': '6.0'}"); + break; + case RequestType.DeviceList: + requestParam = "e30="; + break; + } + + string stringForMD5 = stringForMD5 = _secret + timeStamp.ToString() + nonce + requestParam; + string md5 = CreateMD5(stringForMD5); + + var payloadLogin = "{ \"params\":\"" + requestParam + "\",\"sign\":\"" + md5.ToLower() + "\",\"timestamp\":" + timeStamp + ",\"nonce\":\"" + nonce + "\"}"; + using (var client = new HttpClient()) + { + HttpContent c = new StringContent(payloadLogin, Encoding.UTF8, "application/json"); + if (resultToken != null) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", resultToken.token); + } + else + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic"); + } + + client.DefaultRequestHeaders.Add("vender", "Meross"); + client.DefaultRequestHeaders.Add("AppVersion", "1.3.0"); + client.DefaultRequestHeaders.Add("AppLanguage", "EN"); + client.DefaultRequestHeaders.Add("User-Agent", "okhttp/3.6.0"); + + HttpResponseMessage result = await client.PostAsync(u, c); + if (result.IsSuccessStatusCode) + { + response = await result.Content.ReadAsStringAsync(); + } + } + return response; + } + + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + + public static string CreateMD5(string input) + { + // Use input string to calculate MD5 hash + using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) + { + byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input); + byte[] hashBytes = md5.ComputeHash(inputBytes); + + // Convert the byte array to hexadecimal string + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hashBytes.Length; i++) + { + sb.Append(hashBytes[i].ToString("X2")); + } + return sb.ToString(); + } + } + + private static string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static Random random = new Random(); + + private static string GenerateNonce(int length) + { + var nonceString = new StringBuilder(); + for (int i = 0; i < length; i++) + { + nonceString.Append(validChars[random.Next(0, validChars.Length - 1)]); + } + + return nonceString.ToString(); + } + + private void ConnexionToMqtt() + { + try + { + // Create a new MQTT client. + _client = new MqttFactory().CreateMqttClient(); + + string stringForMD5 = resultToken.userid + resultToken.key; + string hashed_password = CreateMD5(stringForMD5); + + MqttClientOptionsBuilderTlsParameters tlsParameters = new MqttClientOptionsBuilderTlsParameters(); + tlsParameters.UseTls = true; + tlsParameters.SslProtocol = System.Security.Authentication.SslProtocols.Tls; + + _options = new MqttClientOptionsBuilder() + .WithClientId("app: 08d4c9f99da40203ebc798a79512ec14") + .WithTcpServer(domain, port) + .WithCredentials(resultToken.userid, hashed_password) + .WithTls(tlsParameters) + .WithProtocolVersion(MqttProtocolVersion.V311) + .Build(); + + + _client.ConnectAsync(_options, CancellationToken.None).ContinueWith(res => { + if (res.Status == TaskStatus.RanToCompletion) + { + Console.WriteLine("It's connected"); + } + else + { + Console.WriteLine($"Error connecting to {domain}"); + } + }); + + + _client.UseDisconnectedHandler(async e => + { + Console.WriteLine("### DISCONNECTED FROM SERVER ###"); + await Task.Delay(TimeSpan.FromSeconds(5)); + + try + { + await _client.ConnectAsync(_options, CancellationToken.None); // Since 3.0.5 with CancellationToken + } + catch + { + Console.WriteLine("### RECONNECTING FAILED ###"); + } + }); + + _client.UseConnectedHandler(async e => + { + Console.WriteLine("### CONNECTED WITH SERVER ###"); + + // Subscribe to a topic + await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic("#").Build()); + + Console.WriteLine("### SUBSCRIBED ###"); + }); + + _client.UseApplicationMessageReceivedHandler(e => + { + Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); + Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); + Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); + Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); + Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); + Console.WriteLine(); + + }); + } + catch (Exception e) + { + + } + } + } +} diff --git a/MyCore/Startup.cs b/MyCore/Startup.cs index 954c651..733ec87 100644 --- a/MyCore/Startup.cs +++ b/MyCore/Startup.cs @@ -26,7 +26,9 @@ namespace MyCore { Configuration = configuration; - MQTTService mQTTService = new MQTTService(); + //MQTTService mQTTService = new MQTTService(); + + MerossService merossService = new MerossService(); } public IConfiguration Configuration { get; }