diff --git a/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs b/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs new file mode 100644 index 0000000..784c343 --- /dev/null +++ b/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs @@ -0,0 +1,15 @@ +using MyCore.Interfaces.DTO; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MyCore.Interfaces.Models.MyControlPanel +{ + public class SupportedDevice + { + public string Manufacturer { get; set; } + public string Model { get; set; } + public string Description { get; set; } + public DeviceType DeviceType { get; set; } + } +} diff --git a/MyCore/Controllers/AuthenticationController.cs b/MyCore/Controllers/AuthenticationController.cs index 375b0ce..5372e81 100644 --- a/MyCore/Controllers/AuthenticationController.cs +++ b/MyCore/Controllers/AuthenticationController.cs @@ -11,6 +11,10 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; +using Mqtt.Client.AspNetCore.Services; +using MyCore.Services.MyControlPanel; +using MyCore.Services; +using MyCore.Services.Devices; namespace MyCore.Service.Controllers { @@ -24,16 +28,25 @@ namespace MyCore.Service.Controllers { private readonly ILogger _logger; private readonly TokensService _tokensService; + private readonly DeviceDatabaseService _DeviceDatabaseService; + private readonly ProviderDatabaseService _ProviderDatabaseService; + private readonly LocationDatabaseService _LocationDatabaseService; + private readonly UserDatabaseService _UserDatabaseService; + private readonly ActionService _ActionService; + private readonly IMqttClientService _mqttClientService; + private readonly IMqttOnlineClientService _mqttOnlineClientService; - /// - /// Constructor - /// - /// Logger - /// Tokens service - public AuthenticationController(ILogger logger, TokensService tokensService) + public AuthenticationController(ILogger logger, TokensService tokensService, DeviceDatabaseService DeviceDatabaseService, ProviderDatabaseService ProviderDatabaseService, LocationDatabaseService LocationDatabaseService, UserDatabaseService UserDatabaseService, ActionService ActionService, MqttClientServiceProvider provider, MqttClientOnlineServiceProvider onlineProvider) { _logger = logger; _tokensService = tokensService; + _DeviceDatabaseService = DeviceDatabaseService; + _ProviderDatabaseService = ProviderDatabaseService; + _LocationDatabaseService = LocationDatabaseService; + _UserDatabaseService = UserDatabaseService; + _ActionService = ActionService; + _mqttClientService = provider.MqttClientService; + _mqttOnlineClientService = onlineProvider.MqttOnlineClientService; } private ActionResult Authenticate(string email, string password) @@ -41,7 +54,7 @@ namespace MyCore.Service.Controllers try { var token = _tokensService.Authenticate(email.ToLower(), password); - + MqttClientService.SetServices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, _ActionService, "5fef55bae30e1016d4776bfe"); // TODO Get userId when connected !! return Ok(token); } /*catch (UnauthorizedAccessException ex) diff --git a/MyCore/Controllers/Devices/DeviceController.cs b/MyCore/Controllers/Devices/DeviceController.cs index f60a6c3..f2bf7aa 100644 --- a/MyCore/Controllers/Devices/DeviceController.cs +++ b/MyCore/Controllers/Devices/DeviceController.cs @@ -138,11 +138,11 @@ namespace MyCore.Controllers Provider provider = ProviderService.GetProviderById(this._ProviderDatabaseService, userId, providerId); if (provider == null) - throw new KeyNotFoundException("Provider id is null"); + throw new KeyNotFoundException("Provider not found"); - List devicesCreated = await DeviceService.CreateFromProvider(this._DeviceDatabaseService, this._ProviderDatabaseService, this._LocationDatabaseService, userId, provider); + Dictionary> devices = await DeviceService.CreateFromProvider(this._DeviceDatabaseService, this._ProviderDatabaseService, this._LocationDatabaseService, userId, provider); - return new OkObjectResult(devicesCreated); + return new OkObjectResult(devices); } catch (InvalidOperationException ex) { diff --git a/MyCore/Extensions/AppSettingsProvider.cs b/MyCore/Extensions/AppSettingsProvider.cs index 18996ef..56fa97e 100644 --- a/MyCore/Extensions/AppSettingsProvider.cs +++ b/MyCore/Extensions/AppSettingsProvider.cs @@ -11,4 +11,10 @@ public static BrokerOnlineHostSettings BrokerHostOnlineSettings; public static ClientOnlineSettings ClientOnlineSettings; } + + public class AppSettingsMerossProvider + { + public static BrokerMerossHostSettings BrokerHostMerossSettings; + public static ClientMerossSettings ClientMerossSettings; + } } diff --git a/MyCore/Extensions/AspCoreMqttClientOptionBuilder.cs b/MyCore/Extensions/AspCoreMqttClientOptionBuilder.cs index c9fe00b..90ed5e8 100644 --- a/MyCore/Extensions/AspCoreMqttClientOptionBuilder.cs +++ b/MyCore/Extensions/AspCoreMqttClientOptionBuilder.cs @@ -22,4 +22,14 @@ namespace Mqtt.Client.AspNetCore.Options ServiceOnlineProvider = serviceProvider; } } + + public class AspCoreMerossClientOptionBuilder : MqttClientOptionsBuilder + { + public IServiceProvider ServiceMerossProvider { get; } + + public AspCoreMerossClientOptionBuilder(IServiceProvider serviceProvider) + { + ServiceMerossProvider = serviceProvider; + } + } } diff --git a/MyCore/Extensions/BrokerHostSettings.cs b/MyCore/Extensions/BrokerHostSettings.cs index 5244f52..cd52b31 100644 --- a/MyCore/Extensions/BrokerHostSettings.cs +++ b/MyCore/Extensions/BrokerHostSettings.cs @@ -11,4 +11,10 @@ public string Host { set; get; } public int Port { set; get; } } + + public class BrokerMerossHostSettings + { + public string Host { set; get; } + public int Port { set; get; } + } } diff --git a/MyCore/Extensions/ClientSettings.cs b/MyCore/Extensions/ClientSettings.cs index f250753..79e9416 100644 --- a/MyCore/Extensions/ClientSettings.cs +++ b/MyCore/Extensions/ClientSettings.cs @@ -13,4 +13,11 @@ public string UserName { set; get; } public string Password { set; get; } } + + public class ClientMerossSettings + { + public string Id { set; get; } + public string UserName { set; get; } + public string Password { set; get; } + } } diff --git a/MyCore/Extensions/IMqttClientService.cs b/MyCore/Extensions/IMqttClientService.cs index 45b739a..ece1223 100644 --- a/MyCore/Extensions/IMqttClientService.cs +++ b/MyCore/Extensions/IMqttClientService.cs @@ -18,4 +18,11 @@ namespace Mqtt.Client.AspNetCore.Services IMqttApplicationMessageReceivedHandler { } + + public interface IMqttMerossClientService : IHostedService, + IMqttClientConnectedHandler, + IMqttClientDisconnectedHandler, + IMqttApplicationMessageReceivedHandler + { + } } diff --git a/MyCore/Extensions/MqttClientMerossService.cs b/MyCore/Extensions/MqttClientMerossService.cs new file mode 100644 index 0000000..3c008c7 --- /dev/null +++ b/MyCore/Extensions/MqttClientMerossService.cs @@ -0,0 +1,520 @@ +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Disconnecting; +using MQTTnet.Client.Options; +using MQTTnet.Formatter; +using MyCore.Interfaces.Models; +using MyCore.Services.Devices; +using MyCore.Services.MyControlPanel; +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.Security.Authentication; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static MyCore.Interfaces.Models.DeviceAbilities; + +namespace Mqtt.Client.AspNetCore.Services +{ + public class MqttClientMerossService : IMqttMerossClientService + { + // 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; + + private List allDevices; + + // API MQTT + private const int port = 2001; + private const string domain = "iot.meross.com"; + + private static IMqttClient client; + private IMqttClientOptions options; + private string _userMQTT; + private string _passwordMQTT; + + public string clientId = ""; + public static string appId = ""; + + 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 MqttClientMerossService(IMqttClientOptions _options) + { + try + { + // TODO + /*this.username = username; + this.password = password;*/ + + // LOGIN + var loginTask = Task.Run(() => PostURI(new Uri(_loginUrl), RequestType.Login)); + loginTask.Wait(); + + if (loginTask.Result != "") + { + //RESULT TOKEN + var data = ((JObject)JsonConvert.DeserializeObject(loginTask.Result))["data"]; + resultToken = JsonConvert.DeserializeObject(data.ToString()); + + allDevices = GetMerossDevices(); + + // RETRIEVE LOG TODO Create a GetLogs method + /*var logTask = Task.Run(() => PostURI(new Uri(_logUrl), RequestType.Log)); + logTask.Wait(); + + if (logTask.Result != "") + { + data = ((JObject)JsonConvert.DeserializeObject(logTask.Result))["data"]; + }*/ + } + } + catch (Exception e) + { + resultToken = null; + throw new AuthenticationException("Bad service username or password"); + } + + 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(clientId) + .WithTcpServer(domain, port) + .WithCredentials(resultToken.userid, hashed_password) + .WithCleanSession() + .WithTls() + .WithProtocolVersion(MqttProtocolVersion.V311) + .Build(); + + client = new MqttFactory().CreateMqttClient(); + + ConfigureMqttClient(); + } + + private void ConfigureMqttClient() + { + client.ConnectedHandler = this; + client.DisconnectedHandler = this; + client.ApplicationMessageReceivedHandler = this; + } + + public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e) + { + Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); + Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); + var payload = ""; + if (e.ApplicationMessage.Payload != null) + { + Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); + payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + } + + Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); + Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); + Console.WriteLine(); + + var topic = e.ApplicationMessage.Topic; + + /* 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} + * } + * } + */ + return null; + } + + public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs) + { + System.Console.WriteLine("connected meross"); + + var topic = $"/app/{resultToken.userid}-{appId.ToLower()}/subscribe"; + + await client.SubscribeAsync(topic); + //await client.SubscribeAsync("#"); + + //await client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build()); + + /*MerossDevice multisocket = allDevices.Where(d => d.onlineStatus == 1 && d.deviceType == "mss425f").FirstOrDefault(); + + 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 ###");*/ + } + + public static 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) + { + 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; + } + + // 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 static 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 async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs) + { + if (!client.IsConnected) + { + await client.ReconnectAsync(); + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await client.ConnectAsync(options); + if (!client.IsConnected) + { + await client.ReconnectAsync(); + } + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + var disconnectOption = new MqttClientDisconnectOptions + { + ReasonCode = MqttClientDisconnectReason.NormalDisconnection, + ReasonString = "NormalDiconnection" + }; + await client.DisconnectAsync(disconnectOption, cancellationToken); + } + await client.DisconnectAsync(); + } + + public static async Task PublishMessage(string topic, string message) + { + var mqttMessage = new MqttApplicationMessageBuilder() + .WithTopic(topic) + .WithPayload(message) + .WithExactlyOnceQoS() + .WithRetainFlag() + .Build(); + + if (client.IsConnected) + await client.PublishAsync(mqttMessage).ContinueWith(res => { + if (res.Status == TaskStatus.RanToCompletion) + { + } + }); + } + + 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(); + } + } + + public 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; + } + + private void GenerateClientAndAppId(string uuid) + { + var md5Result = CreateMD5($"API{uuid}"); + appId = md5Result; + clientId = $"app:{md5Result}"; + } + + 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(); + } + + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + + public static List GetMerossDevices() + { + // GET DEVICE LIST + var deviceTask = Task.Run(() => PostURI(new Uri(_devList), RequestType.DeviceList)); + deviceTask.Wait(); + + if (deviceTask.Result != "") + { + var data = ((JObject)JsonConvert.DeserializeObject(deviceTask.Result))["data"]; + // RETRIEVE ALL DEVICES + allDevices = JsonConvert.DeserializeObject>(data.ToString()); + } + else + throw new HttpRequestException("Error retrieving meross devices"); + + return allDevices; + } + + #region MQTT + + 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 + } + #endregion MQTT + } + +} diff --git a/MyCore/Extensions/MqttClientMerossServiceProvider.cs b/MyCore/Extensions/MqttClientMerossServiceProvider.cs new file mode 100644 index 0000000..3463ef1 --- /dev/null +++ b/MyCore/Extensions/MqttClientMerossServiceProvider.cs @@ -0,0 +1,12 @@ +namespace Mqtt.Client.AspNetCore.Services +{ + public class MqttClientMerossServiceProvider + { + public readonly IMqttMerossClientService MqttMerossClientService; + + public MqttClientMerossServiceProvider(IMqttMerossClientService mqttMerossClientService) + { + MqttMerossClientService = mqttMerossClientService; + } + } +} diff --git a/MyCore/Extensions/MqttClientOnlineService.cs b/MyCore/Extensions/MqttClientOnlineService.cs index cc1c32d..1b65f64 100644 --- a/MyCore/Extensions/MqttClientOnlineService.cs +++ b/MyCore/Extensions/MqttClientOnlineService.cs @@ -22,6 +22,14 @@ namespace Mqtt.Client.AspNetCore.Services public MqttClientOnlineService(IMqttClientOptions options) { this.onlineOptions = options; + + this.onlineOptions = new MqttClientOptionsBuilder() + .WithClientId("ApiService") + .WithTcpServer("myhomie.be") // TODO replace by localhost + .WithCredentials("thomas", "MyCore,1") + .WithCleanSession() + .Build(); + mqttClient = new MqttFactory().CreateMqttClient(); ConfigureMqttClient(); } @@ -79,7 +87,6 @@ namespace Mqtt.Client.AspNetCore.Services System.Console.WriteLine("connected"); //await mqttClient.SubscribeAsync("hello/world"); await mqttClient.SubscribeAsync("#"); - await PublishMessage("zigbee2mqtt/bridge/config/devices/get", ""); } public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs) @@ -88,7 +95,6 @@ namespace Mqtt.Client.AspNetCore.Services { await mqttClient.ReconnectAsync(); } - //throw new System.NotImplementedException(); } public async Task StartAsync(CancellationToken cancellationToken) diff --git a/MyCore/Extensions/MqttClientService.cs b/MyCore/Extensions/MqttClientService.cs index 9f35888..129ca1a 100644 --- a/MyCore/Extensions/MqttClientService.cs +++ b/MyCore/Extensions/MqttClientService.cs @@ -4,6 +4,8 @@ using MQTTnet.Client.Connecting; using MQTTnet.Client.Disconnecting; using MQTTnet.Client.Options; using MyCore.Interfaces.Models; +using MyCore.Services.Devices; +using MyCore.Services.MyControlPanel; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -18,7 +20,11 @@ namespace Mqtt.Client.AspNetCore.Services private static IMqttClient mqttClient; private IMqttClientOptions options; public static List devices = new List(); - + public static string userId; + static DeviceDatabaseService _deviceDatabaseService; + static ProviderDatabaseService _providerDatabaseService; + static LocationDatabaseService _locationDatabaseService; + static ActionService _actionService; public MqttClientService(IMqttClientOptions options) { @@ -51,13 +57,19 @@ namespace Mqtt.Client.AspNetCore.Services payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); } - Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); Console.WriteLine(); var topic = e.ApplicationMessage.Topic; + if (_actionService != null) { + ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _providerDatabaseService, _locationDatabaseService, userId); + } + + //if () { } + //List Devices = _DeviceDatabaseService.GetByProviderId(topic); + switch (topic) { case "zigbee2mqtt/bridge/config/devices": @@ -139,6 +151,15 @@ namespace Mqtt.Client.AspNetCore.Services return devices; } + public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, ActionService _ActionService, string UserId) + { + _deviceDatabaseService = _DeviceDatabaseService; + _providerDatabaseService = _ProviderDatabaseService; + _locationDatabaseService = _LocationDatabaseService; + _actionService = _ActionService; + userId = UserId; + } + public static async Task> AskDevicesAsync() { await PublishMessage("zigbee2mqtt/bridge/config/devices/get", ""); diff --git a/MyCore/Extensions/ServiceCollectionExtension.cs b/MyCore/Extensions/ServiceCollectionExtension.cs index 1b24e6a..e3dbaf9 100644 --- a/MyCore/Extensions/ServiceCollectionExtension.cs +++ b/MyCore/Extensions/ServiceCollectionExtension.cs @@ -4,6 +4,7 @@ using Mqtt.Client.AspNetCore.Options; using Mqtt.Client.AspNetCore.Services; using Mqtt.Client.AspNetCore.Settings; using MQTTnet.Client.Options; +using MyCore.Services; using System; namespace MyCore.Service.Extensions @@ -41,7 +42,21 @@ namespace MyCore.Service.Extensions return services; } - + public static IServiceCollection AddMerossClientHostedService(this IServiceCollection services) + { + services.AddMerossClientServiceWithConfig(aspOnlineOptionBuilder => + { + /*var clientSettings = AppSettingsMerossProvider.ClientMerossSettings; + var brokerHostSettings = AppSettingsMerossProvider.BrokerHostMerossSettings; + + aspOnlineOptionBuilder + .WithCredentials(clientSettings.UserName, clientSettings.Password) + .WithClientId(clientSettings.Id) + .WithTcpServer(brokerHostSettings.Host, brokerHostSettings.Port);*/ + }); + return services; + } + private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action configure) { @@ -88,5 +103,27 @@ namespace MyCore.Service.Extensions return services; } + private static IServiceCollection AddMerossClientServiceWithConfig(this IServiceCollection services, Action configure) + { + /*services.AddSingleton(serviceMerossProvider => + { + var optionBuilder = new AspCoreMerossClientOptionBuilder(serviceMerossProvider); + configure(optionBuilder); + return optionBuilder.Build(); + });*/ + services.AddSingleton(); + services.AddSingleton(serviceProvider => + { + return serviceProvider.GetService(); + }); + services.AddSingleton(serviceProvider => + { + var merossClientService = serviceProvider.GetService(); + var merossServiceProvider = new MqttClientMerossServiceProvider(merossClientService); + return merossServiceProvider; + }); + return services; + } + } } diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 309cea9..9c746ce 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -1,12 +1,89 @@ -using System; +using Mqtt.Client.AspNetCore.Services; +using MyCore.Interfaces.Models; +using MyCore.Services.MyControlPanel; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static Mqtt.Client.AspNetCore.Services.MqttClientMerossService; namespace MyCore.Services.Devices { public class ActionService { + public static bool isOpen = false; + public static long lastActionTime; // TODO it's here that action are thrown.. Call from Mqtt Or other service like controller if from RpiServices + public async static Task HandleActionFromMQTTAsync(string topic, string message, DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId) + { + // TODO Check if last action is not too close for each action + var actionTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + var providers = _ProviderDatabaseService.GetAll(userId); + string[] topicSplit = topic.Split('/'); + switch (topicSplit[0]) { + case "zigbee2mqtt": + // switch case according to device type (topic !) + if (topicSplit[1].Contains("MagicCube0")) + { + var test = JsonConvert.DeserializeObject(message); + if (test.Action == "shake") + { + if (YeelightService.devices.Count <= 0) { + await YeelightService.GetDevices(); + } + var labLamp = YeelightService.devices.Where(d => d.Hostname == "192.168.31.74").FirstOrDefault(); + if (labLamp != null) { + Task.Run(async () => { await YeelightService.Toggle(labLamp); }); + } + } + if (test.Action == "tap") + { + var provider = providers.Where(p => p.Type == "meross").FirstOrDefault(); + var merossDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); + + var multiprise = merossDevices.Where(md => md.Name == "Multiprise bureau").FirstOrDefault(); + //var prise = merossDevices.Where(md => md.Name == "Imprimante 3D").FirstOrDefault(); + + if (multiprise != null) + { + //As multisocket channel 0 is all the sockets, skip 0 + var tests = actionTime - lastActionTime; + if (tests >= 1000) { + isOpen = !isOpen; + if (isOpen) + { + /*foreach (var channel in multiprise.channels) + { + if (i != 0) + MerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, i); + i++; + }*/ + MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 1); + //MqttClientMerossService.ExecuteCommand(prise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 0); + } + else + { + MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 1); + //MqttClientMerossService.ExecuteCommand(prise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 0); + } + } + + lastActionTime = actionTime; + + } + /*if (lightStateIkeaBulb == LightState.Undefined || lightStateIkeaBulb == LightState.Off) + PublishMessage("zigbee2mqtt/0x14b457fffe7628fa/set", "{\"state\": \"ON\"}"); + else + PublishMessage("zigbee2mqtt/0x14b457fffe7628fa/set", "{\"state\": \"OFF\"}");*/ + } + //await MqttClientOnlineService.PublishMessage("Notification", "Hey magic cube 0 !"); + } + break; + default: + break; + } + } } } diff --git a/MyCore/Services/Devices/DeviceService.cs b/MyCore/Services/Devices/DeviceService.cs index b5f85f3..a289141 100644 --- a/MyCore/Services/Devices/DeviceService.cs +++ b/MyCore/Services/Devices/DeviceService.cs @@ -2,6 +2,7 @@ using Mqtt.Client.AspNetCore.Services; using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; +using MyCore.Interfaces.Models.MyControlPanel; using MyCore.Services.MyControlPanel; using System; using System.Collections.Generic; @@ -14,6 +15,25 @@ namespace MyCore.Services.Devices { public class DeviceService { + static List supportedDevices = new List() { + new SupportedDevice { Manufacturer = "Aqara", Model = "RTCGQ11LM", Description = "Aqara human body movement and illuminance sensor", DeviceType = DeviceType.Motion }, + new SupportedDevice { Manufacturer = "Aqara", Model = "DJT11LM", Description = "Aqara vibration sensor", DeviceType = DeviceType.Environment }, + new SupportedDevice { Manufacturer = "Aqara", Model = "SJCGQ11LM", Description = "Aqara water leak sensor", DeviceType = DeviceType.Environment }, + new SupportedDevice { Manufacturer = "Aqara", Model = "WSDCGQ11LM", Description = "Aqara temperature, humidity and pressure sensor", DeviceType = DeviceType.Environment }, + + new SupportedDevice { Manufacturer = "Xiaomi", Model = "WXKG01LM", Description = "MiJia wireless switch", DeviceType = DeviceType.Switch }, + new SupportedDevice { Manufacturer = "Xiaomi", Model = "MFKZQ01LM", Description = "Mi/Aqara smart home cube", DeviceType = DeviceType.Switch }, + new SupportedDevice { Manufacturer = "Xiaomi", Model = "MCCGQ01LM", Description = "MiJia door & window contact sensor", DeviceType = DeviceType.Door }, + new SupportedDevice { Manufacturer = "Xiaomi", Model = "JTYJ-GD-01LM/BW", Description = "MiJia Honeywell smoke detector", DeviceType = DeviceType.Environment }, + new SupportedDevice { Manufacturer = "Xiaomi", Model = "ZNCZ04LM", Description = "Mi power plug ZigBee EU", DeviceType = DeviceType.Plug }, + + new SupportedDevice { Manufacturer = "Ikea", Model = "E1744", Description = "SYMFONISK Sound Controller", DeviceType = DeviceType.Switch }, + new SupportedDevice { Manufacturer = "Ikea", Model = "LED1836G9", Description = "TRADFRI LED bulb E26/E27 806 lumen, dimmable, warm white", DeviceType = DeviceType.Light }, + new SupportedDevice { Manufacturer = "Ikea", Model = "LED1842G3", Description = "TRADFRI LED bulb E27 WW clear 250 lumen, dimmable", DeviceType = DeviceType.Light }, + new SupportedDevice { Manufacturer = "Ikea", Model = "LED1837R5", Description = "TRADFRI LED bulb GU10 400 lumen, dimmable", DeviceType = DeviceType.Light }, + new SupportedDevice { Manufacturer = "Ikea", Model = "E1743", Description = "TRADFRI ON/OFF switch", DeviceType = DeviceType.Switch }, + new SupportedDevice { Manufacturer = "Ikea", Model = "E1524/E1810", Description = "TRADFRI remote control", DeviceType = DeviceType.Switch }, + }; public static DeviceDetailDTO CreateOrUpdate(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, DeviceDetailDTO deviceDetailDTO, bool create) { @@ -32,6 +52,8 @@ namespace MyCore.Services.Devices device.UserId = userId; device.Name = deviceDetailDTO.Name; + device.Description = deviceDetailDTO.Description; + device.ManufacturerName = deviceDetailDTO.ManufacturerName; if (_ProviderDatabaseService.IsExist(deviceDetailDTO.ProviderId)) device.ProviderId = deviceDetailDTO.ProviderId; @@ -47,6 +69,7 @@ namespace MyCore.Services.Devices device.Model = deviceDetailDTO.Model; device.Type = deviceDetailDTO.Type; device.FirmwareVersion = deviceDetailDTO.FirmwareVersion; + device.HardwareVersion = deviceDetailDTO.HardwareVersion; device.Status = deviceDetailDTO.Status; if (create) device.ConnectionStatus = ConnectionStatus.Unknown; @@ -74,31 +97,31 @@ namespace MyCore.Services.Devices return _DeviceDatabaseService.Update(device.Id, device).ToDTO(); } - public async static Task> CreateFromProvider(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, Provider provider) + public async static Task>> CreateFromProvider(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, Provider provider) { if (!ProviderService.IsProviderSupported(provider.Type)) throw new KeyNotFoundException("Provider is not yet supported"); - List createdDevice = new List(); + Dictionary> devices = new Dictionary>(); try { switch (provider.Type) { case "arlo": List arloDevices = new ArloService(provider.Username, provider.Password).GetAllDevices(); - createdDevice = CreateArloDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, arloDevices, provider); + devices = CreateArloDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, arloDevices, provider); break; case "meross": - List merossDevices = new MerossService(provider.Username, provider.Password).GetMerossDevices(); - createdDevice = CreateMerossDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, merossDevices, provider); + List merossDevices = MqttClientMerossService.GetMerossDevices(); // TO TEST IF IT WORKS + devices = CreateMerossDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, merossDevices, provider); break; case "yeelight": - List yeelightDevices = await new YeelightService().GetDevices(); - createdDevice = CreateYeelightDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, yeelightDevices, provider); + List yeelightDevices = await YeelightService.GetDevices(); + devices = CreateYeelightDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, yeelightDevices, provider); break; case "zigbee2mqtt": List zigbee2MqttDevices = MqttClientService.devices; - createdDevice = await CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider); + devices = await CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider); break; } } @@ -111,14 +134,15 @@ namespace MyCore.Services.Devices catch (Exception ex) { } - return createdDevice; + return devices; } - public static async Task> CreateFromZigbeeAsync(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List zigbee2MqttDevices, Provider provider) + public static async Task>> CreateFromZigbeeAsync(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List zigbee2MqttDevices, Provider provider) { List createdZigbeeDevices = new List(); + List notSupportedZigbeeDevices = new List(); - List existingDevices = _DeviceDatabaseService.GetByprovider(provider.Id); + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); if (zigbee2MqttDevices.Count <= 0) { @@ -136,25 +160,12 @@ namespace MyCore.Services.Devices deviceDetailDTO.ProviderId = provider.Id; deviceDetailDTO.ProviderName = provider.Name; - deviceDetailDTO.Model = zigbee2MqttDevice.model; // Will be the base to understand incoming messages ! - deviceDetailDTO.FirmwareVersion = zigbee2MqttDevice.softwareBuildID; + deviceDetailDTO.Model = zigbee2MqttDevice.type == "Coordinator" ? "Coordinator" : zigbee2MqttDevice.model; // Is the base to understand incoming messages ! + deviceDetailDTO.FirmwareVersion = zigbee2MqttDevice.softwareBuildID; deviceDetailDTO.HardwareVersion = zigbee2MqttDevice.hardwareVersion.ToString(); - - deviceDetailDTO.Type = DeviceType.Unknown; - - // TODO ! => switch case en fonction du model pour gestion des appareils compatibles - if (zigbee2MqttDevice.type == "Coordinator") deviceDetailDTO.Type = DeviceType.Gateway; - if (zigbee2MqttDevice.modelID.Contains("plug")) deviceDetailDTO.Type = DeviceType.Plug; - if (zigbee2MqttDevice.modelID.Contains("bulb")) deviceDetailDTO.Type = DeviceType.Light; - if (zigbee2MqttDevice.modelID.Contains("remote") || zigbee2MqttDevice.modelID.Contains("switch") || zigbee2MqttDevice.modelID.Contains("cube") || zigbee2MqttDevice.modelID.Contains("Sound Controller")) deviceDetailDTO.Type = DeviceType.Switch; - if (zigbee2MqttDevice.modelID.Contains("magnet")) deviceDetailDTO.Type = DeviceType.Door; - if (zigbee2MqttDevice.modelID.Contains("motion")) deviceDetailDTO.Type = DeviceType.Motion; - if (zigbee2MqttDevice.modelID.Contains("weather") || zigbee2MqttDevice.modelID.Contains("smoke") || zigbee2MqttDevice.modelID.Contains("wleak") || zigbee2MqttDevice.modelID.Contains("vibration")) deviceDetailDTO.Type = DeviceType.Environment; - - - deviceDetailDTO.Battery = zigbee2MqttDevice.powerSource.Contains("Battery"); - deviceDetailDTO.ManufacturerName = zigbee2MqttDevice.vendor; + deviceDetailDTO.Battery = zigbee2MqttDevice.powerSource == null ? false : zigbee2MqttDevice.powerSource.Contains("Battery"); + deviceDetailDTO.ManufacturerName = zigbee2MqttDevice.vendor == null ? provider.Type : zigbee2MqttDevice.vendor.ToLower(); deviceDetailDTO.MeansOfCommunications = new List(); deviceDetailDTO.MeansOfCommunications.Add(MeansOfCommunication.Zigbee); @@ -162,25 +173,48 @@ namespace MyCore.Services.Devices deviceDetailDTO.UpdatedDate = DateTime.Now; deviceDetailDTO.LastStateDate = new DateTime(zigbee2MqttDevice.lastSeen); - createdZigbeeDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); + deviceDetailDTO.Type = zigbee2MqttDevice.type == "Coordinator" ? DeviceType.Gateway : GetDeviceTypeFromZigbeeModel(zigbee2MqttDevice.model); + + if (deviceDetailDTO.Type != DeviceType.Unknown) + { + // Supoorted device ! + createdZigbeeDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); + } + else { + // Not yet supported ! + notSupportedZigbeeDevices.Add(deviceDetailDTO); + } } - return createdZigbeeDevices; - + return new Dictionary>() { + { "createdDevices", createdZigbeeDevices }, + { "notSupportedDevices", notSupportedZigbeeDevices } + }; } - public static List CreateArloDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List arloDevices, Provider provider) + private static DeviceType GetDeviceTypeFromZigbeeModel(string zigbeeModel) + { + return supportedDevices.Any(sd => sd.Model == zigbeeModel) ? + // It's supported ! + supportedDevices.Where(sd => sd.Model == zigbeeModel).FirstOrDefault().DeviceType : + // Not yet supported + DeviceType.Unknown; + } + + public static Dictionary> CreateArloDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List arloDevices, Provider provider) { List createdArloDevices = new List(); - List existingDevices = _DeviceDatabaseService.GetByprovider(provider.Id); + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); arloDevices = arloDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd.deviceId)).ToList(); foreach (var arlo in arloDevices) { DeviceDetailDTO deviceDetailDTO = new DeviceDetailDTO(); + deviceDetailDTO.ManufacturerName = provider.Type; deviceDetailDTO.Name = arlo.deviceName; + deviceDetailDTO.Description = arlo.deviceName; // As description not exist, put name in description deviceDetailDTO.ServiceIdentification = arlo.deviceId; deviceDetailDTO.ProviderId = provider.Id; deviceDetailDTO.ProviderName = provider.Name; @@ -221,21 +255,24 @@ namespace MyCore.Services.Devices createdArloDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); } - return createdArloDevices; + return new Dictionary>() { { "createdDevices", createdArloDevices } }; // TODO Check if exist not supported devices } - public static List CreateMerossDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List merossDevices, Provider provider) + public static Dictionary> CreateMerossDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List merossDevices, Provider provider) { List createdMerossDevices = new List(); - List existingDevices = _DeviceDatabaseService.GetByprovider(provider.Id); + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); merossDevices = merossDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd.uuid)).ToList(); foreach (var meross in merossDevices) { DeviceDetailDTO deviceDetailDTO = new DeviceDetailDTO(); + deviceDetailDTO.ManufacturerName = provider.Type; + deviceDetailDTO.HardwareVersion = meross.hdwareVersion; deviceDetailDTO.Name = meross.devName; + deviceDetailDTO.Description = meross.devName; // As description not exist, put name in description deviceDetailDTO.ServiceIdentification = meross.uuid; deviceDetailDTO.ProviderId = provider.Id; deviceDetailDTO.ProviderName = provider.Name; @@ -273,21 +310,23 @@ namespace MyCore.Services.Devices createdMerossDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); } - return createdMerossDevices; + return new Dictionary>() { { "createdDevices", createdMerossDevices } }; // TODO Check if exist not supported devices } - public static List CreateYeelightDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List yeelightDevices, Provider provider) + public static Dictionary> CreateYeelightDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List yeelightDevices, Provider provider) { List createdYeelightDevices = new List(); - List existingDevices = _DeviceDatabaseService.GetByprovider(provider.Id); + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); yeelightDevices = yeelightDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd.Id)).ToList(); foreach (var light in yeelightDevices) { DeviceDetailDTO deviceDetailDTO = new DeviceDetailDTO(); + deviceDetailDTO.ManufacturerName = provider.Type; deviceDetailDTO.Name = light.Name; + deviceDetailDTO.Description = light.Name; // As description not exist, put name in description deviceDetailDTO.IpAddress = light.Hostname; deviceDetailDTO.ServiceIdentification = light.Id; deviceDetailDTO.ProviderId = provider.Id; @@ -317,7 +356,7 @@ namespace MyCore.Services.Devices createdYeelightDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); } - return createdYeelightDevices; + return new Dictionary>() { { "createdDevices", createdYeelightDevices } }; // TODO Check if exist not supported devices } } } diff --git a/MyCore/Services/Devices/MQTTService.cs b/MyCore/Services/Devices/MQTTService.cs index 430abc0..e9005d4 100644 --- a/MyCore/Services/Devices/MQTTService.cs +++ b/MyCore/Services/Devices/MQTTService.cs @@ -146,8 +146,8 @@ namespace MyCore.Services var test = JsonConvert.DeserializeObject(payload); if (test.Action == "shake") { - var labLamp = yeelightService.devices.Where(d => d.Hostname == "192.168.31.74").FirstOrDefault(); - Task.Run(async () => { await yeelightService.Toggle(labLamp); }); + /*var labLamp = yeelightService.devices.Where(d => d.Hostname == "192.168.31.74").FirstOrDefault(); + Task.Run(async () => { await yeelightService.Toggle(labLamp); });*/ } if (test.Action == "slide") { @@ -184,10 +184,10 @@ namespace MyCore.Services if (aqaraSwitch.Click == "single") { YeelightService yeelighService = new YeelightService(); - var devicesYeelight = yeelighService.GetDevices().Result; - var labLamp = devicesYeelight.Where(light => light.Hostname == "192.168.31.74").FirstOrDefault(); - if (labLamp != null) - yeelightService.Toggle(labLamp); + // var devicesYeelight = yeelighService.GetDevices().Result; + // var labLamp = devicesYeelight.Where(light => light.Hostname == "192.168.31.74").FirstOrDefault(); + //if (labLamp != null) { } + // yeelightService.Toggle(labLamp); } if (aqaraSwitch.Click == "double") diff --git a/MyCore/Services/Devices/SupportedDevices/MerossService.cs b/MyCore/Services/Devices/SupportedDevices/MerossService-OLD.cs similarity index 91% rename from MyCore/Services/Devices/SupportedDevices/MerossService.cs rename to MyCore/Services/Devices/SupportedDevices/MerossService-OLD.cs index 7c9e1af..9eb1746 100644 --- a/MyCore/Services/Devices/SupportedDevices/MerossService.cs +++ b/MyCore/Services/Devices/SupportedDevices/MerossService-OLD.cs @@ -1,5 +1,7 @@ -using MQTTnet; +using Mqtt.Client.AspNetCore.Services; +using MQTTnet; using MQTTnet.Client; +using MQTTnet.Client.Disconnecting; using MQTTnet.Client.Options; using MQTTnet.Formatter; using MQTTnet.Implementations; @@ -19,7 +21,7 @@ using static MyCore.Interfaces.Models.DeviceAbilities; namespace MyCore.Services { - public class MerossService + public class MerossService { // API HTTP private const string _merossUrl = "https://iot.meross.com"; @@ -39,7 +41,7 @@ namespace MyCore.Services private const int port = 2001; private const string domain = "iot.meross.com"; - private IMqttClient _client; + private static IMqttClient _client; private IMqttClientOptions _options; private string _userMQTT; private string _passwordMQTT; @@ -67,7 +69,7 @@ namespace MyCore.Services public MerossService(string username, string password) { - + try { // TODO @@ -94,7 +96,7 @@ namespace MyCore.Services }*/ //MQTT CONNEXION TODO Create a Connexion To MQTT method - // ConnexionToMqtt(); + ConnexionToMqtt(); } } catch (Exception e) @@ -104,6 +106,30 @@ namespace MyCore.Services } } + public async Task StartAsync(CancellationToken cancellationToken) + { + await _client.ConnectAsync(_options); + if (!_client.IsConnected) + { + await _client.ReconnectAsync(); + } + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + var disconnectOption = new MqttClientDisconnectOptions + { + ReasonCode = MqttClientDisconnectReason.NormalDisconnection, + ReasonString = "NormalDiconnection" + }; + await _client.DisconnectAsync(disconnectOption, cancellationToken); + } + await _client.DisconnectAsync(); + } + + public List GetMerossDevices() { // GET DEVICE LIST var deviceTask = Task.Run(() => PostURI(new Uri(_devList), RequestType.DeviceList)); @@ -278,7 +304,7 @@ namespace MyCore.Services } public string clientId = ""; - public string appId = ""; + public static string appId = ""; private void ConnexionToMqtt() { @@ -304,9 +330,11 @@ namespace MyCore.Services .WithProtocolVersion(MqttProtocolVersion.V311) .Build(); - _client.ConnectAsync(_options, CancellationToken.None).ContinueWith(res => { + _client.ConnectAsync(_options, CancellationToken.None).ContinueWith(res => + { if (res.Status == TaskStatus.RanToCompletion) { + ExecuteCommand("1912209125699190807048e1e9144656", Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 1); Console.WriteLine("It's connected"); } else @@ -417,7 +445,7 @@ namespace MyCore.Services clientId = $"app:{md5Result}"; } - private void ExecuteCommand(string uuid, Method method, CommandMqtt command, string payload = null, string callback = null, ToggleStatus toggleStatus = 0,int channelChosen = 0) + public 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 = ""; @@ -475,7 +503,7 @@ namespace MyCore.Services PublishMessage(topic, message); } - private string BuildMQTTMessage(Method method, string namespaceVar, string payloadVar) + private static string BuildMQTTMessage(Method method, string namespaceVar, string payloadVar) { HeaderMqtt headerMqtt = new HeaderMqtt(); @@ -503,7 +531,7 @@ namespace MyCore.Services return data; } - public void PublishMessage(string topic, string message) + public static void PublishMessage(string topic, string message) { var mqttMessage = new MqttApplicationMessageBuilder() .WithTopic(topic) diff --git a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs index 166eeae..7decad3 100644 --- a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs +++ b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs @@ -8,15 +8,15 @@ namespace MyCore.Services { public class YeelightService { - public List devices = new List(); + public static List devices = new List(); - public async Task> GetDevices() + public static async Task> GetDevices() { devices = await DeviceLocator.Discover(); return devices; } - public async Task Toggle(Device device) + public static async Task Toggle(Device device) { await device.Connect(); return await device.Toggle(); diff --git a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs index 6f5e53c..f55facc 100644 --- a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs @@ -28,7 +28,7 @@ namespace MyCore.Services.MyControlPanel return _Devices.Find(d => d.Id == id).FirstOrDefault(); } - public List GetByprovider(string providerId) + public List GetByProviderId(string providerId) { return _Devices.Find(d => d.ProviderId == providerId).ToList(); } diff --git a/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs index badcad1..cbb33d0 100644 --- a/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs @@ -38,6 +38,11 @@ namespace MyCore.Services.MyControlPanel return _Providers.Find(p => p.Name == name).FirstOrDefault(); } + public Provider GetByType(string type) + { + return _Providers.Find(p => p.Type == type).FirstOrDefault(); + } + public bool IsExist(string id) { return _Providers.Find(p => p.Id == id).FirstOrDefault() != null ? true : false; diff --git a/MyCore/Startup.cs b/MyCore/Startup.cs index 2c9e1e3..9c32241 100644 --- a/MyCore/Startup.cs +++ b/MyCore/Startup.cs @@ -35,6 +35,7 @@ using MQTTnet.AspNetCore.Extensions; using MyCore.Service.Extensions; using Mqtt.Client.AspNetCore.Services; using Mqtt.Client.AspNetCore.Settings; +using MyCore.Services.Devices; namespace MyCore { @@ -44,17 +45,7 @@ namespace MyCore { Configuration = configuration; - //MQTTService mQTTService = new MQTTService(); - - //MerossService merossService = new MerossService(); - - //ArloService arloService = new ArloService(); - - /*YeelightService yeelighService = new YeelightService(); - yeelighService.GetDevices();*/ - MapConfiguration(); - } public IConfiguration Configuration { get; } @@ -193,6 +184,13 @@ namespace MyCore }; }); + services.AddMqttClientHostedService(); // Todo client files (a lot are useless) + services.AddMqttClientOnlineHostedService(); + + services.AddMerossClientHostedService(); // Todo client files (a lot are useless) + + services.AddScoped(); // To clarify if needed.. ? + services.AddScoped(); services.AddScoped(typeof(ProfileLogic)); @@ -202,10 +200,8 @@ namespace MyCore services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); - - services.AddMqttClientHostedService(); // Todo client files (a lot are useless) - services.AddMqttClientOnlineHostedService(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/MyCore/appsettings.json b/MyCore/appsettings.json index dd5817a..83b4a2d 100644 --- a/MyCore/appsettings.json +++ b/MyCore/appsettings.json @@ -45,5 +45,16 @@ "Id": "5eb020f043ba8930506acbaa", "UserName": "thomas", "Password": "MyCore,1" + }, + + "BrokerMerossHostSettings": { + "Host": "iot.meross.com", + "Port": 2001 + }, + + "ClientMerossSettings": { + "Id": "5eb020f043ba8930506acbbb", + "UserName": "thomas", + "Password": "MyCore,1" } }