Mc Delete device from provider + fix group identification + get bridge devices (supported operations - new methods) + clean code action service

This commit is contained in:
Thomas Fransolet 2021-03-16 18:30:14 +01:00
parent ee568d17da
commit b58473aa34
8 changed files with 267 additions and 81 deletions

View File

@ -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> 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<string> 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> 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
}
}

View File

@ -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
}
}
/// <summary>
/// Delete devices from provider
/// </summary>
/// <param name="userId">User Id</param>
/// <param name="providerId">Id of Provider</param>
[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 };
}
}
/// <summary>
/// Delete all device for a specified
/// </summary>
@ -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);

View File

@ -161,6 +161,7 @@ namespace MyCore.Controllers
{
try
{
// TODO DELETE ALL DEVICES linked
if (providerId == null)
throw new InvalidOperationException("Provider is null");

View File

@ -20,6 +20,7 @@ namespace Mqtt.Client.AspNetCore.Services
private static IMqttClient mqttClient;
private IMqttClientOptions options;
public static List<Zigbee2MqttDevice> devices = new List<Zigbee2MqttDevice>();
public static List<Zigbee2MqttDeviceNew> devicesNew = new List<Zigbee2MqttDeviceNew>();
public static List<Zigbee2MqttGroup> groups = new List<Zigbee2MqttGroup>();
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,25 +61,32 @@ 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;
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))
{
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<List<Zigbee2MqttDeviceNew>>(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<Zigbee2MqttDeviceNew> GetDevicesNew()
{
return devicesNew;
}
public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, ActionService _ActionService, AutomationDatabaseService _AutomationDatabaseService, string UserId)
{

View File

@ -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<LightBulb>(actionDeviceToTest.LastState);
var deserializedLastState = JsonConvert.DeserializeObject<LightBulb>(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,16 +174,29 @@ namespace MyCore.Services.Devices
break;
}
}
else if (stateValue == DeviceAction.on.ToString()) {
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;
}
}
}
if (!alreadyInAskedState) {
System.Console.WriteLine($"Before retrieving type etc {zigbee2MqttRequest.state}");
System.Type type2 = zigbee2MqttRequest.GetType();
@ -189,6 +207,7 @@ namespace MyCore.Services.Devices
System.Console.WriteLine($"Send request ! 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<AqaraMotion>(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<AqaraMotion>(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)

View File

@ -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<Zigbee2MqttDevice> zigbee2MqttDevices = MqttClientService.devices;
devices = await CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider);
List<Zigbee2MqttDeviceNew> zigbee2MqttDevices = MqttClientService.devicesNew;
devices = await CreateFromZigbeeNewAsync(_DeviceDatabaseService, _ProviderDatabaseService, _LocationDatabaseService, userId, zigbee2MqttDevices, provider);
/*List<Zigbee2MqttDevice> 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<Dictionary<string, List<DeviceDetailDTO>>> CreateFromZigbeeNewAsync(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, LocationDatabaseService _LocationDatabaseService, string userId, List<Zigbee2MqttDeviceNew> zigbee2MqttDevices, Provider provider)
{
List<DeviceDetailDTO> createdZigbeeDevices = new List<DeviceDetailDTO>();
List<DeviceDetailDTO> notSupportedZigbeeDevices = new List<DeviceDetailDTO>();
List<Device> 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<MeansOfCommunication>();
deviceDetailDTO.MeansOfCommunications.Add(MeansOfCommunication.Zigbee);
deviceDetailDTO.CreatedDate = DateTime.Now;
deviceDetailDTO.UpdatedDate = DateTime.Now;
if (zigbee2MqttDevice.type != "Coordinator")
{
// EXPOSES !
List<string> supportedOperationsDTO = new List<string>();
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<string, List<DeviceDetailDTO>>() {
{ "createdDevices", createdZigbeeDevices },
{ "notSupportedDevices", notSupportedZigbeeDevices }
};
}
private static DeviceType GetDeviceTypeFromZigbeeModel(string zigbeeModel)
{
return supportedDevices.Any(sd => sd.Model == zigbeeModel) ?

View File

@ -63,7 +63,7 @@ namespace MyCore.Service.Services
group = new Group();
group.CreatedDate = DateTime.Now;
group.DevicesIds = new List<string>();
group.ServiceIdentification = zigbee2MqttGroup.id;
group.UserId = userId;
group.Name = zigbee2MqttGroup.friendly_name;
group.Type = "zigbee2mqtt";

View File

@ -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);