diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs index a6d0e6e..e6109db 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs @@ -79,26 +79,40 @@ namespace MyCore.Interfaces.Models public string DeviceId { get; set; } public string StateName { get; set; } public string StateValue { get; set; } + public TriggerType type { get; set; } + } - public enum Type - { - MQTT, - WEB, - TIME - } + public enum TriggerType + { + MQTT, + WEB, + TIME } public class Condition { public string DeviceId { get; set; } - public string StateName { get; set; } - public string StateValue { get; set; } + public State State { get; set; } + public string StartTime { get; set; } // TIMESPAN JSON SERIALIZED + public string EndTime { get; set; } // TIMESPAN JSON SERIALIZED + public ConditionType Type { get; set; } + public ConditionValue Value { get; set; } + } - public enum Type - { - STATE, - HOUR - } + public enum ConditionType + { + STATE, + TIME + } + + public enum ConditionValue + { + EQUAL, // == + NOT_EQUAL, // != + BIGGER, // > + BIGGEST, // >= + SMALLER, // < + SMALLEST, // <= } public class Action @@ -108,7 +122,7 @@ namespace MyCore.Interfaces.Models public List States { get; set; } public string RawRequest { get; set; } // http, mqtt public string ProviderId { get; set; } - public Type Type { get; set; } + public ActionType Type { get; set; } } public class State @@ -117,7 +131,7 @@ namespace MyCore.Interfaces.Models public string Value { get; set; } // example : ON } - public enum Type + public enum ActionType { DELAY, DEVICE, diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index cfe9698..3d527d0 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -15,96 +15,122 @@ namespace MyCore.Services.Devices { public class ActionService { + private static Provider currentProvider; + private static Device deviceTrigger; + private static string providerFromTopic; + private static string deviceServiceName; public static async Task HandleActionFromMQTTAsync(string topic, string message, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, AutomationDatabaseService _AutomationDatabaseService, string userId) { - var actionTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - var providers = _ProviderDatabaseService.GetAll(userId); - string[] topicSplit = topic.Split('/'); - - var providerFromTopic = topicSplit[0]; - - // TODO Sortir automation d'un type specific.. et créer un objet ? - System.Console.WriteLine($"Received message {message}"); + + string[] topicSplit = topic.Split('/'); + try { + providerFromTopic = topicSplit[0]; + deviceServiceName = topicSplit[1]; + } catch (Exception ex) { + System.Console.WriteLine($"Exeption throw when fetching provider and device from topic {topic} - Error: {ex}"); + } + switch (providerFromTopic) { case "zigbee2mqtt": UpdateZigbee2MqttConfigAsync(topic, message, userId, _DeviceDatabaseService, _GroupDatabaseService, _ProviderDatabaseService, _LocationDatabaseService); + break; + default: + break; + } - var provider = _ProviderDatabaseService.GetByType(topicSplit[0]).Id; - var automations = _AutomationDatabaseService.GetByProvider(provider); + currentProvider = _ProviderDatabaseService.GetByType(providerFromTopic); - var serviceName = topicSplit[1]; - var zigbeeDevice = _DeviceDatabaseService.GetByName(serviceName).FirstOrDefault(); + if (currentProvider != null) + { + var automations = _AutomationDatabaseService.GetByProvider(currentProvider.Id); - if (zigbeeDevice != null) { - try + deviceTrigger = _DeviceDatabaseService.GetByName(deviceServiceName).FirstOrDefault(); + + if (deviceTrigger != null) + { + try + { + // if is not a set => check automation + if (topicSplit.Length <= 2) { - - /* - // Todo Deserialize by type - switch (zigbeeDevice.Type) + foreach (var automation in automations) { - case DeviceType.Switch: - deserializedReceivedMessage = JsonConvert.DeserializeObject(message); - zigbeeDevice.LastState = message; - break; - case DeviceType.Light: - deserializedReceivedMessage = JsonConvert.DeserializeObject(message); - zigbeeDevice.LastState = message; - break; - case DeviceType.Motion: - deserializedReceivedMessage = JsonConvert.DeserializeObject(message); - zigbeeDevice.LastState = message; - break; - case DeviceType.Plug: - zigbeeDevice.LastState = message; - break; - default: - break; - }*/ - - // if is not a set => check automation - if (topicSplit.Length <= 2) - { - foreach (var automation in automations) + // todo check if not null and if more than one element + if (deviceTrigger != null && automation.Triggers.Any(t => t.DeviceId == deviceTrigger.Id)) { + var automationTriggers = automation.Triggers.Where(t => t.DeviceId == deviceTrigger.Id).ToList(); - // todo check if not null and if more than one element - if (zigbeeDevice != null && automation.Triggers.Any(t => t.DeviceId == zigbeeDevice.Id)) + // Test for each automation trigger found + foreach (var automationTrigger in automationTriggers) { - System.Console.WriteLine($"Open automation {automation.Name}"); - var automationTrigger = automation.Triggers.Where(t => t.DeviceId == zigbeeDevice.Id).FirstOrDefault(); + List exposes = GetDeviceExposes(deviceTrigger); - List exposes = GetDeviceExposes(zigbeeDevice); + var triggerStateValueCheck = (object)null; - var triggerStateValueCheck = (object) null; - // Try to get automationTrigger.StateName of zigbee device var expose = exposes.Where(e => e.name == automationTrigger.StateName).FirstOrDefault(); bool validTrigger = expose != null; // No expose found.. => the device has not the correct state => not correct automation - //System.Type typeToCheck = GetStateType(expose); - try { // Try to get specific field from message var dic = JsonConvert.DeserializeObject>(message); triggerStateValueCheck = dic[automationTrigger.StateName]; // if action slide => get slide } - catch (Exception ex) { + catch (Exception ex) + { validTrigger = false; } - + if (validTrigger && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) { // Correct trigger ! + System.Console.WriteLine($"Correct trigger for automation {automation.Name}"); - // Todo check condition - if (automation.Conditions.Count <= 0) + var isConditionsRespected = automation.Conditions.Count <= 0 ? new List() { true } : new List(new bool[automation.Conditions.Count]); + var i = 0; + foreach (var condition in automation.Conditions) { - System.Console.WriteLine("None conditions"); + switch (condition.Type) + { + case ConditionType.STATE: + break; + case ConditionType.TIME: + TimeSpan now = DateTime.Now.TimeOfDay; + + var test = JsonConvert.SerializeObject(now); + + TimeSpan start = JsonConvert.DeserializeObject(condition.StartTime); + TimeSpan end = JsonConvert.DeserializeObject(condition.EndTime); + + // => Test if ok with night ! + + if (start <= end) + { + // start and stop times are in the same day + if (now >= start && now <= end) + { + isConditionsRespected[i] = true; + } + } + else + { + // start and stop times are in different days + if (now >= start || now <= end) + { + isConditionsRespected[i] = true; + } + } + break; + } + i++; + } + + if (!isConditionsRespected.Any(cr => !cr)) + { + System.Console.WriteLine("Conditions respected !"); foreach (var action in automation.Actions) { @@ -112,8 +138,9 @@ namespace MyCore.Services.Devices Device actionDeviceToTest = new Device(); // Retrieve action type - switch (action.Type) { - case Interfaces.Models.Type.DEVICE: + switch (action.Type) + { + case ActionType.DEVICE: var zigbeeDeviceAction = _DeviceDatabaseService.GetById(action.DeviceId); var providerActionTest = _ProviderDatabaseService.GetById(userId, action.ProviderId); @@ -122,7 +149,7 @@ namespace MyCore.Services.Devices System.Console.WriteLine($"We get a zigbeeDeviceAction ! Name={zigbeeDeviceAction.Name} Type={zigbeeDeviceAction.Type}"); System.Console.WriteLine($"Check action provider type ! Type={providerActionTest.Type} Name={providerActionTest.Name}"); break; - case Interfaces.Models.Type.GROUP: + case ActionType.GROUP: var zigbeeGroupAction = _GroupDatabaseService.GetById(action.GroupId); DeviceNameForAction = zigbeeGroupAction.Name; @@ -132,12 +159,12 @@ namespace MyCore.Services.Devices // Check state of first device of a group actionDeviceToTest = _DeviceDatabaseService.GetByGroup(zigbeeGroupAction.Id).FirstOrDefault(); // TODO : Send error if no device found ! break; - case Interfaces.Models.Type.MQTT: // Correct way ? - // TODO - //requestType = Interfaces.Models.Type.MQTT; + case ActionType.MQTT: // Correct way ? + // TODO + //requestType = Interfaces.Models.Type.MQTT; break; - case Interfaces.Models.Type.HTTP: // Correct way ? - // TODO + case ActionType.HTTP: // Correct way ? + // TODO break; } @@ -163,9 +190,11 @@ namespace MyCore.Services.Devices dic = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); } - try { + try + { - switch (actionDeviceToTest.Type) { + switch (actionDeviceToTest.Type) + { case DeviceType.Light: case DeviceType.Switch: var actionDeviceExposeForFeatures = actionDeviceExpose.Where(e => e.type == ((DeviceType)actionDeviceToTest.Type).ToString().ToLower()).FirstOrDefault(); @@ -187,22 +216,25 @@ namespace MyCore.Services.Devices // TODO clean this if (state.Name == "brightness") { - try + try { int brightness = int.Parse(state.Value); // Add to request buildRequest.Add(state.Name, brightness); - } catch (Exception ex) + } + catch (Exception ex) { System.Console.WriteLine($"zigbee - Parse to int error {ex}"); } } - else { + else + { // Add to request buildRequest.Add(state.Name, state.Value); } } - else { + else + { throw new Exception($"Action device light|switch does not have expose of type {state.Name}"); } } @@ -214,7 +246,8 @@ namespace MyCore.Services.Devices foreach (var state in action.States) { System.Console.WriteLine($"Check Action ! {state.Name} {state.Value}"); - if (!actionDeviceExpose.Any(ade => ade.name == state.Name)) { + if (!actionDeviceExpose.Any(ade => ade.name == state.Name)) + { throw new Exception($"Action device does not have expose of type {state.Name}"); } @@ -230,13 +263,15 @@ namespace MyCore.Services.Devices MqttClientService.PublishMessage("zigbee2mqtt/" + DeviceNameForAction + "/set", request); - } catch (Exception ex) { + } + catch (Exception ex) + { System.Console.WriteLine($"Not a valid action !"); } break; case "meross": - try + try { var merossDevice = _DeviceDatabaseService.GetById(actionDeviceToTest.Id); @@ -252,6 +287,7 @@ namespace MyCore.Services.Devices var plugIds = JsonConvert.DeserializeObject>(channels.Value); List lastStatePlug = new List(); // Off by default + // => TODO GET ALL POSSIBILITIES FROM DEVICE (number of channels) if (actionDeviceToTest.LastState != null) // Get last state of device { @@ -264,7 +300,8 @@ namespace MyCore.Services.Devices System.Console.WriteLine($"Meross error: Last state ! - {ex}"); } } - else { + else + { // By default set to OFF foreach (var plug in plugIds) { @@ -273,7 +310,7 @@ namespace MyCore.Services.Devices } List statuses = new List(); - var i = 0; + var p = 0; foreach (var plugId in plugIds) { ToggleStatus status = new ToggleStatus(); @@ -284,7 +321,7 @@ namespace MyCore.Services.Devices case DeviceType.Plug: if (state.Value.ToLower() == "toggle") { - status = lastStatePlug[i] == ToggleStatus.OFF ? ToggleStatus.ON : ToggleStatus.OFF; + status = lastStatePlug[p] == ToggleStatus.OFF ? ToggleStatus.ON : ToggleStatus.OFF; } else { @@ -295,7 +332,7 @@ namespace MyCore.Services.Devices // Send request MqttClientMerossService.ExecuteCommand(actionDeviceToTest.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", status, plugId); statuses.Add(status); - i++; + p++; } // Update Last state @@ -303,12 +340,14 @@ namespace MyCore.Services.Devices actionDeviceToTest.LastStateDate = DateTime.Now; _DeviceDatabaseService.Update(actionDeviceToTest); } - else { + else + { throw new Exception("Meross: Request action invalid"); - } + } // TODO UPDATE STATE } - } catch (Exception ex) + } + catch (Exception ex) { System.Console.WriteLine($"Meross error: {ex}"); } @@ -329,29 +368,42 @@ namespace MyCore.Services.Devices break; } } - else { + else + { System.Console.WriteLine($"Device or group found in action incorrect"); } } } + else + { + System.Console.WriteLine($"One or more condition aren't respected"); + } } } } - - zigbeeDevice.LastStateDate = DateTime.Now; - zigbeeDevice.LastState = message; - _DeviceDatabaseService.Update(zigbeeDevice); - } - else - { - // do nothing is a set } + // Update last state of devices + deviceTrigger.LastStateDate = DateTime.Now; + deviceTrigger.LastState = message; + _DeviceDatabaseService.Update(deviceTrigger); + } + else + { + // do nothing is a set } - catch (Exception ex) { } } - break; - default: - break; + catch (Exception ex) + { + System.Console.WriteLine($"Exeption from automation logic - {ex}"); + } + } + else + { + System.Console.WriteLine($"Current device Trigger not found - {deviceTrigger}"); + } + } + else { + System.Console.WriteLine($"Current provider not found - {providerFromTopic}"); } }