diff --git a/MyCore.Interfaces/DTO/MyControlPanel/GroupDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/GroupDTO.cs index 06519f7..aa5c878 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/GroupDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/GroupDTO.cs @@ -5,10 +5,29 @@ using System.Threading.Tasks; namespace MyCore.Interfaces.DTO { - public class GroupDTO + public class GroupSummaryDTO { public string Id { get; set; } + + public string UserId { get; set; } + public string Name { get; set; } - public List Devices { get; set; } + + public string Type { get; set; } + public bool IsAlarm { get; set; } + } + + public class GroupDetailDTO : GroupSummaryDTO + { + public DateTime CreatedDate { get; set; } + + public DateTime UpdatedDate { get; set; } + + public List Devices { get; set; } + } + + public class GroupCreateOrUpdateDetailDTO : GroupSummaryDTO + { + public List DeviceIds { get; set; } } } diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs index aa46ade..42df88c 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Automation.cs @@ -103,16 +103,22 @@ namespace MyCore.Interfaces.Models public class Action { + public string GroupId { get; set; } public string DeviceId { get; set; } 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 + public string RawRequest { get; set; } // http, mqtt + public string ProviderId { get; set; } + public Type Type { get; set; } + } - public enum Type - { - DELAY, - DEVICE - } + public enum Type + { + DELAY, + DEVICE, + HTTP, + ZIGBEE2MQTT, + MQTT, + GROUP } } diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs index 4245c25..cd1b65b 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs @@ -111,8 +111,7 @@ namespace MyCore.Interfaces.Models Status = Status, ConnectionStatus = ConnectionStatus, ProviderId = ProviderId, - LocationId = LocationId, // Check if correct way - // Location = + LocationId = LocationId, // Include room id LastStateDate = LastStateDate, Battery = Battery, BatteryStatus = BatteryStatus @@ -132,8 +131,7 @@ namespace MyCore.Interfaces.Models Port = Port, FirmwareVersion = FirmwareVersion, ConnectionStatus = ConnectionStatus, - LocationId = LocationId, // Check if correct way - // Location = + LocationId = LocationId, // Include room id MeansOfCommunications = MeansOfCommunications, CreatedDate = CreatedDate, UpdatedDate = UpdatedDate, diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Group.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Group.cs index a1ed510..e1eb4af 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Group.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Group.cs @@ -14,20 +14,60 @@ namespace MyCore.Interfaces.Models [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } + [BsonElement("UserId")] + [BsonRequired] + public string UserId { get; set; } + [BsonElement("Name")] [BsonRequired] public string Name { get; set; } - [BsonElement("Devices")] - public List Devices { get; set; } + [BsonElement("Type")] + [BsonRequired] + public string Type { get; set; } // zigbee2mqtt specific => generated from zigbee groups config + // TODO specific group = Alarm, sound etc ? Useless if we check device type during creation ? - public GroupDTO ToDTO() + [BsonElement("ServiceIdentification")] // For zigbee2mqtt + [BsonRequired] + public int ServiceIdentification { get; set; } + + [BsonElement("IsAlarm")] + [BsonRequired] + public bool IsAlarm { get; set; } // For specific group = Alarm + + [BsonElement("CreatedDate")] + public DateTime CreatedDate { get; set; } + + [BsonElement("UpdatedDate")] + public DateTime UpdatedDate { get; set; } + + [BsonElement("DevicesIds")] + public List DevicesIds { get; set; } + + public GroupSummaryDTO ToSummaryDTO() { - return new GroupDTO() + return new GroupSummaryDTO() { Id = Id, + UserId = UserId, Name = Name, - Devices = Devices + Type = Type, + IsAlarm = IsAlarm + }; + } + + public GroupDetailDTO ToDTO(List devicesDetail) + { + return new GroupDetailDTO() + { + Id = Id, + UserId = UserId, + Name = Name, + Type = Type, + IsAlarm = IsAlarm, + CreatedDate = CreatedDate, + UpdatedDate = UpdatedDate, + Devices = devicesDetail }; } } diff --git a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttGroup.cs b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttGroup.cs new file mode 100644 index 0000000..b7fd9b2 --- /dev/null +++ b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttGroup.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MyCore.Interfaces.Models +{ + public class Zigbee2MqttGroup + { + public string friendly_name { get; set; } + public int id { get; set; } + public List members { get; set; } + } + + public class Member + { + public int endpoint { get; set; } + public string ieee_address { get; set; } + } +} diff --git a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2MqttRequest.cs b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttRequest.cs similarity index 100% rename from MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2MqttRequest.cs rename to MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttRequest.cs diff --git a/MyCore/Controllers/AuthenticationController.cs b/MyCore/Controllers/AuthenticationController.cs index d54e8c1..1b95f26 100644 --- a/MyCore/Controllers/AuthenticationController.cs +++ b/MyCore/Controllers/AuthenticationController.cs @@ -29,6 +29,7 @@ namespace MyCore.Service.Controllers private readonly ILogger _logger; private readonly TokensService _tokensService; private readonly DeviceDatabaseService _DeviceDatabaseService; + private readonly GroupDatabaseService _GroupDatabaseService; private readonly ProviderDatabaseService _ProviderDatabaseService; private readonly LocationDatabaseService _LocationDatabaseService; private readonly UserDatabaseService _UserDatabaseService; @@ -37,11 +38,12 @@ namespace MyCore.Service.Controllers private readonly IMqttClientService _mqttClientService; //private readonly IMqttOnlineClientService _mqttOnlineClientService; - public AuthenticationController(ILogger logger, TokensService tokensService, DeviceDatabaseService DeviceDatabaseService, ProviderDatabaseService ProviderDatabaseService, LocationDatabaseService LocationDatabaseService, UserDatabaseService UserDatabaseService, ActionService ActionService, AutomationDatabaseService AutomationDatabaseService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) + public AuthenticationController(ILogger logger, TokensService tokensService, DeviceDatabaseService DeviceDatabaseService, GroupDatabaseService GroupDatabaseService, ProviderDatabaseService ProviderDatabaseService, LocationDatabaseService LocationDatabaseService, UserDatabaseService UserDatabaseService, ActionService ActionService, AutomationDatabaseService AutomationDatabaseService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) { _logger = logger; _tokensService = tokensService; _DeviceDatabaseService = DeviceDatabaseService; + _GroupDatabaseService = GroupDatabaseService; _ProviderDatabaseService = ProviderDatabaseService; _LocationDatabaseService = LocationDatabaseService; _UserDatabaseService = UserDatabaseService; @@ -55,6 +57,9 @@ namespace MyCore.Service.Controllers { try { + // For TEST + email = "test@email.be"; + password = "kljqsdkljqsd"; var token = _tokensService.Authenticate(email.ToLower(), password); // Set user token ? @@ -62,7 +67,7 @@ namespace MyCore.Service.Controllers if (user != null) { System.Console.WriteLine($"Init userId for MqqClientService ! {user.Email}"); - MqttClientService.SetServices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, _ActionService, _AutomationDatabaseService, user.Id); + MqttClientService.SetServices(_DeviceDatabaseService, _GroupDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, _ActionService, _AutomationDatabaseService, user.Id); } return Ok(token); diff --git a/MyCore/Controllers/GroupController.cs b/MyCore/Controllers/GroupController.cs new file mode 100644 index 0000000..858785f --- /dev/null +++ b/MyCore/Controllers/GroupController.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security.Authentication; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using MongoDB.Bson; +using Mqtt.Client.AspNetCore.Services; +using MyCore.Interfaces.DTO; +using MyCore.Interfaces.Models; +using MyCore.Service.Services; +using MyCore.Services; +using MyCore.Services.Devices; +using MyCore.Services.MyControlPanel; + +namespace MyCore.Service.Controllers +{ + [Authorize] // TODO Add ROLES (Roles = "Admin") + [Route("api/group")] + [ApiController] + public class GroupController : ControllerBase + { + private UserDatabaseService _UserDatabaseService; + private GroupDatabaseService _GroupDatabaseService; + private DeviceDatabaseService _DeviceDatabaseService; + private ProviderDatabaseService _ProviderDatabaseService; + private readonly IMqttClientService _mqttClientService; + //private readonly IMqttOnlineClientService _mqttOnlineClientService; + + public GroupController(UserDatabaseService userDatabaseService, GroupDatabaseService groupDatabaseService, DeviceDatabaseService deviceDatabaseService, ProviderDatabaseService providerDatabaseService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) + { + this._UserDatabaseService = userDatabaseService; + this._GroupDatabaseService = groupDatabaseService; + this._DeviceDatabaseService = deviceDatabaseService; + this._ProviderDatabaseService = providerDatabaseService; + this._mqttClientService = provider.MqttClientService; + //this._mqttOnlineClientService = onlineProvider.MqttOnlineClientService; + } + + /// + /// Get all groups for the specified user + /// + /// Id of user + [ProducesResponseType(typeof(List), 200)] + [HttpGet("{userId}")] + public ObjectResult GetAll(string userId) + { + try + { + List Groups = _GroupDatabaseService.GetAll(userId); + + List groupsSummaryDTO = Groups.Select(d => d.ToSummaryDTO()).ToList(); + + return new OkObjectResult(groupsSummaryDTO); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Get detail info of a specified group + /// + /// user id + /// groupid + [ProducesResponseType(typeof(GroupDetailDTO), 200)] + [HttpGet("detail/{groupId}")] + public ObjectResult GetDetail(string userId, string groupId) + { + try + { + if (userId != null && groupId != null) + { + Group group = _GroupDatabaseService.GetById(groupId); + List devices = _DeviceDatabaseService.GetByLocation(group.UserId, groupId); + + return new OkObjectResult(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); + } + else + { + return new ObjectResult("Invalid parameters") { StatusCode = 400 }; + } + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Get list of group from a type + /// + /// user Id + /// group type + [ProducesResponseType(typeof(List), 200)] + [HttpGet("{userId}/type/{type}")] + public ObjectResult GetGroupsByType(string userId, string type) // Change string type to enum ? Lights etc ? + { + try + { + if (userId != null) + { + List groups = _GroupDatabaseService.GetByType(userId, type); + + return new OkObjectResult(groups.Select(d => d.ToSummaryDTO())); + } + else + { + return new ObjectResult("Invalid parameters") { StatusCode = 400 }; + } + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create a group + /// + /// Group to create + [ProducesResponseType(typeof(GroupDetailDTO), 200)] + [HttpPost] + public ObjectResult Create([FromBody] GroupCreateOrUpdateDetailDTO groupCreateOrUpdateDetail) + { + try + { + if (groupCreateOrUpdateDetail == null) + throw new KeyNotFoundException("Group is null"); + + GroupDetailDTO groupCreated = GroupService.CreateOrUpdate(this._GroupDatabaseService, this._DeviceDatabaseService, groupCreateOrUpdateDetail.UserId, groupCreateOrUpdateDetail, true); + + return new OkObjectResult(groupCreated); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create groups from provider + /// + /// User Id + [ProducesResponseType(typeof(List), 200)] + [HttpPost("{userId}/fromZigbee")] + public ObjectResult CreateDevicesFromZigbee2Mqtt(string userId) + { + try + { + if (userId == null) + throw new InvalidOperationException("User not found"); + + if (!UserService.IsExist(_UserDatabaseService, userId)) + throw new KeyNotFoundException("User not found"); + + Provider provider = _ProviderDatabaseService.GetByType("zigbee2mqtt"); + if (provider == null) + throw new KeyNotFoundException("Zigbee2mqtt provider not found"); + + // GET ALL LOCAL GROUPS + var groups = MqttClientService.groups; // Be carefull, we only got the exact result after each connection + + List groupsDetail= GroupService.CreateFromZigbeeAsync(_GroupDatabaseService, _DeviceDatabaseService, userId, groups); + + return new OkObjectResult(groupsDetail); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Update a group + /// + /// group to update + [ProducesResponseType(typeof(GroupCreateOrUpdateDetailDTO), 200)] + [HttpPut] + public ObjectResult Update([FromBody] GroupCreateOrUpdateDetailDTO groupCreateOrUpdateDetail) + { + try + { + if (!_GroupDatabaseService.IsExist(groupCreateOrUpdateDetail.Id)) + throw new KeyNotFoundException("Group does not exist"); + + GroupDetailDTO groupUpdated = GroupService.CreateOrUpdate(this._GroupDatabaseService, this._DeviceDatabaseService, groupCreateOrUpdateDetail.UserId, groupCreateOrUpdateDetail, false); + + return new OkObjectResult(groupUpdated); + } + catch (KeyNotFoundException ex) + { + return new BadRequestObjectResult(ex.Message) { StatusCode = 404 }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete device from a group + /// + /// Id of device to delete from the group + /// Id of group + [HttpDelete("{groupId}/device/{deviceId}")] + public ObjectResult Delete(string deviceId, string groupId) + { + try + { + if (groupId != null && deviceId != null) + { + if (_GroupDatabaseService.IsExist(groupId)) + { + // Update group + Group group = _GroupDatabaseService.GetById(groupId); + group.DevicesIds = group.DevicesIds.Where(d => d != deviceId).ToList(); + group.UpdatedDate = DateTime.Now; + _GroupDatabaseService.Update(group); + + // Update device + Device device = _DeviceDatabaseService.GetById(deviceId); + if (device != null) + { + device.GroupIds = device.GroupIds.Where(g => g != groupId).ToList(); + device.UpdatedDate = DateTime.Now; + _DeviceDatabaseService.Update(device); + } + } + } + + return new OkObjectResult(201); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete a group + /// + /// Id of group to delete + [HttpDelete("{groupId}")] + public ObjectResult Delete(string groupId) + { + try + { + if (groupId != null) + { + if (_GroupDatabaseService.IsExist(groupId)) + { + Group group = _GroupDatabaseService.GetById(groupId); + // Delete group from all devices + List devices = _DeviceDatabaseService.GetByLocation(group.UserId, groupId); + foreach (var device in devices) + { + device.GroupIds = device.GroupIds.Where(g => g != groupId).ToList(); + device.UpdatedDate = DateTime.Now; + _DeviceDatabaseService.Update(device); + } + // Delete group + _GroupDatabaseService.Remove(groupId); + } + } + + return new OkObjectResult(201); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Get all zigbee2Mqtt groups + /// + /// User Id + [ProducesResponseType(typeof(List), 200)] + [HttpGet("zigbee2Mqtt/{userId}")] + public ObjectResult GetGroupsFromZigbee2Mqtt(string userId) + { + try + { + if (userId == null) + throw new InvalidOperationException("User not found"); + + if (!UserService.IsExist(_UserDatabaseService, userId)) + throw new KeyNotFoundException("User not found"); + + // GET ALL LOCAL GROUPS + var groups = MqttClientService.groups; // Be carefull, we only got the exact result after each connection + + return new OkObjectResult(groups); + } + catch (InvalidOperationException ex) + { + return new BadRequestObjectResult(ex.Message) { StatusCode = 400 }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete all group for a specified + /// + /// Id of user + [HttpDelete("user/{userId}")] + public ObjectResult DeleteAllForUser(string userId) + { + try + { + if (userId != null) + { + if (_UserDatabaseService.IsExist(userId)) + { + _GroupDatabaseService.RemoveForUser(userId); + } + } + + return new OkObjectResult(201); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + } +} diff --git a/MyCore/Extensions/MqttClientService.cs b/MyCore/Extensions/MqttClientService.cs index f88b51b..b79040b 100644 --- a/MyCore/Extensions/MqttClientService.cs +++ b/MyCore/Extensions/MqttClientService.cs @@ -20,8 +20,10 @@ namespace Mqtt.Client.AspNetCore.Services private static IMqttClient mqttClient; private IMqttClientOptions options; public static List devices = new List(); + public static List groups = new List(); public static string userId; static DeviceDatabaseService _deviceDatabaseService; + static GroupDatabaseService _groupDatabaseService; static ProviderDatabaseService _providerDatabaseService; static LocationDatabaseService _locationDatabaseService; static AutomationDatabaseService _automationDatabaseService; @@ -64,28 +66,35 @@ namespace Mqtt.Client.AspNetCore.Services var topic = e.ApplicationMessage.Topic; - if (_actionService != null) { - ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _providerDatabaseService, _locationDatabaseService, _automationDatabaseService, userId); + if (_actionService != null) + { + ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _locationDatabaseService, _automationDatabaseService, userId); } - //if () { } - //List Devices = _DeviceDatabaseService.GetByProviderId(topic); - switch (topic) { case "zigbee2mqtt/bridge/config/devices": try { - var test = JsonConvert.DeserializeObject>(payload); - devices = test; - - // TODO Update in DB, current devices state + var devicesConvert = JsonConvert.DeserializeObject>(payload); + devices = devicesConvert; } catch (Exception ex) { Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); } break; + case "zigbee2mqtt/bridge/groups": + try + { + var groupsConvert = JsonConvert.DeserializeObject>(payload); + groups = groupsConvert; + } + catch (Exception ex) + { + Console.WriteLine($"Error during retrieving groups ! Exception: {ex}"); + } + break; } //return new Task(null); @@ -152,9 +161,10 @@ namespace Mqtt.Client.AspNetCore.Services return devices; } - public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, ActionService _ActionService, AutomationDatabaseService _AutomationDatabaseService, string UserId) + public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, ActionService _ActionService, AutomationDatabaseService _AutomationDatabaseService, string UserId) { _deviceDatabaseService = _DeviceDatabaseService; + _groupDatabaseService = _GroupDatabaseService; _providerDatabaseService = _ProviderDatabaseService; _locationDatabaseService = _LocationDatabaseService; _automationDatabaseService = _AutomationDatabaseService; diff --git a/MyCore/Security.cs b/MyCore/Security.cs index 71a3fbe..23bedc4 100644 --- a/MyCore/Security.cs +++ b/MyCore/Security.cs @@ -32,7 +32,7 @@ namespace MyCore.Service /// /// Permissions for each type of profile /// - public static readonly Dictionary ProfilesConfiguration = new Dictionary() + public static readonly Dictionary ProfilesConfiguration = new Dictionary() { // An admin has access to everything //{ typeof(AdminProfile), new[] { Permissions.Admin} }, diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 89e9a1e..61adb92 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -2,6 +2,7 @@ using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; using MyCore.Interfaces.Models.Providers.Zigbee.Aqara; +using MyCore.Service.Services; using MyCore.Services.MyControlPanel; using Newtonsoft.Json; using System; @@ -22,24 +23,26 @@ namespace MyCore.Services.Devices 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 static void 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, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, AutomationDatabaseService _AutomationDatabaseService, string userId) { - // TODO Check if two action from the same device ar not too closed (!! motion (F => T) and switch (action and click = same) + // TODO Check if two action from the same device ar not too closed (!! motion (F => T) and switch (action and click = same) // just update if action is different in case of true false action var actionTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - + var providers = _ProviderDatabaseService.GetAll(userId); string[] topicSplit = topic.Split('/'); + UpdateZigbee2MqttConfigAsync(topic, message, userId, _DeviceDatabaseService, _GroupDatabaseService, _ProviderDatabaseService, _LocationDatabaseService); + System.Console.WriteLine($"Received message {message}"); switch (topicSplit[0]) { case "zigbee2mqtt": - var test0 = _ProviderDatabaseService.GetByType(topicSplit[0]).Id; - var automations = _AutomationDatabaseService.GetByProvider(test0); + var provider = _ProviderDatabaseService.GetByType(topicSplit[0]).Id; + var automations = _AutomationDatabaseService.GetByProvider(provider); var serviceName = topicSplit[1]; var zigbeeDevice = _DeviceDatabaseService.GetByName(serviceName).FirstOrDefault(); - if (zigbeeDevice!= null) { + if (zigbeeDevice != null) { try { @@ -54,6 +57,10 @@ namespace MyCore.Services.Devices 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; @@ -76,13 +83,13 @@ namespace MyCore.Services.Devices System.Console.WriteLine($"Open automation {automation.Name}"); var automationTrigger = automation.Triggers.Where(t => t.DeviceId == zigbeeDevice.Id).FirstOrDefault(); - Type type = deserializedReceivedMessage.GetType(); + System.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) + if (automationTrigger.StateName == triggerStateName && automationTrigger.StateValue == triggerStateValueCheck.ToString()) { // Todo check condition if (automation.Conditions.Count <= 0) @@ -95,11 +102,39 @@ namespace MyCore.Services.Devices var stateName = action.StateName; var stateValue = action.StateValue; - var zigbeeDeviceAction = _DeviceDatabaseService.GetById(action.DeviceId); - var providerAction = _ProviderDatabaseService.GetById(userId, zigbeeDeviceAction.ProviderId); + var actionName = ""; + Device actionDeviceToTest = new Device(); - System.Console.WriteLine($"We get a zigbeeDeviceAction ! Name={zigbeeDeviceAction.Name} Type={zigbeeDeviceAction.Type}"); - System.Console.WriteLine($"Check action provider type ! Type={providerAction.Type} Name={providerAction.Name}"); + switch (action.Type) { + case Interfaces.Models.Type.DEVICE: + var zigbeeDeviceAction = _DeviceDatabaseService.GetById(action.DeviceId); + var providerActionTest = _ProviderDatabaseService.GetById(userId, action.ProviderId); + + actionName = zigbeeDeviceAction.Name; + actionDeviceToTest = zigbeeDeviceAction; + 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: + var zigbeeGroupAction = _GroupDatabaseService.GetById(action.GroupId); + actionName = zigbeeGroupAction.Name; + + System.Console.WriteLine($"We get a zigbeeGroupAction ! Name={zigbeeGroupAction.Name} Type={zigbeeGroupAction.Type}"); + System.Console.WriteLine($"Check action zigbeeGroupAction type ! Type={zigbeeGroupAction.Type}"); + + // 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; + break; + case Interfaces.Models.Type.HTTP: // Correct way ? + // TODO + break; + } + + var providerAction = _ProviderDatabaseService.GetById(userId, action.ProviderId); switch (providerAction.Type) { @@ -107,14 +142,14 @@ namespace MyCore.Services.Devices Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { }; var actionRequest = ""; - System.Console.WriteLine($"Zigbee type !"); - + System.Console.WriteLine($"zigbee2mqtt type !"); + // Todo GET AND CHECK DEVICE ACTION POSSIBLE // todo check state name (state, action.. ) - System.Console.WriteLine($"zigbeeDeviceAction.Type {zigbeeDeviceAction.Type}"); - if (zigbeeDeviceAction.Type == DeviceType.Light) + System.Console.WriteLine($"actionDeviceToTest.Type {actionDeviceToTest.Type}"); + if (actionDeviceToTest.Type == DeviceType.Light) { - var deserializedReceivedMessage2 = JsonConvert.DeserializeObject(zigbeeDeviceAction.LastState); + var deserializedReceivedMessage2 = JsonConvert.DeserializeObject(actionDeviceToTest.LastState); if (stateValue == DeviceAction.toggle.ToString()) { @@ -134,23 +169,29 @@ namespace MyCore.Services.Devices break; } } + else if (stateValue == DeviceAction.on.ToString()) { + actionRequest = "ON"; + zigbee2MqttRequest.brightness = 255; + } else { + // DeviceAction.off.ToString() => Need to ad validation ? + actionRequest = "OFF"; + zigbee2MqttRequest.brightness = 0; + } } System.Console.WriteLine($"Before retrieving type etc {zigbee2MqttRequest.state}"); - Type type2 = zigbee2MqttRequest.GetType(); + System.Type type2 = zigbee2MqttRequest.GetType(); PropertyInfo property2 = type2.GetProperty(stateName); property2.SetValue(zigbee2MqttRequest, actionRequest, null); var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - var name = zigbeeDeviceAction.Name.Substring(0, zigbeeDeviceAction.Name.Length - 1); // TODO CHANGE !!!! + System.Console.WriteLine($"Send request ! zigbee2mqtt/{actionName}/set/{request}"); - System.Console.WriteLine($"Send request ! zigbee2mqtt/{name}/set/{request}"); - - MqttClientService.PublishMessage("zigbee2mqtt/" + name + "/set", request); + MqttClientService.PublishMessage("zigbee2mqtt/" + actionName + "/set", request); break; case "meross": - + System.Console.WriteLine($"meross type !"); break; } } @@ -180,19 +221,19 @@ namespace MyCore.Services.Devices Task.Run(async () => { await YeelightService.Toggle(labLamp); }); } }*/ - /* if (test.action == "shake") - { - // TODO Check state + /* if (test.action == "shake") + { + // TODO Check state - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF", brightness = 0 }; - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); + 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); + }*/ if (test.action == "tap") { - var provider = providers.Where(p => p.Type == "meross").FirstOrDefault(); - var merossDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); + var merossProvider = providers.Where(p => p.Type == "meross").FirstOrDefault(); + var merossDevices = _DeviceDatabaseService.GetByProviderId(merossProvider.Id); var multiprise = merossDevices.Where(md => md.Name == "Multiprise bureau").FirstOrDefault(); //var prise = merossDevices.Where(md => md.Name == "Imprimante 3D").FirstOrDefault(); @@ -235,12 +276,12 @@ namespace MyCore.Services.Devices } //await MqttClientOnlineService.PublishMessage("Notification", "Hey magic cube 0 !"); } - if (topicSplit[1].Contains("MotionWC")) + if (topicSplit[1].Contains("MotionWC")) { var aqaraSwitch = JsonConvert.DeserializeObject(message); if (aqaraSwitch.occupancy) { - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "ON"}; + Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "ON" }; var request = JsonConvert.SerializeObject(zigbee2MqttRequest); MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request); @@ -275,5 +316,56 @@ namespace MyCore.Services.Devices break; } } + + public static async Task UpdateZigbee2MqttConfigAsync(string topic, string message, string userId, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService) { + // update zigbee2mqqtt config + switch (topic) + { + case "zigbee2mqtt/bridge/config/devices": + try + { + var devices = JsonConvert.DeserializeObject>(message); + var zigbee2mqttProvider = _ProviderDatabaseService.GetByType("zigbee2mqtt"); + + if (zigbee2mqttProvider != null) + { + // Retrieve existing devices + List existingDevices = _DeviceDatabaseService.GetByProviderId(zigbee2mqttProvider.Id); + var existingDevicesAddresses = existingDevices.Select(ed => ed.ServiceIdentification); + + // Filter devices and check if something new + var filteredDevices = devices.Where(d => !existingDevicesAddresses.Contains(d.ieeeAddr)).ToList(); + // Add new devices + Dictionary> createdDevices = await DeviceService.CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, filteredDevices, zigbee2mqttProvider); + } + + System.Console.WriteLine($"Devices updated for user {userId}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); + } + break; + case "zigbee2mqtt/bridge/groups": + try + { + var groupsConvert = JsonConvert.DeserializeObject>(message); + + var zigbee2mqttProvider = _ProviderDatabaseService.GetByType("zigbee2mqtt"); + + if (zigbee2mqttProvider != null) + { + var groups = _GroupDatabaseService.GetByType(userId, "zigbee2mqtt"); + // Compare the groups from MyCore and the group we received, if something diff in one group => Hard refresh (delete, new) + GroupService.CompareGroupsFromZigbee2Mqtt(userId, groups, groupsConvert, _DeviceDatabaseService, _GroupDatabaseService); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error during retrieving groups ! Exception: {ex}"); + } + break; + } + } } } diff --git a/MyCore/Services/GroupService.cs b/MyCore/Services/GroupService.cs new file mode 100644 index 0000000..d076f51 --- /dev/null +++ b/MyCore/Services/GroupService.cs @@ -0,0 +1,147 @@ +using MyCore.Interfaces.DTO; +using MyCore.Interfaces.Models; +using MyCore.Services.MyControlPanel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Service.Services +{ + public class GroupService + { + public static GroupDetailDTO CreateOrUpdate(GroupDatabaseService _GroupDatabaseService, DeviceDatabaseService _DeviceDatabaseService, string userId, GroupCreateOrUpdateDetailDTO groupCreateOrUpdateDetailDTO, bool create) + { + List devices = new List(); + Group group; + if (create) + { + group = new Group(); + group.CreatedDate = DateTime.Now; + group.DevicesIds = new List(); + } + else + { + group = _GroupDatabaseService.GetById(groupCreateOrUpdateDetailDTO.Id); + } + + group.UserId = userId; + group.Name = groupCreateOrUpdateDetailDTO.Name; + group.Type = group.Type; // TODO + group.UpdatedDate = DateTime.Now; + group.DevicesIds.AddRange(groupCreateOrUpdateDetailDTO.DeviceIds); + + if (create) + { + group = _GroupDatabaseService.Create(group); + } + else + { + group = _GroupDatabaseService.Update(group); + } + + foreach (var deviceId in groupCreateOrUpdateDetailDTO.DeviceIds) + { + Device device = _DeviceDatabaseService.GetById(deviceId); + devices.Add(device); + device.GroupIds.Add(group.Id); + _DeviceDatabaseService.Update(device); + } + + return group.ToDTO(devices.Select(d => d.ToDTO()).ToList()); + } + + public static List CreateFromZigbeeAsync(GroupDatabaseService _GroupDatabaseService, DeviceDatabaseService _DeviceDatabaseService, string userId, List zigbee2MqttGroups) + { + List groups = new List(); + + 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.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); + } + + groups.Add(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); + } + + return groups; + } + + // We only go in this method if the group config change during operation + public static void CompareGroupsFromZigbee2Mqtt(string userId, List groups, List zigbee2MqttGroups, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService) + { + // We do not refresh all list as we don't want to create plenty of new groupId and have to delete from device param everytime + List existingGroups = new List(); + foreach (var group in groups) + { + var devicesFromGroup = _DeviceDatabaseService.GetByGroup(group.Id); + // convert existing group to zigbee2mqtt group + existingGroups.Add(new Zigbee2MqttGroup { friendly_name = group.Name, id = group.ServiceIdentification, members = devicesFromGroup.Select(d => new Member() { ieee_address = d.ServiceIdentification }).ToList() }); ; + } + + foreach (var zigbee2MqttGroup in zigbee2MqttGroups) + { + var existingGroupWithThisId = existingGroups.Where(eg => eg.id == zigbee2MqttGroup.id).ToList().FirstOrDefault(); + + if (existingGroupWithThisId == null) + { + // New group -> Create it ! + CreateFromZigbeeAsync(_GroupDatabaseService, _DeviceDatabaseService, userId, zigbee2MqttGroups.Where(z => z.id == zigbee2MqttGroup.id).ToList()); + } else { + if (!Enumerable.SequenceEqual(zigbee2MqttGroup.members.Select(m => m.ieee_address).OrderBy(t => t), existingGroupWithThisId.members.Select(m => m.ieee_address).OrderBy(t => t))) + { + // different ? Update + + // As user not change everytime.. => Hard refresh. + var groupToDelete = groups.Where(eg => eg.ServiceIdentification == existingGroupWithThisId.id).FirstOrDefault(); + + // Delete in device first + var devicesInGroup = _DeviceDatabaseService.GetByGroup(groupToDelete.Id); + + foreach (var device in devicesInGroup) + { + device.GroupIds = device.GroupIds.Where(g => g != groupToDelete.Id).ToList(); + _DeviceDatabaseService.Update(device); + } + // Delete group + _GroupDatabaseService.Remove(groupToDelete.Id); + + // Create new group + CreateFromZigbeeAsync(_GroupDatabaseService, _DeviceDatabaseService, userId, new List() { zigbee2MqttGroup }); + } + } + // else do nothing + } + } + + + } +} diff --git a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs index 0f4fd4a..1caa5f5 100644 --- a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs @@ -29,6 +29,11 @@ namespace MyCore.Services.MyControlPanel return _Devices.Find(d => d.Id == id).FirstOrDefault(); } + public Device GetByServiceIdentification(string serviceIdentification) + { + return _Devices.Find(d => d.ServiceIdentification == serviceIdentification).FirstOrDefault(); + } + public List GetByIds(List ids) { return _Devices.Find(d => ids.Contains(d.Id)).ToList(); @@ -49,6 +54,11 @@ namespace MyCore.Services.MyControlPanel return _Devices.Find(d => d.ProviderId == providerId).ToList(); } + public List GetByGroup(string groupId) + { + return _Devices.Find(d => d.GroupIds.Contains(groupId)).ToList(); + } + public List GetByName(string name) { return _Devices.Find(d => d.Name == name).ToList(); diff --git a/MyCore/Services/MyControlPanel/Database/GroupDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/GroupDatabaseService.cs index 3728131..5ff60d8 100644 --- a/MyCore/Services/MyControlPanel/Database/GroupDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/GroupDatabaseService.cs @@ -18,14 +18,25 @@ namespace MyCore.Services.MyControlPanel var database = client.GetDatabase("MyCoreDb"); _Groups = database.GetCollection("Groups"); } - public List GetAll() + + public List GetAll(string userId) { - return _Groups.Find(d => true).ToList(); + return _Groups.Find(d => d.UserId == userId).ToList(); } public Group GetById(string id) { - return _Groups.Find(g => g.Id == id).FirstOrDefault(); + return _Groups.Find(d => d.Id == id).FirstOrDefault(); + } + + public List GetByType(string userId, string type) + { + return _Groups.Find(d => d.UserId == userId && d.Type == type).ToList(); + } + + public bool IsExist(string id) + { + return _Groups.Find(d => d.Id == id).FirstOrDefault() != null ? true : false; } public Group Create(Group group) @@ -34,9 +45,9 @@ namespace MyCore.Services.MyControlPanel return group; } - public Group Update(string id, Group groupIn) + public Group Update(Group groupIn) { - _Groups.ReplaceOne(group => group.Id == id, groupIn); + _Groups.ReplaceOne(room => room.Id == groupIn.Id, groupIn); return groupIn; } @@ -44,5 +55,10 @@ namespace MyCore.Services.MyControlPanel { _Groups.DeleteOne(group => group.Id == id); } + + public void RemoveForUser(string userId) + { + _Groups.DeleteMany(group => group.UserId == userId); + } } } diff --git a/MyCore/Startup.cs b/MyCore/Startup.cs index 33efbc0..3952e66 100644 --- a/MyCore/Startup.cs +++ b/MyCore/Startup.cs @@ -199,6 +199,7 @@ namespace MyCore services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped();