diff --git a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs index a8390c1..d419859 100644 --- a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs +++ b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs @@ -45,10 +45,10 @@ namespace MyCore.Interfaces.Models public string model { get; set; } public bool supports_ota { get; set; } public string vendor { get; set; } - public List exposes { get; set; } + public List exposes { get; set; } } - public class Exposes { + public class Expose { public int access { get; set; } public string description { get; set; } public string name { get; set; } @@ -58,8 +58,9 @@ namespace MyCore.Interfaces.Models public List values { get; set; } // if enum - public bool value_on { get; set; } // if binary - public bool value_off { get; set; } // if binary + public object value_on { get; set; } // if binary + public object value_off { get; set; } // if binary + public object value_toggle { get; set; } // if binary public int value_max { get; set; } // if numeric public int value_min { get; set; } // if numeric @@ -76,9 +77,9 @@ namespace MyCore.Interfaces.Models public string property { get; set; } public string type { get; set; } // numeric, binary, enum, light - public string value_on { get; set; } // if binary - public string value_off { get; set; } // if binary - public string value_toggle { get; set; } // if binary + public object value_on { get; set; } // if binary + public object value_off { get; set; } // if binary + public object value_toggle { get; set; } // if binary public int value_max { get; set; } // if numeric public int value_min { get; set; } // if numeric diff --git a/MyCore/Controllers/GroupController.cs b/MyCore/Controllers/GroupController.cs index 858785f..e5f5732 100644 --- a/MyCore/Controllers/GroupController.cs +++ b/MyCore/Controllers/GroupController.cs @@ -76,6 +76,9 @@ namespace MyCore.Service.Controllers if (userId != null && groupId != null) { Group group = _GroupDatabaseService.GetById(groupId); + if (group == null) + throw new KeyNotFoundException("Group not found"); + List devices = _DeviceDatabaseService.GetByLocation(group.UserId, groupId); return new OkObjectResult(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); @@ -85,6 +88,10 @@ namespace MyCore.Service.Controllers return new ObjectResult("Invalid parameters") { StatusCode = 400 }; } } + catch (KeyNotFoundException ex) + { + return new BadRequestObjectResult(ex.Message) { StatusCode = 404 }; + } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; diff --git a/MyCore/Services/AutomationService.cs b/MyCore/Services/AutomationService.cs index 4855981..7b29b7d 100644 --- a/MyCore/Services/AutomationService.cs +++ b/MyCore/Services/AutomationService.cs @@ -28,9 +28,19 @@ namespace MyCore.Service.Services 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)); + allDeviceIds.AddRange(automation.Triggers.Select(t => t.DeviceId)); // Only take the deviceIds from triggers + /*allDeviceIds.AddRange(automation.Conditions.Select(t => t.DeviceId)); + + foreach(var action in automation.Actions) { + if (action.DeviceId != null) { + allDeviceIds.Add(action.DeviceId); + } + if (action.GroupId != null) + { + + } + }*/ + automation.DevicesIds = allDeviceIds; if (create) diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 8ac0928..9912a4d 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -18,9 +18,9 @@ namespace MyCore.Services.Devices { public static bool isOpen = false; public static long lastActionTime; - private static dynamic deserializedReceivedMessage; - private static dynamic triggerStateName; - private static dynamic triggerStateValueCheck; + //private static dynamic deserializedReceivedMessage; + //private static dynamic triggerStateName; + //private static dynamic triggerStateValueCheck; public static void HandleActionFromMQTTAsync(string topic, string message, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, AutomationDatabaseService _AutomationDatabaseService, string userId) { @@ -48,6 +48,7 @@ namespace MyCore.Services.Devices try { + /* // Todo Deserialize by type switch (zigbeeDevice.Type) { @@ -68,14 +69,11 @@ namespace MyCore.Services.Devices break; default: break; - } + }*/ // if is not a set => check automation if (topicSplit.Length <= 2) { - zigbeeDevice.LastStateDate = DateTime.Now; - _DeviceDatabaseService.Update(zigbeeDevice); - foreach (var automation in automations) { @@ -85,13 +83,31 @@ namespace MyCore.Services.Devices System.Console.WriteLine($"Open automation {automation.Name}"); var automationTrigger = automation.Triggers.Where(t => t.DeviceId == zigbeeDevice.Id).FirstOrDefault(); - System.Type type = deserializedReceivedMessage.GetType(); - PropertyInfo property = type.GetProperty(automationTrigger.StateName); - triggerStateValueCheck = property.GetValue(deserializedReceivedMessage); - triggerStateName = property.Name; + List exposes = GetDeviceExposes(zigbeeDevice); - if (automationTrigger.StateName == triggerStateName && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) + 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) { + validTrigger = false; + } + + + if (validTrigger && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) + { + // Correct trigger ! + // Todo check condition if (automation.Conditions.Count <= 0) { @@ -136,88 +152,144 @@ namespace MyCore.Services.Devices var providerAction = _ProviderDatabaseService.GetById(userId, action.ProviderId); - switch (providerAction.Type) + // Check if device exist + if (actionDeviceToTest != null) { - case "zigbee2mqtt": - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { }; - var actionRequest = ""; + switch (providerAction.Type) + { + case "zigbee2mqtt": + //Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { }; + var request = ""; - // If the state is already good, do not send the request. (in order to not spam mqtt broker) - bool alreadyInAskedState = false; + // If the state is already good, do not send the request. (in order to not spam mqtt broker) + bool alreadyInAskedState = false; - System.Console.WriteLine($"zigbee2mqtt type !"); + System.Console.WriteLine($"zigbee2mqtt type !"); - // TODO: GET AND CHECK DEVICE ACTION POSSIBILITIES - // TODO: Check state name (state, action.. ) - System.Console.WriteLine($"actionDeviceToTest.Type {actionDeviceToTest.Type}"); - // TODO: Check if switch correct and if we can toggle => Same for Plug etc (depend on request) - // TODO: Check if device has brightness possibility.. - if (actionDeviceToTest.Type == DeviceType.Light || actionDeviceToTest.Type == DeviceType.Switch) - { - var deserializedLastState = JsonConvert.DeserializeObject(actionDeviceToTest.LastState); + var actionDeviceExpose = GetDeviceExposes(actionDeviceToTest); + bool validAction = false; - if (stateValue == DeviceAction.toggle.ToString()) + if (actionDeviceToTest.Type == DeviceType.Light || actionDeviceToTest.Type == DeviceType.Switch) { - System.Console.WriteLine($"Toggle action"); - // TO CHECK - switch (deserializedLastState.state) + var typeToTest = actionDeviceToTest.Type == DeviceType.Light ? "light" : "switch"; + var exposeToTest = actionDeviceExpose.Where(e => e.type == typeToTest).FirstOrDefault(); + validAction = exposeToTest != null; + } + else { + // Retrieve exposes for actionDevice + var exposeToTest = actionDeviceExpose.Where(e => e.name == action.StateName).FirstOrDefault(); + validAction = exposeToTest != null; + } + + if (validAction) + { + + var dic = new Dictionary(); + if (actionDeviceToTest.LastState != null) { + dic = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); + } + + // TODO: Check if switch correct and if we can toggle => Same for Plug etc (depend on request) + // TODO: Check if device has brightness possibility.. + bool isBrightness = false; + + if (actionDeviceToTest.Type == DeviceType.Light || actionDeviceToTest.Type == DeviceType.Switch) { - case "ON": - actionRequest = "OFF"; - zigbee2MqttRequest.brightness = 0; - break; - case "OFF": - case null: - default: - actionRequest = "ON"; - zigbee2MqttRequest.brightness = 255; - break; + + var requestBefore = new Dictionary(); + requestBefore.Add(action.StateName, action.StateValue); + + request = JsonConvert.SerializeObject(requestBefore); + + if (dic.Count > 0) { + if (dic["state"].ToString().ToLower() == stateValue.ToLower()) + { + alreadyInAskedState = true; + } + } + + /*if (stateValue == DeviceAction.toggle.ToString()) + { + System.Console.WriteLine($"Toggle action"); + // TODO => Check exposes device etc + switch (dic["state"].ToLower()) + { + case "on": + actionRequest = "off"; + + zigbee2MqttRequest.brightness = 0; + break; + case "off": + case null: + default: + actionRequest = "on"; + zigbee2MqttRequest.brightness = 255; + break; + } + } + else if (stateValue.ToLower() == DeviceAction.on.ToString().ToLower()) + { + // retrieve device state to check if already in asked state + if (dic["state"].ToLower() == stateValue.ToLower()) + alreadyInAskedState = true; + else + { + actionRequest = "on"; + zigbee2MqttRequest.brightness = 255; + } + } + else + { + // DeviceAction.off.ToString() => Need to ad validation ? + + // retrieve device state to check if already in asked state + if (dic["state"].ToLower() == stateValue.ToLower()) + alreadyInAskedState = true; + else + { + actionRequest = "off"; + zigbee2MqttRequest.brightness = 0; + } + }*/ + } + + if (!alreadyInAskedState) + { + /*System.Console.WriteLine($"Before retrieving type etc {zigbee2MqttRequest.state}"); + + System.Type type2 = zigbee2MqttRequest.GetType(); + PropertyInfo property2 = type2.GetProperty(stateName); + property2.SetValue(zigbee2MqttRequest, actionRequest, null); + var request = JsonConvert.SerializeObject(zigbee2MqttRequest);*/ + + var requestToSend = $"Send request ! zigbee2mqtt/{actionName}/set/{request}"; + System.Console.WriteLine($"Send request ! zigbee2mqtt/{actionName}/set/{request}"); + + MqttClientService.PublishMessage("zigbee2mqtt/" + actionName + "/set", request); } } - else if (stateValue.ToLower() == DeviceAction.on.ToString().ToLower()) { - // retrieve device state to check if already in asked state - if (deserializedLastState.state.ToLower() == stateValue.ToLower()) - alreadyInAskedState = true; - else { - actionRequest = "ON"; - zigbee2MqttRequest.brightness = 255; - } - } else { - // DeviceAction.off.ToString() => Need to ad validation ? - - // retrieve device state to check if already in asked state - if (deserializedLastState.state.ToLower() == stateValue.ToLower()) - alreadyInAskedState = true; - else - { - actionRequest = "OFF"; - zigbee2MqttRequest.brightness = 0; - } + else { + // No expose found.. => the device has not the correct state => not correct automation + System.Console.WriteLine($"Invalid action - the request was not sent"); } - } - - if (!alreadyInAskedState) { - System.Console.WriteLine($"Before retrieving type etc {zigbee2MqttRequest.state}"); - - System.Type type2 = zigbee2MqttRequest.GetType(); - PropertyInfo property2 = type2.GetProperty(stateName); - property2.SetValue(zigbee2MqttRequest, actionRequest, null); - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - - System.Console.WriteLine($"Send request ! zigbee2mqtt/{actionName}/set/{request}"); - - MqttClientService.PublishMessage("zigbee2mqtt/" + actionName + "/set", request); - } - break; - case "meross": - System.Console.WriteLine($"meross type !"); - break; + break; + case "meross": + System.Console.WriteLine($"meross type !"); + break; + } + } + else { + System.Console.WriteLine($"Device or group found in action incorrect"); } } } } } } + + zigbeeDevice.LastStateDate = DateTime.Now; + zigbeeDevice.LastState = message; + _DeviceDatabaseService.Update(zigbeeDevice); } else { @@ -301,6 +373,59 @@ namespace MyCore.Services.Devices } } + private static List GetDeviceExposes(Device device) + { + List exposes = new List(); + // Get Exposes for the zigbee device + foreach (var supportedOperation in device.SupportedOperations) + { + exposes.Add(JsonConvert.DeserializeObject(supportedOperation)); + } + return exposes; + } + + private static System.Type GetStateType(Expose expose) + { + switch (expose.type) + { + // e.g.: occupancy + case "binary": + if (expose.value_toggle == null) + { + // value are boolean + return true.GetType(); + } + else + { + // value are string + return "".GetType(); + } + case "numeric": + return new long().GetType(); + case "enum": + return expose.values.GetType(); + break; + /*case "text": + break; + case "light": + break; + case "composite": + break; + case "switch": + break;*/ + /*case "fan": + break; + case "cover": + break; + case "lock": + break; + case "climate": + break;*/ + default: + return null; + } + } + public static async Task UpdateZigbee2MqttConfigAsync(string topic, string message, string userId, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService) { // update zigbee2mqqtt config switch (topic) diff --git a/MyCore/Services/GroupService.cs b/MyCore/Services/GroupService.cs index 4a33345..10993c8 100644 --- a/MyCore/Services/GroupService.cs +++ b/MyCore/Services/GroupService.cs @@ -55,41 +55,63 @@ namespace MyCore.Service.Services { List groups = new List(); + // Get zigbee groups + List existingGroups = _GroupDatabaseService.GetByType(userId, "zigbee2mqtt"); + foreach (var zigbee2MqttGroup in zigbee2MqttGroups.Where(z => z.members.Count > 0)) // Only take group with members { - List devices = new List(); - Group group; - - group = new Group(); - group.CreatedDate = DateTime.Now; - group.DevicesIds = new List(); - group.ServiceIdentification = zigbee2MqttGroup.id; - group.UserId = userId; - group.Name = zigbee2MqttGroup.friendly_name; - group.Type = "zigbee2mqtt"; - group.UpdatedDate = DateTime.Now; - - foreach (var member in zigbee2MqttGroup.members) + bool create = true; + if (existingGroups.Any(eg => eg.ServiceIdentification == zigbee2MqttGroup.id)) { - Device device = _DeviceDatabaseService.GetByServiceIdentification(member.ieee_address); - if (device != null) { - group.DevicesIds.Add(device.Id); + var existingGroup = existingGroups.Where(eg => eg.ServiceIdentification == zigbee2MqttGroup.id).FirstOrDefault(); + + GroupCreateOrUpdateDetailDTO groupToUpdate = new GroupCreateOrUpdateDetailDTO(); + groupToUpdate.UserId = existingGroup.UserId; + groupToUpdate.Name = existingGroup.Name; + groupToUpdate.Id = existingGroup.Id; + groupToUpdate.IsAlarm = existingGroup.IsAlarm; + groupToUpdate.DeviceIds = existingGroup.DevicesIds; + // Hard refresh + CreateOrUpdate(_GroupDatabaseService, _DeviceDatabaseService, userId, groupToUpdate, false); + create = false; + } + + if (create) { + List devices = new List(); + Group group; + + group = new Group(); + group.CreatedDate = DateTime.Now; + group.DevicesIds = new List(); + group.ServiceIdentification = zigbee2MqttGroup.id; + group.UserId = userId; + group.Name = zigbee2MqttGroup.friendly_name; + group.Type = "zigbee2mqtt"; + group.UpdatedDate = DateTime.Now; + + foreach (var member in zigbee2MqttGroup.members) + { + Device device = _DeviceDatabaseService.GetByServiceIdentification(member.ieee_address); + if (device != null) + { + group.DevicesIds.Add(device.Id); + } } - } - - group = _GroupDatabaseService.Create(group); - foreach (var deviceId in group.DevicesIds) - { - Device device = _DeviceDatabaseService.GetById(deviceId); - devices.Add(device); - if (device.GroupIds == null) - device.GroupIds = new List(); - device.GroupIds.Add(group.Id); - _DeviceDatabaseService.Update(device); - } + group = _GroupDatabaseService.Create(group); - groups.Add(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); + foreach (var deviceId in group.DevicesIds) + { + Device device = _DeviceDatabaseService.GetById(deviceId); + devices.Add(device); + if (device.GroupIds == null) + device.GroupIds = new List(); + device.GroupIds.Add(group.Id); + _DeviceDatabaseService.Update(device); + } + + groups.Add(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); + } } return groups;