MC Add time condition

This commit is contained in:
Thomas Fransolet 2021-03-23 18:21:15 +01:00
parent dcf6ccef5a
commit a4f3748649
2 changed files with 174 additions and 108 deletions

View File

@ -79,26 +79,40 @@ namespace MyCore.Interfaces.Models
public string DeviceId { get; set; } public string DeviceId { get; set; }
public string StateName { get; set; } public string StateName { get; set; }
public string StateValue { get; set; } public string StateValue { get; set; }
public TriggerType type { get; set; }
}
public enum Type public enum TriggerType
{ {
MQTT, MQTT,
WEB, WEB,
TIME TIME
}
} }
public class Condition public class Condition
{ {
public string DeviceId { get; set; } public string DeviceId { get; set; }
public string StateName { get; set; } public State State { get; set; }
public string StateValue { get; set; } public string StartTime { get; set; } // TIMESPAN JSON SERIALIZED
public string EndTime { get; set; } // TIMESPAN JSON SERIALIZED
public ConditionType Type { get; set; }
public ConditionValue Value { get; set; }
}
public enum Type public enum ConditionType
{ {
STATE, STATE,
HOUR TIME
} }
public enum ConditionValue
{
EQUAL, // ==
NOT_EQUAL, // !=
BIGGER, // >
BIGGEST, // >=
SMALLER, // <
SMALLEST, // <=
} }
public class Action public class Action
@ -108,7 +122,7 @@ namespace MyCore.Interfaces.Models
public List<State> States { get; set; } public List<State> States { get; set; }
public string RawRequest { get; set; } // http, mqtt public string RawRequest { get; set; } // http, mqtt
public string ProviderId { get; set; } public string ProviderId { get; set; }
public Type Type { get; set; } public ActionType Type { get; set; }
} }
public class State public class State
@ -117,7 +131,7 @@ namespace MyCore.Interfaces.Models
public string Value { get; set; } // example : ON public string Value { get; set; } // example : ON
} }
public enum Type public enum ActionType
{ {
DELAY, DELAY,
DEVICE, DEVICE,

View File

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