diff --git a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs index 9990265..9062c85 100644 --- a/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs +++ b/MyCore.Interfaces/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs @@ -23,4 +23,65 @@ namespace MyCore.Interfaces.Models public string dateCode { get; set; } public long lastSeen { get; set; } } + + public class Zigbee2MqttDeviceNew + { + public string date_code { get; set; } + public string friendly_name { get; set; } + public string ieeeAddr { get; set; } + public bool interview_completed { get; set; } + public bool interviewing { get; set; } + public string model_id { get; set; } + public int network_address { get; set; } + public string powerSource { get; set; } + public string software_build_id { get; set; } + public bool supported { get; set; } + public string type { get; set; } + public DeviceDefinition definition { get; set; } + } + + public class DeviceDefinition { + public string description { get; set; } + public string model { get; set; } + public bool supports_ota { get; set; } + public string vendor { get; set; } + public List exposes { get; set; } + } + + public class Exposes { + public int access { get; set; } + public string description { get; set; } + public string name { get; set; } + public string property { get; set; } + public string type { get; set; } // numeric, binary, enum, light, switch + public string unit { get; set; } // if numeric + + public List values { get; set; } // if enum + + public bool value_on { get; set; } // if binary + public bool value_off { get; set; } // if binary + + public int value_max { get; set; } // if numeric + public int value_min { get; set; } // if numeric + + public List features { get; set; } // if light or switch + + } + + public class Features + { + public int access { get; set; } + public string description { get; set; } + public string name { get; set; } + 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 int value_max { get; set; } // if numeric + public int value_min { get; set; } // if numeric + + } } \ No newline at end of file diff --git a/MyCore/Controllers/Devices/DeviceController.cs b/MyCore/Controllers/Devices/DeviceController.cs index d9ad709..8e4e7f9 100644 --- a/MyCore/Controllers/Devices/DeviceController.cs +++ b/MyCore/Controllers/Devices/DeviceController.cs @@ -262,6 +262,7 @@ namespace MyCore.Controllers { if (deviceId != null) { + // TODO REMOVE DEVICE ID IN AUTOMATION and delete automation if none device ? if (_DeviceDatabaseService.IsExist(deviceId)) { _DeviceDatabaseService.Remove(deviceId); @@ -276,6 +277,33 @@ namespace MyCore.Controllers } } + /// + /// Delete devices from provider + /// + /// User Id + /// Id of Provider + [HttpDelete("{userId}/fromProvider/{providerId}")] + public ObjectResult DeleteDevicesFromProvider(string userId, string providerId) + { + try + { + if (userId != null) + { + // TODO REMOVE DEVICE ID IN AUTOMATION and delete automation if none device ? + if (_UserDatabaseService.IsExist(userId)) + { + _DeviceDatabaseService.RemoveForProvider(providerId); + } + } + + return new OkObjectResult(201); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + /// /// Delete all device for a specified /// @@ -287,6 +315,7 @@ namespace MyCore.Controllers { if (userId != null) { + // TODO REMOVE DEVICE ID IN AUTOMATION and delete automation if none device ? if (_UserDatabaseService.IsExist(userId)) { _DeviceDatabaseService.RemoveForUser(userId); diff --git a/MyCore/Controllers/Devices/ProviderController.cs b/MyCore/Controllers/Devices/ProviderController.cs index 5ef329e..ad02ed3 100644 --- a/MyCore/Controllers/Devices/ProviderController.cs +++ b/MyCore/Controllers/Devices/ProviderController.cs @@ -161,6 +161,7 @@ namespace MyCore.Controllers { try { + // TODO DELETE ALL DEVICES linked if (providerId == null) throw new InvalidOperationException("Provider is null"); diff --git a/MyCore/Extensions/MqttClientService.cs b/MyCore/Extensions/MqttClientService.cs index b79040b..e14ffe9 100644 --- a/MyCore/Extensions/MqttClientService.cs +++ b/MyCore/Extensions/MqttClientService.cs @@ -20,6 +20,7 @@ namespace Mqtt.Client.AspNetCore.Services private static IMqttClient mqttClient; private IMqttClientOptions options; public static List devices = new List(); + public static List devicesNew = new List(); public static List groups = new List(); public static string userId; static DeviceDatabaseService _deviceDatabaseService; @@ -29,12 +30,21 @@ namespace Mqtt.Client.AspNetCore.Services static AutomationDatabaseService _automationDatabaseService; static ActionService _actionService; + public static string lastTopic; + public static long lastTimeTopic; + public MqttClientService(IMqttClientOptions options) { + var server = "localhost"; + var clientId = "ApiService"; +#if DEBUG + server = "192.168.31.140"; + clientId = "ApiServiceTest"; +#endif this.options = options; this.options = new MqttClientOptionsBuilder() - .WithClientId("ApiServiceTest") // prod = ApiService - .WithTcpServer("192.168.31.140") // TODO replace by localhost + .WithClientId(clientId) + .WithTcpServer(server) .WithCredentials("mqtt", "mqtt") .WithCleanSession() .Build(); @@ -51,24 +61,31 @@ namespace Mqtt.Client.AspNetCore.Services 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; - if (_actionService != null) + var currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + // For check if not doubled message (example : motion double true value) + var test = currentTime - lastTimeTopic; + + // Less than one second between two messages from a same device + if (!(lastTopic == topic && test <= 1000)) { - ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _locationDatabaseService, _automationDatabaseService, userId); + if (_actionService != null) + { + ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _locationDatabaseService, _automationDatabaseService, userId); + } + } + else + { + // TODO Check if not a valuable message (example state true to false => correct message..) + System.Console.WriteLine($"Drop message - spam from {topic}"); } switch (topic) @@ -95,18 +112,28 @@ namespace Mqtt.Client.AspNetCore.Services Console.WriteLine($"Error during retrieving groups ! Exception: {ex}"); } break; + case "zigbee2mqtt/bridge/devices": + try + { + var devicesConvert = JsonConvert.DeserializeObject>(payload); + devicesNew = devicesConvert; + } + catch (Exception ex) + { + Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); + } + break; } - //return new Task(null); - // TODO check what to do - //await PublishMessage("test", "teeest"); + lastTimeTopic = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + lastTopic = topic; + return null; } public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs) { System.Console.WriteLine("connected"); - //await mqttClient.SubscribeAsync("hello/world"); await mqttClient.SubscribeAsync("#"); await PublishMessage("zigbee2mqtt/bridge/config/devices/get", ""); } @@ -117,7 +144,6 @@ namespace Mqtt.Client.AspNetCore.Services { await mqttClient.ReconnectAsync(); } - //throw new System.NotImplementedException(); } public async Task StartAsync(CancellationToken cancellationToken) @@ -160,6 +186,10 @@ namespace Mqtt.Client.AspNetCore.Services { return devices; } + public static List GetDevicesNew() + { + return devicesNew; + } public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, ActionService _ActionService, AutomationDatabaseService _AutomationDatabaseService, string UserId) { diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 61adb92..8ac0928 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -19,23 +19,25 @@ 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 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) // 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); + var providerFromTopic = topicSplit[0]; + + // TODO Sortir automation d'un type specific.. et créer un objet ? System.Console.WriteLine($"Received message {message}"); - switch (topicSplit[0]) { + switch (providerFromTopic) { case "zigbee2mqtt": + UpdateZigbee2MqttConfigAsync(topic, message, userId, _DeviceDatabaseService, _GroupDatabaseService, _ProviderDatabaseService, _LocationDatabaseService); + var provider = _ProviderDatabaseService.GetByType(topicSplit[0]).Id; var automations = _AutomationDatabaseService.GetByProvider(provider); @@ -88,14 +90,12 @@ namespace MyCore.Services.Devices triggerStateValueCheck = property.GetValue(deserializedReceivedMessage); triggerStateName = property.Name; - // Todo check state name and value for triggers.. - if (automationTrigger.StateName == triggerStateName && automationTrigger.StateValue == triggerStateValueCheck.ToString()) + if (automationTrigger.StateName == triggerStateName && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) { // Todo check condition if (automation.Conditions.Count <= 0) { System.Console.WriteLine("None conditions"); - // => SEND REQUEST foreach (var action in automation.Actions) { System.Console.WriteLine($"Check Action ! {action.StateName} {action.StateValue}"); @@ -142,20 +142,25 @@ namespace MyCore.Services.Devices Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { }; var actionRequest = ""; + // 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 !"); - - // Todo GET AND CHECK DEVICE ACTION POSSIBLE - // todo check state name (state, action.. ) + + // TODO: GET AND CHECK DEVICE ACTION POSSIBILITIES + // TODO: Check state name (state, action.. ) System.Console.WriteLine($"actionDeviceToTest.Type {actionDeviceToTest.Type}"); - if (actionDeviceToTest.Type == DeviceType.Light) + // 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 deserializedReceivedMessage2 = JsonConvert.DeserializeObject(actionDeviceToTest.LastState); + var deserializedLastState = JsonConvert.DeserializeObject(actionDeviceToTest.LastState); if (stateValue == DeviceAction.toggle.ToString()) { System.Console.WriteLine($"Toggle action"); // TO CHECK - switch (deserializedReceivedMessage2.state) + switch (deserializedLastState.state) { case "ON": actionRequest = "OFF"; @@ -169,26 +174,40 @@ namespace MyCore.Services.Devices break; } } - else if (stateValue == DeviceAction.on.ToString()) { - actionRequest = "ON"; - zigbee2MqttRequest.brightness = 255; + 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 ? - actionRequest = "OFF"; - zigbee2MqttRequest.brightness = 0; + + // retrieve device state to check if already in asked state + if (deserializedLastState.state.ToLower() == stateValue.ToLower()) + alreadyInAskedState = true; + else + { + actionRequest = "OFF"; + zigbee2MqttRequest.brightness = 0; + } } } - System.Console.WriteLine($"Before retrieving type etc {zigbee2MqttRequest.state}"); + 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.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}"); + System.Console.WriteLine($"Send request ! zigbee2mqtt/{actionName}/set/{request}"); - MqttClientService.PublishMessage("zigbee2mqtt/" + actionName + "/set", request); + MqttClientService.PublishMessage("zigbee2mqtt/" + actionName + "/set", request); + } break; case "meross": System.Console.WriteLine($"meross type !"); @@ -276,41 +295,6 @@ namespace MyCore.Services.Devices } //await MqttClientOnlineService.PublishMessage("Notification", "Hey magic cube 0 !"); } - if (topicSplit[1].Contains("MotionWC")) - { - var aqaraSwitch = JsonConvert.DeserializeObject(message); - if (aqaraSwitch.occupancy) - { - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "ON" }; - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - - MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request); - } - else { - /*Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF" }; - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - - MqttClientService.PublishMessage("zigbee2mqtt/LampeWC/set", request);*/ - } - } - if (topicSplit[1].Contains("Motion0")) - { - var aqaraMotion = JsonConvert.DeserializeObject(message); - if (aqaraMotion.occupancy) - { - Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "ON", brightness = 255 }; - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - - MqttClientService.PublishMessage("zigbee2mqtt/GU10Bureau/set", request); - } - else - { - /*Zigbee2MqttRequest zigbee2MqttRequest = new Zigbee2MqttRequest() { state = "OFF", brightness = 0 }; - var request = JsonConvert.SerializeObject(zigbee2MqttRequest); - - MqttClientService.PublishMessage("zigbee2mqtt/GU10Bureau/set", request);*/ - } - } break; default: break; @@ -357,7 +341,8 @@ namespace MyCore.Services.Devices { 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); + // TODO : wait for new devices + // GroupService.CompareGroupsFromZigbee2Mqtt(userId, groups, groupsConvert, _DeviceDatabaseService, _GroupDatabaseService); } } catch (Exception ex) diff --git a/MyCore/Services/Devices/DeviceService.cs b/MyCore/Services/Devices/DeviceService.cs index 2ca70c9..295357f 100644 --- a/MyCore/Services/Devices/DeviceService.cs +++ b/MyCore/Services/Devices/DeviceService.cs @@ -4,6 +4,7 @@ using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; using MyCore.Interfaces.Models.MyControlPanel; using MyCore.Services.MyControlPanel; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; @@ -33,6 +34,8 @@ namespace MyCore.Services.Devices 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 }, + + new SupportedDevice { Manufacturer = "SONOFF", Model = "BASICZBR3", Description = "Zigbee smart switch", DeviceType = DeviceType.Switch }, }; public static DeviceDetailDTO CreateOrUpdate(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, DeviceDetailDTO deviceDetailDTO, bool create) @@ -122,8 +125,10 @@ namespace MyCore.Services.Devices devices = CreateYeelightDevices(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, yeelightDevices, provider); break; case "zigbee2mqtt": - List zigbee2MqttDevices = MqttClientService.devices; - devices = await CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider); + List zigbee2MqttDevices = MqttClientService.devicesNew; + devices = await CreateFromZigbeeNewAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider); + /*List zigbee2MqttDevices = MqttClientService.devices; + devices = await CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider);*/ break; } } @@ -194,6 +199,76 @@ namespace MyCore.Services.Devices }; } + public static async Task>> CreateFromZigbeeNewAsync(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List zigbee2MqttDevices, Provider provider) + { + List createdZigbeeDevices = new List(); + List notSupportedZigbeeDevices = new List(); + + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); + + // Not necessarry + if (zigbee2MqttDevices.Count <= 0) + { + //zigbee2MqttDevices = await MqttClientService.AskDevicesAsync(); + } + + zigbee2MqttDevices = zigbee2MqttDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd.ieeeAddr)).ToList(); + + foreach (var zigbee2MqttDevice in zigbee2MqttDevices) + { + DeviceDetailDTO deviceDetailDTO = new DeviceDetailDTO(); + deviceDetailDTO.Name = zigbee2MqttDevice.friendly_name; + deviceDetailDTO.ServiceIdentification = zigbee2MqttDevice.ieeeAddr; + deviceDetailDTO.ProviderId = provider.Id; + deviceDetailDTO.ProviderName = provider.Name; + + deviceDetailDTO.Description = zigbee2MqttDevice.type == "Coordinator" ? "Coordinator" : zigbee2MqttDevice.definition.description; + + deviceDetailDTO.Model = zigbee2MqttDevice.type == "Coordinator" ? "Coordinator" : zigbee2MqttDevice.definition.model; // Is the base to understand incoming messages ! + deviceDetailDTO.FirmwareVersion = zigbee2MqttDevice.software_build_id; + + deviceDetailDTO.Battery = zigbee2MqttDevice.powerSource == null ? false : zigbee2MqttDevice.powerSource.Contains("Battery"); + deviceDetailDTO.ManufacturerName = zigbee2MqttDevice.definition?.vendor == null ? provider.Type : zigbee2MqttDevice.definition?.vendor.ToLower(); + deviceDetailDTO.MeansOfCommunications = new List(); + deviceDetailDTO.MeansOfCommunications.Add(MeansOfCommunication.Zigbee); + + deviceDetailDTO.CreatedDate = DateTime.Now; + deviceDetailDTO.UpdatedDate = DateTime.Now; + + if (zigbee2MqttDevice.type != "Coordinator") + { + // EXPOSES ! + List supportedOperationsDTO = new List(); + foreach (var supportedOperation in zigbee2MqttDevice.definition?.exposes) + { + supportedOperationsDTO.Add(JsonConvert.SerializeObject(supportedOperation)); + } + deviceDetailDTO.SupportedOperations = supportedOperationsDTO; + + deviceDetailDTO.Type = GetDeviceTypeFromZigbeeModel(zigbee2MqttDevice.definition.model); + } + else { + deviceDetailDTO.Type = DeviceType.Gateway; + } + + if (zigbee2MqttDevice.supported) + { + // Supported device ! + createdZigbeeDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, deviceDetailDTO, true)); + } + else + { + // Not yet supported ! + notSupportedZigbeeDevices.Add(deviceDetailDTO); + } + } + + return new Dictionary>() { + { "createdDevices", createdZigbeeDevices }, + { "notSupportedDevices", notSupportedZigbeeDevices } + }; + } + private static DeviceType GetDeviceTypeFromZigbeeModel(string zigbeeModel) { return supportedDevices.Any(sd => sd.Model == zigbeeModel) ? diff --git a/MyCore/Services/GroupService.cs b/MyCore/Services/GroupService.cs index d076f51..4a33345 100644 --- a/MyCore/Services/GroupService.cs +++ b/MyCore/Services/GroupService.cs @@ -63,7 +63,7 @@ namespace MyCore.Service.Services 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"; diff --git a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs index 1caa5f5..c71ce59 100644 --- a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs @@ -91,6 +91,11 @@ namespace MyCore.Services.MyControlPanel _Devices.DeleteOne(device => device.Id == id); } + public void RemoveForProvider(string providerId) + { + _Devices.DeleteMany(device => device.ProviderId == providerId); + } + public void RemoveForUser(string userId) { _Devices.DeleteMany(device => device.UserId == userId);