From fd1e6994daeced6096262eb34db32819fa0ed5d0 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 3 Mar 2021 18:14:17 +0100 Subject: [PATCH] MC add automation handler in action service + test with zigbee2mqtt devices --- MyCore.Interfaces/DTO/Common/DeviceType.cs | 7 + .../DTO/MyControlPanel/AutomationDTO.cs | 2 +- .../MyControlPanel/Database/Automation.cs | 21 +-- .../Models/MyControlPanel/SupportedDevice.cs | 1 + .../Providers/Zigbee/Aqara/AqaraCube.cs | 6 +- MyCore/Controllers/AutomationController.cs | 2 +- MyCore/Services/AutomationService.cs | 7 +- MyCore/Services/Devices/ActionService.cs | 124 ++++++++++++++++-- MyCore/Services/Devices/DeviceService.cs | 2 + MyCore/Services/Devices/MQTTService-OLD.cs | 4 +- .../Database/AutomationDatabaseService.cs | 4 +- .../Database/DeviceDatabaseService.cs | 5 + 12 files changed, 152 insertions(+), 33 deletions(-) diff --git a/MyCore.Interfaces/DTO/Common/DeviceType.cs b/MyCore.Interfaces/DTO/Common/DeviceType.cs index f78c791..e7ef430 100644 --- a/MyCore.Interfaces/DTO/Common/DeviceType.cs +++ b/MyCore.Interfaces/DTO/Common/DeviceType.cs @@ -22,4 +22,11 @@ namespace MyCore.Interfaces.DTO Gateway, Unknown } + + public enum DeviceAction // TO BE Continued + { + toggle = 1, + on, + off + } } diff --git a/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs index 6b4f7f0..e52105c 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs @@ -20,7 +20,7 @@ namespace MyCore.Interfaces.DTO public List Triggers { get; set; } public List Conditions { get; set; } public List Actions { get; set; } - public List DeviceIds { get; set; } + public List DevicesIds { get; set; } } public class AutomationCreateOrUpdateDetailDTO : AutomationDTO diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs index 1bf3154..aa46ade 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs @@ -48,6 +48,8 @@ namespace MyCore.Interfaces.Models Id = Id, Name = Name, UserId = UserId, + CreatedDate = CreatedDate, + UpdatedDate = UpdatedDate, //Triggers = Triggers //Conditions = Conditions //Actions = Actions @@ -63,9 +65,10 @@ namespace MyCore.Interfaces.Models UserId = UserId, CreatedDate = CreatedDate, UpdatedDate = UpdatedDate, - //Triggers = Triggers - //Conditions = Conditions - //Actions = Actions + Triggers = Triggers, + Conditions = Conditions, + Actions = Actions, + DevicesIds = DevicesIds }; } } @@ -74,8 +77,8 @@ namespace MyCore.Interfaces.Models { public string ProviderId { get; set; } public string DeviceId { get; set; } - public object StateName { get; set; } - public object StateValue { get; set; } + public string StateName { get; set; } + public string StateValue { get; set; } public enum Type { @@ -88,8 +91,8 @@ namespace MyCore.Interfaces.Models public class Condition { public string DeviceId { get; set; } - public object StateName { get; set; } - public object StateValue { get; set; } + public string StateName { get; set; } + public string StateValue { get; set; } public enum Type { @@ -101,8 +104,8 @@ namespace MyCore.Interfaces.Models public class Action { public string DeviceId { get; set; } - public object StateName { get; set; } // example : state - public object StateValue { get; set; } // example : ON + public string StateName { get; set; } // example : state + public string StateValue { get; set; } // example : ON public string ProviderId { get; set; } // TODO public string DeviceType { get; set; } // TODO diff --git a/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs b/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs index 784c343..dbad617 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/SupportedDevice.cs @@ -11,5 +11,6 @@ namespace MyCore.Interfaces.Models.MyControlPanel public string Model { get; set; } public string Description { get; set; } public DeviceType DeviceType { get; set; } + public List DeviceActions { get; set; } // TODO <= } } diff --git a/MyCore.Interfaces/Models/Providers/Zigbee/Aqara/AqaraCube.cs b/MyCore.Interfaces/Models/Providers/Zigbee/Aqara/AqaraCube.cs index ecf6b93..74402db 100644 --- a/MyCore.Interfaces/Models/Providers/Zigbee/Aqara/AqaraCube.cs +++ b/MyCore.Interfaces/Models/Providers/Zigbee/Aqara/AqaraCube.cs @@ -10,8 +10,8 @@ namespace MyCore.Interfaces.Models { public class AqaraCube : AqaraDevice { - public double Angle { get; set; } - public int Side { get; set; } - public string Action { get; set; } + public double angle { get; set; } + public int side { get; set; } + public string action { get; set; } } } \ No newline at end of file diff --git a/MyCore/Controllers/AutomationController.cs b/MyCore/Controllers/AutomationController.cs index b2179fc..ae48ab9 100644 --- a/MyCore/Controllers/AutomationController.cs +++ b/MyCore/Controllers/AutomationController.cs @@ -63,7 +63,7 @@ namespace MyCore.Service.Controllers /// /// automation id [ProducesResponseType(typeof(AutomationDetailDTO), 200)] - [HttpGet("detail/{roomId}")] + [HttpGet("detail/{automationId}")] public ObjectResult GetDetail(string automationId) { try diff --git a/MyCore/Services/AutomationService.cs b/MyCore/Services/AutomationService.cs index 7f7c47e..4855981 100644 --- a/MyCore/Services/AutomationService.cs +++ b/MyCore/Services/AutomationService.cs @@ -22,11 +22,16 @@ namespace MyCore.Service.Services automation.UserId = userId; automation.Name = automationCreateOrUpdateDetailDTO.Name; - automation.CreatedDate = DateTime.Now; + automation.CreatedDate = create ? DateTime.Now : automation.CreatedDate; automation.UpdatedDate = DateTime.Now; automation.Triggers = automationCreateOrUpdateDetailDTO.Triggers; automation.Conditions = automationCreateOrUpdateDetailDTO.Conditions; automation.Actions = automationCreateOrUpdateDetailDTO.Actions; + var allDeviceIds = new List(); + allDeviceIds.AddRange(automation.Triggers.Select(t => t.DeviceId)); + allDeviceIds.AddRange(automation.Conditions.Select(t => t.DeviceId)); + allDeviceIds.AddRange(automation.Actions.Select(t => t.DeviceId)); + automation.DevicesIds = allDeviceIds; if (create) return _AutomationDatabaseService.Create(automation).ToDTO(); diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 3b208cd..2e04bf9 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -1,4 +1,5 @@ using Mqtt.Client.AspNetCore.Services; +using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; using MyCore.Interfaces.Models.Providers.Zigbee.Aqara; using MyCore.Services.MyControlPanel; @@ -6,6 +7,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using static Mqtt.Client.AspNetCore.Services.MqttClientMerossService; @@ -15,8 +17,12 @@ namespace MyCore.Services.Devices { public static bool isOpen = false; public static long lastActionTime; + private static dynamic deserializedReceivedMessage; + private static dynamic triggerStateValue; + private static dynamic triggerStateName; + private static dynamic triggerStateValueCheck; // 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, AutomationDatabaseService _AutomationDatabaseService, string userId) + public static void HandleActionFromMQTTAsync(string topic, string message, DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, AutomationDatabaseService _AutomationDatabaseService, string userId) { // TODO Check if last action is not too close for each action var actionTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); @@ -26,9 +32,99 @@ namespace MyCore.Services.Devices switch (topicSplit[0]) { case "zigbee2mqtt": var test0 = _ProviderDatabaseService.GetByType(topicSplit[0]).Id; - var anotherTest = _AutomationDatabaseService.GetByProvider(test0); + var automations = _AutomationDatabaseService.GetByProvider(test0); + foreach (var automation in automations) { + // TODO filter incoming message, retrieve + var serviceName = topicSplit[1]; + var zigbeeDevice = _DeviceDatabaseService.GetByName(serviceName).FirstOrDefault(); + // todo check if not null and if more than one element + if (zigbeeDevice != null && automation.Triggers.Any(t => t.DeviceId == zigbeeDevice.Id)) { + var automationTrigger = automation.Triggers.Where(t => t.DeviceId == zigbeeDevice.Id).FirstOrDefault(); + + // Todo Deserialize by type + switch (zigbeeDevice.Model) + { + case "MFKZQ01LM": + deserializedReceivedMessage = JsonConvert.DeserializeObject(message); + break; + default: + break; + } + + Type type = deserializedReceivedMessage.GetType(); + PropertyInfo property = type.GetProperty(automationTrigger.StateName); + triggerStateValueCheck = property.GetValue(deserializedReceivedMessage); + triggerStateName = property.Name; + + // Todo check state name and value for triggers.. + if (automationTrigger.StateName == triggerStateName && automationTrigger.StateValue == triggerStateValueCheck) { + // Todo check condition + if (automation.Conditions.Count >= 0) + { + // => SEND REQUEST + foreach (var action in automation.Actions) + { + var stateName = action.StateName; + var stateValue = action.StateValue; + + var zigbeeDeviceAction = _DeviceDatabaseService.GetById(action.DeviceId); + + var providerAction = _ProviderDatabaseService.GetById(userId, zigbeeDeviceAction.ProviderId); + + switch (providerAction.Type) + { + case "zigbee2mqtt": + Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { }; + var actionRequest = ""; + + // Todo GET AND CHECK DEVICE ACTION POSSIBLE + // todo check state name (state, action.. ) + if (zigbeeDeviceAction.Type == DeviceType.Light) + { + if (stateValue == DeviceAction.toggle.ToString()) + { + // TO CHECK + switch (zigbeeDeviceAction.LastState) + { + case "ON": + actionRequest = "OFF"; + zigbee2MqttRequest.brightness = 0; + break; + case "OFF": + case null: + default: + actionRequest = "ON"; + zigbee2MqttRequest.brightness = 255; + break; + } + zigbeeDeviceAction.LastState = actionRequest; + zigbeeDeviceAction.LastStateDate = DateTime.Now; + } + } + + Type type2 = zigbee2MqttRequest.GetType(); + PropertyInfo property2 = type2.GetProperty(stateName); + property2.SetValue(zigbee2MqttRequest, actionRequest, null); + var request = JsonConvert.SerializeObject(zigbee2MqttRequest); + + MqttClientService.PublishMessage("zigbee2mqtt/" + zigbeeDeviceAction.Name.Substring(0, zigbeeDeviceAction.Name.Length-1) + "/set", request); + + // Save laststate + + DeviceService.CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, zigbeeDeviceAction.UserId, zigbeeDeviceAction.ToDTO(), false); + break; + case "meross": + + break; + } + //=> TODO SEND REQUEST + } + } + } + } + } // switch case according to device type (topic !) if (topicSplit[1].Contains("MagicCube0")) @@ -44,16 +140,16 @@ namespace MyCore.Services.Devices Task.Run(async () => { await YeelightService.Toggle(labLamp); }); } }*/ - if (test.Action == "shake") + /* if (test.action == "shake") { // TODO Check state Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF", brightness = 0 }; var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - MqttClientService.PublishMessage("zigbee2mqtt/LampeTable/set", request); - } - if (test.Action == "tap") + MqttClientService.PublishMessage("zigbee2mqtt/GU10Bureau/set", request); + }*/ + if (test.action == "tap") { var provider = providers.Where(p => p.Type == "meross").FirstOrDefault(); var merossDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); @@ -76,15 +172,15 @@ namespace MyCore.Services.Devices i++; }*/ MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 1); - MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 2); - MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 3); + /*MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 2); + MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.ON, 3);*/ //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(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 2); - MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 3); + /*MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 2); + MqttClientMerossService.ExecuteCommand(multiprise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 3);*/ //MqttClientMerossService.ExecuteCommand(prise.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", ToggleStatus.OFF, 0); } } @@ -110,10 +206,10 @@ namespace MyCore.Services.Devices MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request); } else { - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF" }; + /*Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF" }; var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request); + MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request);*/ } } if (topicSplit[1].Contains("Motion0")) @@ -128,10 +224,10 @@ namespace MyCore.Services.Devices } else { - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF", brightness = 0 }; + /*Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF", brightness = 0 }; var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - MqttClientService.PublishMessage("zigbee2mqtt/GU10Bureau/set", request); + MqttClientService.PublishMessage("zigbee2mqtt/GU10Bureau/set", request);*/ } } break; diff --git a/MyCore/Services/Devices/DeviceService.cs b/MyCore/Services/Devices/DeviceService.cs index a289141..8554014 100644 --- a/MyCore/Services/Devices/DeviceService.cs +++ b/MyCore/Services/Devices/DeviceService.cs @@ -79,6 +79,8 @@ namespace MyCore.Services.Devices device.LocationId = deviceDetailDTO.LocationId; device.CreatedDate = DateTime.Now; device.UpdatedDate = DateTime.Now; + device.LastState = deviceDetailDTO.LastState; + device.LastStateDate = deviceDetailDTO.LastStateDate; device.MeansOfCommunications = deviceDetailDTO.MeansOfCommunications; device.IpAddress = deviceDetailDTO.IpAddress; diff --git a/MyCore/Services/Devices/MQTTService-OLD.cs b/MyCore/Services/Devices/MQTTService-OLD.cs index e9005d4..7c7b1c2 100644 --- a/MyCore/Services/Devices/MQTTService-OLD.cs +++ b/MyCore/Services/Devices/MQTTService-OLD.cs @@ -144,12 +144,12 @@ namespace MyCore.Services try { var test = JsonConvert.DeserializeObject(payload); - if (test.Action == "shake") + if (test.action == "shake") { /*var labLamp = yeelightService.devices.Where(d => d.Hostname == "192.168.31.74").FirstOrDefault(); Task.Run(async () => { await yeelightService.Toggle(labLamp); });*/ } - if (test.Action == "slide") + if (test.action == "slide") { if (lightStateIkeaBulb == LightState.Undefined || lightStateIkeaBulb == LightState.Off) PublishMessage("zigbee2mqtt/0x14b457fffe7628fa/set", "{\"state\": \"ON\"}"); diff --git a/MyCore/Services/MyControlPanel/Database/AutomationDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/AutomationDatabaseService.cs index 191458a..0d18aa5 100644 --- a/MyCore/Services/MyControlPanel/Database/AutomationDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/AutomationDatabaseService.cs @@ -28,9 +28,9 @@ namespace MyCore.Services.MyControlPanel return _Automations.Find(a => a.Id == id).FirstOrDefault(); } - public Automation GetByProvider(string id) + public List GetByProvider(string id) { - return _Automations.Find(a => a.Triggers.Any(t => t.ProviderId == id)).FirstOrDefault(); + return _Automations.Find(a => a.Triggers.Any(t => t.ProviderId == id)).ToList(); } public bool IsExist(string id) diff --git a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs index f55facc..b6ee685 100644 --- a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs @@ -33,6 +33,11 @@ namespace MyCore.Services.MyControlPanel return _Devices.Find(d => d.ProviderId == providerId).ToList(); } + public List GetByName(string name) + { + return _Devices.Find(d => d.Name == name).ToList(); + } + public bool IsExist(string id) { return _Devices.Find(d => d.Id == id).FirstOrDefault() != null ? true : false;