From c694f4a9aaf04e50bfdfdaabe4c0d18d1bf16c28 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 24 Mar 2021 17:40:41 +0100 Subject: [PATCH] MC Clean code + action on yeelight (from zigbee) --- MyCore/Services/Devices/ActionService.cs | 526 +++++++++++------- .../SupportedDevices/YeelightService.cs | 23 + 2 files changed, 337 insertions(+), 212 deletions(-) diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 3d527d0..e3a2d1a 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -89,45 +89,8 @@ namespace MyCore.Services.Devices // Correct trigger ! System.Console.WriteLine($"Correct trigger for automation {automation.Name}"); - var isConditionsRespected = automation.Conditions.Count <= 0 ? new List() { true } : new List(new bool[automation.Conditions.Count]); - var i = 0; - foreach (var condition in automation.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(condition.StartTime); - TimeSpan end = JsonConvert.DeserializeObject(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++; - } - + var isConditionsRespected = CheckConditions(automation.Conditions); + if (!isConditionsRespected.Any(cr => !cr)) { System.Console.WriteLine("Conditions respected !"); @@ -176,195 +139,32 @@ namespace MyCore.Services.Devices switch (providerAction.Type) { case "zigbee2mqtt": - var request = ""; - var buildRequest = new Dictionary(); - - System.Console.WriteLine($"zigbee2mqtt type !"); - - var actionDeviceExpose = GetDeviceExposes(actionDeviceToTest); - - // Get device last state - var dic = new Dictionary(); - if (actionDeviceToTest.LastState != null) + try { - dic = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); + ActionOnZigbee2Mqtt(actionDeviceToTest, action, DeviceNameForAction); + } catch (Exception ex) { + System.Console.WriteLine($"ActionOnZigbee2Mqtt result in error: {ex}"); } - - try - { - - switch (actionDeviceToTest.Type) - { - case DeviceType.Light: - case DeviceType.Switch: - var actionDeviceExposeForFeatures = actionDeviceExpose.Where(e => e.type == ((DeviceType)actionDeviceToTest.Type).ToString().ToLower()).FirstOrDefault(); - if (actionDeviceExposeForFeatures != null) - { - foreach (var state in action.States) - { - System.Console.WriteLine($"Check Action in light|switch ! {state.Name} {state.Value}"); - if (actionDeviceExposeForFeatures.features.Any(ade => ade.name == state.Name)) - { - if (dic.Count > 0) - { - if (dic["state"].ToString().ToLower() == state.Value.ToLower()) - { - throw new Exception($"Action device is already at the good state : {state.Name} {state.Value}"); - } - } - - // TODO clean this - if (state.Name == "brightness") - { - try - { - int brightness = int.Parse(state.Value); - // Add to request - buildRequest.Add(state.Name, brightness); - } - catch (Exception ex) - { - System.Console.WriteLine($"zigbee - Parse to int error {ex}"); - } - } - else - { - // Add to request - buildRequest.Add(state.Name, state.Value); - } - } - else - { - throw new Exception($"Action device light|switch does not have expose of type {state.Name}"); - } - } - } - else - throw new Exception("Action device does not have expose of type light|switch"); - break; - default: - foreach (var state in action.States) - { - System.Console.WriteLine($"Check Action ! {state.Name} {state.Value}"); - if (!actionDeviceExpose.Any(ade => ade.name == state.Name)) - { - throw new Exception($"Action device does not have expose of type {state.Name}"); - } - - } - break; - } - - request = JsonConvert.SerializeObject(buildRequest); - - // SEND REQUEST - var requestToSend = $"Send request ! zigbee2mqtt/{DeviceNameForAction}/set/{request}"; - System.Console.WriteLine($"Send request ! zigbee2mqtt/{DeviceNameForAction}/set/{request}"); - - MqttClientService.PublishMessage("zigbee2mqtt/" + DeviceNameForAction + "/set", request); - - } - catch (Exception ex) - { - System.Console.WriteLine($"Not a valid action !"); - } - break; case "meross": try { - var merossDevice = _DeviceDatabaseService.GetById(actionDeviceToTest.Id); - - if (merossDevice != null) - { - //As multisocket channel 0 is all the sockets, skip 0 - - // two state = first state = ON/OFF, second var = switch channels number (LIST) // TODO better check - if (action.States.Count >= 2 && action.States.Any(s => s.Name.ToLower() == "state" && (s.Value.ToLower() == "on" || s.Value.ToLower() == "off" || s.Value.ToLower() == "toggle"))) - { - var state = action.States.Where(s => s.Name.ToLower() == "state").FirstOrDefault(); - var channels = action.States.Where(s => s.Name.ToLower() == "channels").FirstOrDefault(); - var plugIds = JsonConvert.DeserializeObject>(channels.Value); - - List lastStatePlug = new List(); // Off by default - // => TODO GET ALL POSSIBILITIES FROM DEVICE (number of channels) - - if (actionDeviceToTest.LastState != null) // Get last state of device - { - try - { - lastStatePlug = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); - } - catch (Exception ex) - { - System.Console.WriteLine($"Meross error: Last state ! - {ex}"); - } - } - else - { - // By default set to OFF - foreach (var plug in plugIds) - { - lastStatePlug.Add(ToggleStatus.OFF); - } - } - - List statuses = new List(); - var p = 0; - foreach (var plugId in plugIds) - { - ToggleStatus status = new ToggleStatus(); - // check if device, check if multiprise - switch (merossDevice.Type) - { - case DeviceType.Multiplug: - case DeviceType.Plug: - if (state.Value.ToLower() == "toggle") - { - status = lastStatePlug[p] == ToggleStatus.OFF ? ToggleStatus.ON : ToggleStatus.OFF; - } - else - { - status = state.Value.ToLower() == "on" ? ToggleStatus.ON : ToggleStatus.OFF; - } - break; - } - // Send request - MqttClientMerossService.ExecuteCommand(actionDeviceToTest.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", status, plugId); - statuses.Add(status); - p++; - } - - // Update Last state - actionDeviceToTest.LastState = JsonConvert.SerializeObject(statuses); - actionDeviceToTest.LastStateDate = DateTime.Now; - _DeviceDatabaseService.Update(actionDeviceToTest); - } - else - { - throw new Exception("Meross: Request action invalid"); - } - // TODO UPDATE STATE - } + ActionOnMeross(_DeviceDatabaseService, actionDeviceToTest, action, DeviceNameForAction); } catch (Exception ex) { - System.Console.WriteLine($"Meross error: {ex}"); + System.Console.WriteLine($"ActionOnMeross result in error: {ex}"); } break; case "yeelight": - - if (YeelightService.devices.Count <= 0) + try { - await YeelightService.GetDevices(); + ActionOnYeelight(_DeviceDatabaseService, actionDeviceToTest, action); } - var labLamp = YeelightService.devices.Where(d => d.Hostname == actionDeviceToTest.IpAddress).FirstOrDefault(); - if (labLamp != null) + catch (Exception ex) { - // Check action type.. - Task.Run(async () => { await YeelightService.Toggle(labLamp); }); + System.Console.WriteLine($"ActionOnYeelight result in error: {ex}"); } - break; } } @@ -407,6 +207,308 @@ namespace MyCore.Services.Devices } } + private async static void ActionOnYeelight(DeviceDatabaseService deviceDatabaseService, Device actionDeviceToTest, Interfaces.Models.Action action) + { + if (YeelightService.devices.Count <= 0) + { + await YeelightService.GetDevices(); + } + var lamp = YeelightService.devices.Where(d => d.Hostname == actionDeviceToTest.IpAddress).FirstOrDefault(); + if (lamp != null) + { + System.Console.WriteLine($"yellight type !"); + + // Get device last state + var dic = new List(); + if (actionDeviceToTest.LastState != null) + { + dic = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); + } + + try + { + + switch (actionDeviceToTest.Type) + { + case DeviceType.Light: + var states = action.States.Where(s => s.Name == "state" || s.Name == "brightness").ToList(); + + if (states.Count >= 0) + { + if (dic.Count > 0) + { + /*if (dic["state"].ToString().ToLower() == state.Value.ToLower() && action.States.Count <= 1) // workaround if brightness not the same + { + throw new Exception($"Action device is already at the good state : {state.Name} {state.Value}"); + }*/ + } + + foreach (var state in states) + { + switch (state.Name) + { + case "state": + switch (state.Value.ToLower()) + { + case "on": + Task.Run(async () => { await YeelightService.TurnOn(lamp, null); }); + break; + case "off": + Task.Run(async () => { await YeelightService.TurnOff(lamp); }); + break; + case "toggle": + Task.Run(async () => { await YeelightService.Toggle(lamp); }); + break; + } + break; + case "brightness": + var brightness = int.Parse(state.Value); + Task.Run(async () => { await YeelightService.TurnOn(lamp, brightness); }); + break; + } + } + + // Update Last state + actionDeviceToTest.LastState = JsonConvert.SerializeObject(states); + actionDeviceToTest.LastStateDate = DateTime.Now; + deviceDatabaseService.Update(actionDeviceToTest); + } + else + { + throw new Exception($"Action device light in yeelight does not have correct expose"); + } + break; + default: + break; + } + } + catch (Exception ex) + { + System.Console.WriteLine($"Not a valid action !"); + } + } + else + { + System.Console.WriteLine($"Yeelight - Lamp offline! - {actionDeviceToTest.Name}"); + } + } + + private static void ActionOnMeross(DeviceDatabaseService _DeviceDatabaseService, Device actionDeviceToTest, Interfaces.Models.Action action, string deviceNameForAction) + { + var merossDevice = _DeviceDatabaseService.GetById(actionDeviceToTest.Id); + + if (merossDevice != null) + { + //As multisocket channel 0 is all the sockets, skip 0 + + // two state = first state = ON/OFF, second var = switch channels number (LIST) // TODO better check + if (action.States.Count >= 2 && action.States.Any(s => s.Name.ToLower() == "state" && (s.Value.ToLower() == "on" || s.Value.ToLower() == "off" || s.Value.ToLower() == "toggle"))) + { + var state = action.States.Where(s => s.Name.ToLower() == "state").FirstOrDefault(); + var channels = action.States.Where(s => s.Name.ToLower() == "channels").FirstOrDefault(); + var plugIds = JsonConvert.DeserializeObject>(channels.Value); + + List lastStatePlug = new List(); // Off by default + // => TODO GET ALL POSSIBILITIES FROM DEVICE (number of channels) + + if (actionDeviceToTest.LastState != null) // Get last state of device + { + try + { + lastStatePlug = JsonConvert.DeserializeObject>(actionDeviceToTest.LastState); + } + catch (Exception ex) + { + System.Console.WriteLine($"Meross error: Last state ! - {ex}"); + } + } + else + { + // By default set to OFF + foreach (var plug in plugIds) + { + lastStatePlug.Add(ToggleStatus.OFF); + } + } + + List statuses = new List(); + var p = 0; + foreach (var plugId in plugIds) + { + ToggleStatus status = new ToggleStatus(); + // check if device, check if multiprise + switch (merossDevice.Type) + { + case DeviceType.Multiplug: + case DeviceType.Plug: + if (state.Value.ToLower() == "toggle") + { + status = lastStatePlug[p] == ToggleStatus.OFF ? ToggleStatus.ON : ToggleStatus.OFF; + } + else + { + status = state.Value.ToLower() == "on" ? ToggleStatus.ON : ToggleStatus.OFF; + } + break; + } + // Send request + MqttClientMerossService.ExecuteCommand(actionDeviceToTest.ServiceIdentification, Method.SET, CommandMqtt.TOGGLEX, "", "", status, plugId); + statuses.Add(status); + p++; + } + + // Update Last state + actionDeviceToTest.LastState = JsonConvert.SerializeObject(statuses); + actionDeviceToTest.LastStateDate = DateTime.Now; + _DeviceDatabaseService.Update(actionDeviceToTest); + } + else + { + throw new Exception("Meross: Request action invalid"); + } + } + } + + private static void ActionOnZigbee2Mqtt(Device device, Interfaces.Models.Action action, string deviceNameForAction) + { + var request = ""; + var buildRequest = new Dictionary(); + + System.Console.WriteLine($"zigbee2mqtt type !"); + + var actionDeviceExpose = GetDeviceExposes(device); + + // Get device last state + var dic = new Dictionary(); + if (device.LastState != null) + { + dic = JsonConvert.DeserializeObject>(device.LastState); + } + + try + { + + switch (device.Type) + { + case DeviceType.Light: + case DeviceType.Switch: + var actionDeviceExposeForFeatures = actionDeviceExpose.Where(e => e.type == ((DeviceType)device.Type).ToString().ToLower()).FirstOrDefault(); + if (actionDeviceExposeForFeatures != null) + { + foreach (var state in action.States) + { + System.Console.WriteLine($"Check Action in light|switch ! {state.Name} {state.Value}"); + if (actionDeviceExposeForFeatures.features.Any(ade => ade.name == state.Name)) + { + if (dic.Count > 0) + { + if (dic["state"].ToString().ToLower() == state.Value.ToLower() && action.States.Count <= 1) // workaround if brightness not the same + { + throw new Exception($"Action device is already at the good state : {state.Name} {state.Value}"); + } + } + + // TODO clean this + if (state.Name == "brightness") + { + try + { + int brightness = int.Parse(state.Value); + // Add to request + buildRequest.Add(state.Name, brightness); + } + catch (Exception ex) + { + System.Console.WriteLine($"zigbee - Parse to int error {ex}"); + } + } + else + { + // Add to request + buildRequest.Add(state.Name, state.Value); + } + } + else + { + throw new Exception($"Action device light|switch does not have expose of type {state.Name}"); + } + } + } + else + throw new Exception("Action device does not have expose of type light|switch"); + break; + default: + foreach (var state in action.States) + { + System.Console.WriteLine($"Check Action ! {state.Name} {state.Value}"); + if (!actionDeviceExpose.Any(ade => ade.name == state.Name)) + { + throw new Exception($"Action device does not have expose of type {state.Name}"); + } + + } + break; + } + + request = JsonConvert.SerializeObject(buildRequest); + + // SEND REQUEST + var requestToSend = $"Send request ! zigbee2mqtt/{deviceNameForAction}/set/{request}"; + System.Console.WriteLine($"Send request ! zigbee2mqtt/{deviceNameForAction}/set/{request}"); + + MqttClientService.PublishMessage("zigbee2mqtt/" + deviceNameForAction + "/set", request); + + } + catch (Exception ex) + { + System.Console.WriteLine($"Not a valid action !"); + } + } + + private static List CheckConditions(List conditions) + { + var isConditionsRespected = conditions.Count <= 0 ? new List() { true } : new List(new bool[conditions.Count]); + var i = 0; + foreach (var condition in 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(condition.StartTime); + TimeSpan end = JsonConvert.DeserializeObject(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++; + } + + return isConditionsRespected; + } + private static List GetDeviceExposes(Device device) { List exposes = new List(); diff --git a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs index 7decad3..26478be 100644 --- a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs +++ b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs @@ -21,5 +21,28 @@ namespace MyCore.Services await device.Connect(); return await device.Toggle(); } + + public static async Task TurnOff(Device device) + { + await device.Connect(); + return await device.TurnOff(); + } + + public static async Task TurnOn(Device device, int? brightness) + { + await device.Connect(); + if (brightness != null) + { + if (brightness >= 0 && brightness <= 255) + { + await device.TurnOn(); + return await device.SetBrightness((int)brightness); + } + else return await device.TurnOn(); + } + else { + return await device.TurnOn(); + } + } } }