diff --git a/MyCore.Interfaces/DTO/MyControlPanel/AlarmModeDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/AlarmModeDTO.cs index 642d560..0ecc1cd 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/AlarmModeDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/AlarmModeDTO.cs @@ -1,7 +1,9 @@ -using System; +using MyCore.Interfaces.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static MyCore.Interfaces.Models.AlarmMode; namespace MyCore.Interfaces.DTO { @@ -13,9 +15,27 @@ namespace MyCore.Interfaces.DTO public string Id { get; set; } public string HomeId { get; set; } public string Name { get; set; } + public AlarmType Type { get; set; } + public bool Activated { get; set; } public bool IsDefault { get; set; } + public bool Notification { get; set; } public DateTime CreatedDate { get; set; } public DateTime UpdatedDate { get; set; } - public List DevicesIds { get; set; } // Check if ok + } + + public class AlarmModeDetailDTO : AlarmModeDTO + { + public List Triggers { get; set; } + public List Devices { get; set; } + public ProgrammedMode ProgrammedMode { get; set; } + public GeolocalizedMode GeolocalizedMode { get; set; } + } + + public class AlarmModeCreateOrUpdateDetailDTO : AlarmModeDTO + { + public List Triggers { get; set; } + public List Actions { get; set; } + public ProgrammedMode ProgrammedMode { get; set; } + public GeolocalizedMode GeolocalizedMode { get; set; } } } diff --git a/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs index 51af82e..a8e93f6 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/AutomationDTO.cs @@ -23,12 +23,4 @@ namespace MyCore.Interfaces.DTO public List Actions { get; set; } public List DevicesIds { get; set; } } - - public class AutomationCreateOrUpdateDetailDTO : AutomationDTO - { - public List Triggers { get; set; } - public List Conditions { get; set; } - public List Actions { get; set; } - public List DeviceIds { get; set; } - } } diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/AlarmMode.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/AlarmMode.cs index ec6d5c6..2d4f41c 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/AlarmMode.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/AlarmMode.cs @@ -25,19 +25,52 @@ namespace MyCore.Interfaces.Models [BsonRequired] public string Name { get; set; } + [BsonElement("Activated")] + [BsonRequired] + public bool Activated { get; set; } + [BsonElement("IsDefault")] [BsonRequired] public bool IsDefault { get; set; } + [BsonElement("Notification")] + [BsonRequired] + public bool Notification { get; set; } + [BsonElement("CreatedDate")] public DateTime CreatedDate { get; set; } [BsonElement("UpdatedDate")] public DateTime UpdatedDate { get; set; } + [BsonElement("Type")] + public AlarmType Type { get; set; } + + [BsonElement("ProgrammedModes")] + public ProgrammedMode ProgrammedMode { get; set; } + + [BsonElement("GeolocalizedMode")] + public GeolocalizedMode GeolocalizedMode { get; set; } + + [BsonElement("Triggers")] + public List Triggers { get; set; } + + [BsonElement("Actions")] + public List Actions { get; set; } + [BsonElement("DevicesIds")] public List DevicesIds { get; set; } + public enum AlarmType + { + Home, + Absent, + Geolocalized, + Programmed, + Desarmed, + Custom + } + public AlarmModeDTO ToDTO() { return new AlarmModeDTO() @@ -45,11 +78,58 @@ namespace MyCore.Interfaces.Models Id = Id, HomeId = HomeId, Name = Name, + Type = Type, + Activated = Activated, + IsDefault = IsDefault, + Notification = Notification, + CreatedDate = CreatedDate, + UpdatedDate = UpdatedDate, + }; + } + + public AlarmModeDetailDTO ToDetailDTO(List devicesDetail) + { + return new AlarmModeDetailDTO() + { + Id = Id, + HomeId = HomeId, + Name = Name, + Type = Type, + Activated = Activated, IsDefault = IsDefault, CreatedDate = CreatedDate, UpdatedDate = UpdatedDate, - DevicesIds = DevicesIds, + Triggers = Triggers, + Devices = devicesDetail, + ProgrammedMode = ProgrammedMode, + GeolocalizedMode = GeolocalizedMode }; } } + + public class ProgrammedMode + { + public List Monday { get; set; } + public List Tuesday { get; set; } + public List Wednesday { get; set; } + public List Thursday { get; set; } + public List Friday { get; set; } + public List Saturday { get; set; } + public List Sunday { get; set; } + } + + public class TimePeriodAlarm + { + public string Start { get; set; } + public string End { get; set; } + public AlarmMode AlarmMode { get; set; } +} + + public class GeolocalizedMode + { + public string Latitude { get; set; } + public string Longitude { get; set; } + public AlarmMode HomeMode { get; set; } + public AlarmMode AbsentMode { get; set; } + } } diff --git a/MyCore/Controllers/AlarmController.cs b/MyCore/Controllers/AlarmController.cs index 250a110..0804331 100644 --- a/MyCore/Controllers/AlarmController.cs +++ b/MyCore/Controllers/AlarmController.cs @@ -11,6 +11,7 @@ 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; @@ -25,16 +26,323 @@ namespace MyCore.Service.Controllers private UserDatabaseService _UserDatabaseService; private RoomDatabaseService _RoomDatabaseService; private DeviceDatabaseService _DeviceDatabaseService; + private AlarmDatabaseService _AlarmDatabaseService; + private HomeDatabaseService _HomeDatabaseService; private readonly IMqttClientService _mqttClientService; //private readonly IMqttOnlineClientService _mqttOnlineClientService; - public AlarmController(UserDatabaseService userDatabaseService, RoomDatabaseService roomDatabaseService, DeviceDatabaseService deviceDatabaseService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) + public AlarmController(UserDatabaseService userDatabaseService, RoomDatabaseService roomDatabaseService, DeviceDatabaseService deviceDatabaseService, AlarmDatabaseService alarmDatabaseService, HomeDatabaseService homeDatabaseService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) { this._UserDatabaseService = userDatabaseService; this._RoomDatabaseService = roomDatabaseService; this._DeviceDatabaseService = deviceDatabaseService; + this._AlarmDatabaseService = alarmDatabaseService; + this._HomeDatabaseService = homeDatabaseService; this._mqttClientService = provider.MqttClientService; //this._mqttOnlineClientService = onlineProvider.MqttOnlineClientService; } + + /// + /// Get all alarm modes for the specified home + /// + /// Home Id + [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(string), 500)] + [HttpGet("{homeId}")] + public ObjectResult GetAll(string homeId) + { + try + { + List alarmModes = _AlarmDatabaseService.GetAll(homeId); + + List alarmModesDTO = alarmModes.Select(a => a.ToDTO()).ToList(); + + return new OkObjectResult(alarmModesDTO); + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Get detail info of a specified alarm mode + /// + /// alarm id + [ProducesResponseType(typeof(AlarmModeDetailDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpGet("detail/{alarmId}")] + public ObjectResult GetDetail(string alarmModeId) + { + try + { + if (alarmModeId == null) + throw new ArgumentNullException("Incorrect parameters"); + + AlarmMode alarmMode = _AlarmDatabaseService.GetById(alarmModeId); + if (alarmMode == null) + throw new KeyNotFoundException("Alarm does not exist"); + + List devices = new List(); + foreach (var deviceId in alarmMode.DevicesIds) { + Device device = _DeviceDatabaseService.GetById(deviceId); + if (device != null) + { + devices.Add(device); + } + } + + return new OkObjectResult(alarmMode.ToDetailDTO(devices.Select(d => d.ToDTO()).ToList())); + + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create an alarm mode + /// + /// Alarm mode to create + [ProducesResponseType(typeof(AlarmModeDetailDTO), 200)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 500)] + [HttpPost] + public ObjectResult Create([FromBody] AlarmModeCreateOrUpdateDetailDTO alarmModeCreateOrUpdateDetailDTO) + { + try + { + if (alarmModeCreateOrUpdateDetailDTO == null) + throw new ArgumentNullException("Incorrect parameters"); + + AlarmModeDetailDTO alarmModeCreated = AlarmService.CreateOrUpdate(this._AlarmDatabaseService, this._DeviceDatabaseService, alarmModeCreateOrUpdateDetailDTO.HomeId, alarmModeCreateOrUpdateDetailDTO, true); + + return new OkObjectResult(alarmModeCreated); + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Create default alarm modes + /// + /// Home Id + [ProducesResponseType(typeof(bool), 202)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 409)] + [ProducesResponseType(typeof(string), 500)] + [HttpPost("defaults/{homeId}")] + public ObjectResult CreateDefaultAlarms (string homeId) + { + try + { + if (homeId == null) + throw new ArgumentNullException("Incorrect parameters"); + + Home home = _HomeDatabaseService.GetById(homeId); + if (home == null) + throw new KeyNotFoundException("Home does not exist"); + + List alarmModes = _AlarmDatabaseService.GetAll(homeId); + if (alarmModes.Where(a => a.IsDefault).Count() == 5) + throw new SystemException("Default modes already existing"); + + return new ObjectResult(AlarmService.CreateDefaultAlarmModes(_HomeDatabaseService, _AlarmDatabaseService, home)) { StatusCode = 202 }; + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (SystemException ex) + { + return new ObjectResult(ex.Message) { StatusCode = 409 }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Activate current alarm mode + /// + /// Alarm mode to activate + [ProducesResponseType(typeof(string), 202)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 409)] + [ProducesResponseType(typeof(string), 500)] + [HttpPost("activate/{alarmModeId}")] + public ObjectResult Activate(string alarmModeId) + { + try + { + if (alarmModeId == null) + throw new ArgumentNullException("Incorrect parameters"); + + AlarmMode alarmMode = _AlarmDatabaseService.GetById(alarmModeId); + if (alarmMode == null) + throw new KeyNotFoundException("Alarm mode does not exist"); + + if (alarmMode.Activated) + throw new AccessViolationException("This alarm mode is already activated"); + + bool successfull = AlarmService.Activate(_AlarmDatabaseService, alarmMode, _HomeDatabaseService); + + return new OkObjectResult($"Alarm mode {alarmMode.Name} has been successfully activated") { StatusCode = 202 }; + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (AccessViolationException ex) + { + return new ObjectResult(ex.Message) { StatusCode = 409 }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Update an alarm mode + /// + /// alarm mode to update + [ProducesResponseType(typeof(AlarmModeDetailDTO), 200)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPut] + public ObjectResult Update([FromBody] AlarmModeCreateOrUpdateDetailDTO alarmModeCreateOrUpdateDetailDTO) + { + try + { + if (!_AlarmDatabaseService.IsExist(alarmModeCreateOrUpdateDetailDTO.Id)) + throw new KeyNotFoundException("This alarm mode does not exist"); + + AlarmModeDetailDTO alarmModeUpdated = AlarmService.CreateOrUpdate(this._AlarmDatabaseService, this._DeviceDatabaseService, alarmModeCreateOrUpdateDetailDTO.HomeId, alarmModeCreateOrUpdateDetailDTO, false); + + return new OkObjectResult(alarmModeUpdated); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete an alarm mode + /// + /// Id of alarm mode to delete + [ProducesResponseType(typeof(string), 202)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 405)] + [ProducesResponseType(typeof(string), 500)] + [HttpDelete("{alarmModeId}")] + public ObjectResult Delete(string alarmModeId) + { + try + { + if (alarmModeId == null) + throw new ArgumentNullException("Incorrect parameters"); + + if (!_AlarmDatabaseService.IsExist(alarmModeId)) + throw new KeyNotFoundException("This alarm mode does not exist"); + + AlarmMode alarmMode = _AlarmDatabaseService.GetById(alarmModeId); + + if (alarmMode.IsDefault) + throw new NotSupportedException("This alarm mode cannot be deleted"); + + // Delete alarm + _AlarmDatabaseService.Remove(alarmModeId); + + return new OkObjectResult("Alarm mode has been successfully deleted") { StatusCode = 202 }; + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (NotSupportedException ex) + { + return new ObjectResult(ex.Message) { StatusCode = 405 }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + + /// + /// Delete all alarm mode for a specified home + /// + /// Home Id + [ProducesResponseType(typeof(string), 202)] + [ProducesResponseType(typeof(string), 400)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpDelete("home/{homeId}")] + public ObjectResult DeleteAllForHome(string homeId) + { + try + { + if (homeId == null) + throw new ArgumentNullException("Incorrect parameters"); + + if (!_HomeDatabaseService.IsExist(homeId)) + throw new KeyNotFoundException("Home does not exist"); + + _AlarmDatabaseService.RemoveForHome(homeId); + + return new OkObjectResult("All alarm mode associated to specified home has been removed") { StatusCode = 202 }; + } + catch (ArgumentNullException ex) + { + return new BadRequestObjectResult(ex.Message) { }; + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } } } diff --git a/MyCore/Controllers/AutomationController.cs b/MyCore/Controllers/AutomationController.cs index 5578d89..aa622af 100644 --- a/MyCore/Controllers/AutomationController.cs +++ b/MyCore/Controllers/AutomationController.cs @@ -96,23 +96,23 @@ namespace MyCore.Service.Controllers } } - + /// /// Create an automation /// - /// Automation to create + /// Automation to create [ProducesResponseType(typeof(AutomationDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 500)] [HttpPost] - public ObjectResult Create([FromBody] AutomationCreateOrUpdateDetailDTO automationCreateOrUpdateDetail) + public ObjectResult Create([FromBody] AutomationDetailDTO automationDetailDTO) { try { - if (automationCreateOrUpdateDetail == null) + if (automationDetailDTO == null) throw new ArgumentNullException("Incorrect parameters"); - AutomationDTO automationCreated = AutomationService.CreateOrUpdate(this._AutomationDatabaseService, automationCreateOrUpdateDetail.HomeId, automationCreateOrUpdateDetail, true); + AutomationDTO automationCreated = AutomationService.CreateOrUpdate(this._AutomationDatabaseService, automationDetailDTO.HomeId, automationDetailDTO, true); return new OkObjectResult(automationCreated); } @@ -130,23 +130,23 @@ namespace MyCore.Service.Controllers /// /// Update an automation /// - /// automation to update - [ProducesResponseType(typeof(AutomationCreateOrUpdateDetailDTO), 200)] + /// automation to update + [ProducesResponseType(typeof(AutomationDetailDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPut] - public ObjectResult Update([FromBody] AutomationCreateOrUpdateDetailDTO automationCreateOrUpdateDetail) + public ObjectResult Update([FromBody] AutomationDetailDTO automationDetailDTO) { try { - if (automationCreateOrUpdateDetail == null) + if (automationDetailDTO == null) throw new ArgumentNullException("Incorrect parameters"); - if (!_AutomationDatabaseService.IsExist(automationCreateOrUpdateDetail.Id)) + if (!_AutomationDatabaseService.IsExist(automationDetailDTO.Id)) throw new KeyNotFoundException("Automation does not exist"); - AutomationDTO automationUpdated = AutomationService.CreateOrUpdate(this._AutomationDatabaseService, automationCreateOrUpdateDetail.HomeId, automationCreateOrUpdateDetail, false); + AutomationDTO automationUpdated = AutomationService.CreateOrUpdate(this._AutomationDatabaseService, automationDetailDTO.HomeId, automationDetailDTO, false); return new OkObjectResult(automationUpdated); } diff --git a/MyCore/Controllers/GroupController.cs b/MyCore/Controllers/GroupController.cs index 93113fa..8966c90 100644 --- a/MyCore/Controllers/GroupController.cs +++ b/MyCore/Controllers/GroupController.cs @@ -91,7 +91,7 @@ namespace MyCore.Service.Controllers if (group == null) throw new KeyNotFoundException("Group not found"); - List devices = _DeviceDatabaseService.GetByLocation(group.HomeId, groupId); + List devices = _DeviceDatabaseService.GetByRoom(group.HomeId, groupId); return new OkObjectResult(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); @@ -360,7 +360,7 @@ namespace MyCore.Service.Controllers Group group = _GroupDatabaseService.GetById(groupId); // Delete group from all devices - List devices = _DeviceDatabaseService.GetByLocation(group.HomeId, groupId); + List devices = _DeviceDatabaseService.GetByRoom(group.HomeId, groupId); foreach (var device in devices) { device.GroupIds = device.GroupIds.Where(g => g != groupId).ToList(); diff --git a/MyCore/Controllers/Helpers/DevicesHelper.cs b/MyCore/Controllers/Helpers/DevicesHelper.cs new file mode 100644 index 0000000..9f7d0a0 --- /dev/null +++ b/MyCore/Controllers/Helpers/DevicesHelper.cs @@ -0,0 +1,386 @@ +using MyCore.Interfaces.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using MyCore.Interfaces.DTO; +using static Mqtt.Client.AspNetCore.Services.MqttClientMerossService; +using Mqtt.Client.AspNetCore.Services; +using MyCore.Services; + +namespace MyCore.Service.Controllers.Helpers +{ + public class DevicesHelper + { + #region Common methods + public static List GetDeviceExposes(Device device) + { + List exposes = new List(); + // Get Exposes for the zigbee device + foreach (var supportedOperation in device.SupportedOperations) + { + exposes.Add(JsonConvert.DeserializeObject(supportedOperation)); + } + return exposes; + } + + public static System.Type GetStateType(Expose expose) + { + switch (expose.type) + { + // e.g.: occupancy + case "binary": + if (expose.value_toggle == null) + { + // value are boolean + return true.GetType(); + } + else + { + // value are string + return "".GetType(); + } + case "numeric": + return new long().GetType(); + case "enum": + return expose.values.GetType(); + break; + /*case "text": + break; + case "light": + break; + case "composite": + break; + case "switch": + break;*/ + /*case "fan": + break; + case "cover": + break; + case "lock": + break; + case "climate": + break;*/ + default: + return null; + } + } + #endregion + #region ACTIONS ! + public 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($"Yeelight 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}"); + } + } + + public 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"); + } + } + } + + public static void ActionOnZigbee2Mqtt(Device device, Interfaces.Models.Action action, string deviceNameForAction) + { + var request = ""; + var buildRequest = new Dictionary(); + + System.Console.WriteLine($"zigbee2mqtt type !"); + + var actionDeviceExpose = DevicesHelper.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)) + { + // Comment this for test (ensure the request will be sent) + /*if (dic.Count > 0) + { + //if (device.Type == DeviceType.Light) {} + var test = dic["state"].ToString().ToLower(); + var test0 = state.Value.ToLower(); + if (dic["state"].ToString().ToLower() == state.Value.ToLower() && action.States.Count <= 1) // workaround if brightness not the same => For switch or light without brightness + { + throw new Exception($"Action device is already at the good state : {state.Name} {state.Value}"); + } + }*/ + + 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}"); + } + } + + // If any action ask for state update, check if device is already at the asked state + if (action.States.Any(s => s.Name == "state") && !action.IsForce) + { + bool sendRequest = true; + + // If action doesn't ask for brightness just test if device is already at the asked state + if (action.States.Any(s => s.Name == "brightness")) + { + // Check state and brightness + // if we got state value (current value) + if (dic.Any(d => d.Key == "state")) + { + var currentValue = dic["state"].ToString().ToLower(); + var askedValue = action.States.FirstOrDefault(s => s.Name == "state").Value.ToLower(); + if (currentValue == askedValue) + { + // Check brightness difference + if (dic.Any(d => d.Key == "brightness")) + { + try + { + var currentValueBrightness = int.Parse(dic["brightness"].ToString().ToLower()); + var askedValueBrightness = int.Parse(action.States.FirstOrDefault(s => s.Name == "brightness").Value.ToLower()); + if (Math.Abs(currentValueBrightness - askedValueBrightness) <= 5) // brightness diff is lower than 5 => don't change brightness + sendRequest = false; + else + sendRequest = true; + } + catch (Exception ex) + { + sendRequest = false; + System.Console.WriteLine($"int parse error in brightness check", ex.Message); + } + } + } + } + } + else + { + // Check only state value + // if we got state value (current value) + if (dic.Any(d => d.Key == "state")) + { + var currentValue = dic["state"].ToString().ToLower(); + var askedValue = action.States.FirstOrDefault(s => s.Name == "state").Value.ToLower(); + if (currentValue == askedValue) + sendRequest = false; + } + } + + if (!sendRequest) + throw new Exception($"Action device is already at the good state"); + } + } + 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 !"); + } + } + #endregion + } +} diff --git a/MyCore/Controllers/HomeController.cs b/MyCore/Controllers/HomeController.cs index d1f84ae..9219bcf 100644 --- a/MyCore/Controllers/HomeController.cs +++ b/MyCore/Controllers/HomeController.cs @@ -11,6 +11,7 @@ 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; @@ -153,7 +154,11 @@ namespace MyCore.Service.Controllers Home homeCreated = HomeService.CreateOrUpdate(this._HomeDatabaseService, this._AlarmDatabaseService, createOrUpdateHomeDTO, true); - foreach (var userId in createOrUpdateHomeDTO.UsersIds) + if (homeCreated.IsAlarm) + // Create default alarm modes + AlarmService.CreateDefaultAlarmModes(_HomeDatabaseService, _AlarmDatabaseService, homeCreated); // Create and by default select desarmed mode + + foreach (var userId in createOrUpdateHomeDTO.UsersIds) { UserInfo user = _UserDatabaseService.GetById(userId); if (user.HomeIds == null) @@ -228,7 +233,7 @@ namespace MyCore.Service.Controllers throw new KeyNotFoundException("Home does not exist"); Home home = _HomeDatabaseService.GetById(homeId); - // Check if something needs to be done.. (delete all devices related to home, all automations, all groups, etc) + // Check if something needs to be done.. (delete all devices related to home, all automations, all groups, all alarm modes, etc) // Delete home _HomeDatabaseService.Remove(homeId); diff --git a/MyCore/Controllers/RoomController.cs b/MyCore/Controllers/RoomController.cs index 14d324f..c3fbca7 100644 --- a/MyCore/Controllers/RoomController.cs +++ b/MyCore/Controllers/RoomController.cs @@ -79,7 +79,7 @@ namespace MyCore.Service.Controllers Room room = _RoomDatabaseService.GetById(roomId); if (room == null) throw new KeyNotFoundException("Room does not exist"); - List devices = _DeviceDatabaseService.GetByLocation(room.HomeId, roomId); + List devices = _DeviceDatabaseService.GetByRoom(room.HomeId, roomId); return new OkObjectResult(room.ToDTO(devices.Select(d => d.ToDTO()).ToList())); @@ -230,7 +230,7 @@ namespace MyCore.Service.Controllers Room room = _RoomDatabaseService.GetById(roomId); // Delete location from all devices - List devices = _DeviceDatabaseService.GetByLocation(room.HomeId, roomId); + List devices = _DeviceDatabaseService.GetByRoom(room.HomeId, roomId); foreach (var device in devices) { device.RoomId = null; diff --git a/MyCore/Extensions/MqttClientService.cs b/MyCore/Extensions/MqttClientService.cs index d7e22f2..60c6e2f 100644 --- a/MyCore/Extensions/MqttClientService.cs +++ b/MyCore/Extensions/MqttClientService.cs @@ -35,11 +35,12 @@ namespace Mqtt.Client.AspNetCore.Services private AutomationDatabaseService _automationDatabaseService; private ActionService _actionService; private HomeDatabaseService _homeDatabaseService; + private AlarmDatabaseService _alarmDatabaseService; public static string lastTopic; public static long lastTimeTopic; - public MqttClientService(IMqttClientOptions options, HomeDatabaseService homeDatabaseService, DeviceDatabaseService deviceDatabaseService, GroupDatabaseService groupDatabaseService, ProviderDatabaseService providerDatabaseService, RoomDatabaseService roomDatabaseService, ActionService actionService, AutomationDatabaseService automationDatabaseService) + public MqttClientService(IMqttClientOptions options, HomeDatabaseService homeDatabaseService, DeviceDatabaseService deviceDatabaseService, GroupDatabaseService groupDatabaseService, ProviderDatabaseService providerDatabaseService, RoomDatabaseService roomDatabaseService, ActionService actionService, AutomationDatabaseService automationDatabaseService, AlarmDatabaseService alarmDatabaseService) { this._homeDatabaseService = homeDatabaseService; this._deviceDatabaseService = deviceDatabaseService; @@ -47,6 +48,7 @@ namespace Mqtt.Client.AspNetCore.Services this._providerDatabaseService = providerDatabaseService; this._roomDatabaseService = roomDatabaseService; this._automationDatabaseService = automationDatabaseService; + this._alarmDatabaseService = alarmDatabaseService; this._actionService = actionService; Home home = this._homeDatabaseService.GetAll().ToList().Where(h => h.IsDefault).FirstOrDefault(); @@ -115,7 +117,7 @@ namespace Mqtt.Client.AspNetCore.Services { if (_actionService != null && homeId != null) { - ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _roomDatabaseService, _automationDatabaseService, homeId); + ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _roomDatabaseService, _automationDatabaseService, _alarmDatabaseService, homeId); } } else @@ -298,9 +300,7 @@ namespace Mqtt.Client.AspNetCore.Services if (zigbee2mqttProvider != null) { var groups = _GroupDatabaseService.GetByType(homeId, "zigbee2mqtt"); - // Compare the groups from MyCore and the group we received, if something diff in one group => Hard refresh (delete, new) - // TODO : Test with new devices !! => Id always change BUUUUUUG !! GroupService.CompareGroupsFromZigbee2Mqtt(homeId, groups, groupsConvert, _DeviceDatabaseService, _GroupDatabaseService); } } diff --git a/MyCore/Extensions/ServiceCollectionExtension.cs b/MyCore/Extensions/ServiceCollectionExtension.cs index 4a1ebf4..9e76a55 100644 --- a/MyCore/Extensions/ServiceCollectionExtension.cs +++ b/MyCore/Extensions/ServiceCollectionExtension.cs @@ -74,6 +74,7 @@ namespace MyCore.Service.Extensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(serviceProvider => diff --git a/MyCore/Services/AlarmService.cs b/MyCore/Services/AlarmService.cs new file mode 100644 index 0000000..5b9ad83 --- /dev/null +++ b/MyCore/Services/AlarmService.cs @@ -0,0 +1,332 @@ +using Mqtt.Client.AspNetCore.Services; +using MyCore.Interfaces.DTO; +using MyCore.Interfaces.Models; +using MyCore.Service.Controllers.Helpers; +using MyCore.Services; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using static MyCore.Interfaces.Models.AlarmMode; + +namespace MyCore.Service.Services +{ + public class AlarmService + { + #region DB - classic methods + public static AlarmModeDetailDTO CreateOrUpdate(AlarmDatabaseService _AlarmDatabaseService, DeviceDatabaseService _DeviceDatabaseService, string homeId, AlarmModeCreateOrUpdateDetailDTO alarmModeCreateOrUpdateDetailDTO, bool create) + { + AlarmMode alarmMode; + if (create) + { + alarmMode = new AlarmMode(); + alarmMode.CreatedDate = DateTime.Now; + } + else + alarmMode = _AlarmDatabaseService.GetById(alarmModeCreateOrUpdateDetailDTO.Id); + + alarmMode.HomeId = homeId; + alarmMode.Name = alarmModeCreateOrUpdateDetailDTO.Name; + alarmMode.UpdatedDate = DateTime.Now; + alarmMode.Type = alarmModeCreateOrUpdateDetailDTO.Type; + alarmMode.Notification = alarmModeCreateOrUpdateDetailDTO.Notification; + + switch (alarmMode.Type) + { + case AlarmType.Home: + case AlarmType.Absent: + case AlarmType.Custom: + alarmMode.Triggers = alarmModeCreateOrUpdateDetailDTO.Triggers; + alarmMode.Actions = alarmModeCreateOrUpdateDetailDTO.Actions; + break; + case AlarmType.Geolocalized: + // 1. Select GPS coord and zone + // 2. Which mode when absent + // 3. Which mode when present + alarmMode.GeolocalizedMode = alarmModeCreateOrUpdateDetailDTO.GeolocalizedMode; + break; + case AlarmType.Programmed: + // Define periods when specific mode are activated (existing ones) + alarmMode.ProgrammedMode = alarmModeCreateOrUpdateDetailDTO.ProgrammedMode; + break; + case AlarmType.Desarmed: + break; + } + + List devicesIds = alarmModeCreateOrUpdateDetailDTO.Triggers.Select(t => t.DeviceId).ToList(); + alarmMode.DevicesIds = devicesIds; + if (create) + alarmMode = _AlarmDatabaseService.Create(alarmMode); + else + alarmMode = _AlarmDatabaseService.Update(alarmMode); + + // List devices for DTO (only) + List devices = new List(); + foreach (var deviceId in devicesIds) + { + Device device = _DeviceDatabaseService.GetById(deviceId); + if (device != null) + { + devices.Add(device); + } + } + + return alarmMode.ToDetailDTO(devices.Select(d => d.ToDTO()).ToList()); + } + + public static bool Activate(AlarmDatabaseService _AlarmDatabaseService, AlarmMode alarmModeToActivate, HomeDatabaseService _HomeDatabaseService) + { + try { + // Desactivate current mode + AlarmMode alarmMode = _AlarmDatabaseService.GetCurrentActivatedAlarm(alarmModeToActivate.HomeId); + alarmMode.Activated = false; + + alarmMode = _AlarmDatabaseService.Update(alarmMode); + + // Activate the new one + alarmModeToActivate.Activated = true; + alarmModeToActivate = _AlarmDatabaseService.Update(alarmModeToActivate); + + // Update home + return HomeService.UpdateAlarmMode(_HomeDatabaseService, alarmModeToActivate); // Change active mode for home + } + catch (Exception ex) + { + // Todo add log + System.Console.WriteLine($"An error has occurred during activating alarm {alarmModeToActivate.Name} : {ex.Message}"); + return false; + } + + } + + public static bool CreateDefaultAlarmModes(HomeDatabaseService _HomeDatabaseService, AlarmDatabaseService _AlarmDatabaseService, Home home) + { + try + { + // Home mode + AlarmMode homeMode = new AlarmMode() { HomeId = home.Id, Name = AlarmType.Home.ToString(), Type = AlarmType.Home, Activated = false, IsDefault = true, Notification = true, CreatedDate = DateTime.Now, UpdatedDate = DateTime.Now }; + homeMode = _AlarmDatabaseService.Create(homeMode); + + // Absent mode + AlarmMode absentMode = new AlarmMode() { HomeId = home.Id, Name = AlarmType.Absent.ToString(), Type = AlarmType.Absent, Activated = false, IsDefault = true, Notification = true, CreatedDate = DateTime.Now, UpdatedDate = DateTime.Now }; + absentMode = _AlarmDatabaseService.Create(absentMode); + + // Geolocalized + AlarmMode geolocalizedMode = new AlarmMode() { HomeId = home.Id, Name = AlarmType.Geolocalized.ToString(), Type = AlarmType.Geolocalized, Activated = false, IsDefault = true, Notification = true, CreatedDate = DateTime.Now, UpdatedDate = DateTime.Now, GeolocalizedMode = new GeolocalizedMode() }; + geolocalizedMode = _AlarmDatabaseService.Create(geolocalizedMode); + + // Programmed mode + AlarmMode programmedMode = new AlarmMode() { HomeId = home.Id, Name = AlarmType.Programmed.ToString(), Type = AlarmType.Programmed, Activated = false, IsDefault = true, Notification = true, CreatedDate = DateTime.Now, UpdatedDate = DateTime.Now, ProgrammedMode = new ProgrammedMode() }; + programmedMode = _AlarmDatabaseService.Create(programmedMode); + + // Desarmed mode + AlarmMode desarmedMode = new AlarmMode() { HomeId = home.Id, Name = AlarmType.Desarmed.ToString(), Type = AlarmType.Desarmed, Activated = true, IsDefault = true, Notification = true, CreatedDate = DateTime.Now, UpdatedDate = DateTime.Now }; + desarmedMode = _AlarmDatabaseService.Create(desarmedMode); + + return HomeService.UpdateAlarmMode(_HomeDatabaseService, desarmedMode); // Activate desarmed mode by default + } + catch (Exception ex) + { + // Todo add log + System.Console.WriteLine($"An error has occurred during creating default alarm modes for home {home.Name} : {ex.Message}"); + return false; + } + } + #endregion + + #region Handle message event + internal static void HandleMessage(AlarmMode alarmMode, Device deviceTrigger, string message, DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, GroupDatabaseService _GroupDatabaseService, string homeId) + { + bool isAction = false; + switch (alarmMode.Type) + { + case AlarmType.Home: + case AlarmType.Absent: + case AlarmType.Custom: + // Check if trigger status correct + var alarmTriggers = alarmMode.Triggers.Where(t => t.DeviceId == deviceTrigger.Id).ToList(); + + #region Check trigger + foreach (var alarmTrigger in alarmTriggers) + { + List exposes = DevicesHelper.GetDeviceExposes(deviceTrigger); + + var triggerStateValueCheck = (object)null; + + // Try to get automationTrigger.StateName of zigbee device + var expose = exposes.Where(e => e.name == alarmTrigger.StateName).FirstOrDefault(); + bool validTrigger = expose != null; // No expose found.. => the device has not the correct state => not correct automation + + try + { + // Try to get specific field from message + var dic = JsonConvert.DeserializeObject>(message); + triggerStateValueCheck = dic[alarmTrigger.StateName]; // if action slide => get slide + } + catch (Exception ex) + { + validTrigger = false; + } + + + if (validTrigger && alarmTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) + { + // Correct trigger ! + System.Console.WriteLine($"Correct trigger for alarm {alarmMode.Name}"); + + #region Actions ! + foreach (var action in alarmMode.Actions) + { + + var DeviceNameForAction = ""; + Device actionDeviceToTest = new Device(); + + // Retrieve action type + switch (action.Type) + { + case ActionType.DEVICE: + var deviceAction = _DeviceDatabaseService.GetById(action.DeviceId); + var providerActionTest = _ProviderDatabaseService.GetById(homeId, action.ProviderId); + + DeviceNameForAction = deviceAction.Name; + actionDeviceToTest = deviceAction; + System.Console.WriteLine($"We get a device action ! Name={deviceAction.Name} Type={deviceAction.Type}"); + System.Console.WriteLine($"Check action provider type ! Type={providerActionTest.Type} Name={providerActionTest.Name}"); + break; + case ActionType.GROUP: + var groupAction = _GroupDatabaseService.GetById(action.GroupId); + DeviceNameForAction = groupAction.Name; + + System.Console.WriteLine($"We get a group action ! Name={groupAction.Name} Type={groupAction.Type}"); + System.Console.WriteLine($"Check action zigbeeGroupAction type ! Type={groupAction.Type}"); + + // Check state of first device of a group + actionDeviceToTest = _DeviceDatabaseService.GetByGroup(groupAction.Id).FirstOrDefault(); // TODO : Send error if no device found ! + break; + case ActionType.MQTT: + // take raw request and send it ! + RawRequestMQTT rawRequestMQTT = JsonConvert.DeserializeObject(action.RawRequest); + + if (rawRequestMQTT != null) + { + // SEND REQUEST + System.Console.WriteLine($"Send raw request mqtt! topic:{rawRequestMQTT.topic} - message: {rawRequestMQTT.message}"); + MqttClientService.PublishMessage(rawRequestMQTT.topic, rawRequestMQTT.message); + } + break; + case ActionType.HTTP: // Correct way ? + // TODO + break; + } + + var providerAction = _ProviderDatabaseService.GetById(homeId, action.ProviderId); + + // Check if device exist + if (actionDeviceToTest != null && providerAction != null) + { + switch (providerAction.Type) + { + case ProviderType.zigbee2mqtt: + try + { + DevicesHelper.ActionOnZigbee2Mqtt(actionDeviceToTest, action, DeviceNameForAction); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnZigbee2Mqtt result in error: {ex}"); + } + break; + case ProviderType.meross: + try + { + DevicesHelper.ActionOnMeross(_DeviceDatabaseService, actionDeviceToTest, action, DeviceNameForAction); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnMeross result in error: {ex}"); + } + break; + case ProviderType.yeelight: + try + { + DevicesHelper.ActionOnYeelight(_DeviceDatabaseService, actionDeviceToTest, action); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnYeelight result in error: {ex}"); + } + break; + } + } + else + { + System.Console.WriteLine($"Device or group found in action incorrect"); + } + } + #endregion + + } + } + #endregion + + break; + case AlarmType.Geolocalized: + // Todo check geolocalisation.. + break; + case AlarmType.Programmed: + try { + List CurrentDayLogic = new List(); + switch (DateTime.Now.DayOfWeek) + { + case DayOfWeek.Monday: + CurrentDayLogic = alarmMode.ProgrammedMode.Monday; + break; + case DayOfWeek.Tuesday: + CurrentDayLogic = alarmMode.ProgrammedMode.Tuesday; + break; + case DayOfWeek.Wednesday: + CurrentDayLogic = alarmMode.ProgrammedMode.Wednesday; + break; + case DayOfWeek.Thursday: + CurrentDayLogic = alarmMode.ProgrammedMode.Thursday; + break; + case DayOfWeek.Friday: + CurrentDayLogic = alarmMode.ProgrammedMode.Friday; + break; + case DayOfWeek.Saturday: + CurrentDayLogic = alarmMode.ProgrammedMode.Saturday; + break; + case DayOfWeek.Sunday: + CurrentDayLogic = alarmMode.ProgrammedMode.Sunday; + break; + default: + throw new Exception("Error in days"); + } + + // Todo retrieve current time period depending the current time. + + //=> TODO + isAction = false; + } + catch (Exception ex) { + // Todo log + System.Console.WriteLine($"Error in alarm programmed mode {ex.Message}"); + } + + break; + case AlarmType.Desarmed: + isAction = false; + // Do nothing, alarm is disarmed :) + break; + } + + #region Send notification - check if error + // Send notification if exist + if (alarmMode.Notification && isAction) + System.Console.WriteLine($"TODO send notification"); + #endregion + } + #endregion + } +} diff --git a/MyCore/Services/AutomationService.cs b/MyCore/Services/AutomationService.cs index 9f26d5e..2a05f32 100644 --- a/MyCore/Services/AutomationService.cs +++ b/MyCore/Services/AutomationService.cs @@ -1,34 +1,38 @@ -using MyCore.Interfaces.DTO; +using Mqtt.Client.AspNetCore.Services; +using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; +using MyCore.Service.Controllers.Helpers; using MyCore.Services; using MyCore.Services.MyControlPanel; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static Mqtt.Client.AspNetCore.Services.MqttClientMerossService; namespace MyCore.Service.Services { public class AutomationService { - public static AutomationDTO CreateOrUpdate(AutomationDatabaseService _AutomationDatabaseService, string homeId, AutomationCreateOrUpdateDetailDTO automationCreateOrUpdateDetailDTO, bool create) + public static AutomationDTO CreateOrUpdate(AutomationDatabaseService _AutomationDatabaseService, string homeId, AutomationDetailDTO automationDetailDTO, bool create) { Automation automation; if (create) automation = new Automation(); else { - automation = _AutomationDatabaseService.GetById(automationCreateOrUpdateDetailDTO.Id); + automation = _AutomationDatabaseService.GetById(automationDetailDTO.Id); } automation.HomeId = homeId; - automation.Name = automationCreateOrUpdateDetailDTO.Name; - automation.Active = automationCreateOrUpdateDetailDTO.Active; + automation.Name = automationDetailDTO.Name; + automation.Active = automationDetailDTO.Active; automation.CreatedDate = create ? DateTime.Now : automation.CreatedDate; automation.UpdatedDate = DateTime.Now; - automation.Triggers = automationCreateOrUpdateDetailDTO.Triggers; - automation.Conditions = automationCreateOrUpdateDetailDTO.Conditions; - automation.Actions = automationCreateOrUpdateDetailDTO.Actions; + automation.Triggers = automationDetailDTO.Triggers; + automation.Conditions = automationDetailDTO.Conditions; + automation.Actions = automationDetailDTO.Actions; var allDeviceIds = new List(); allDeviceIds.AddRange(automation.Triggers.Select(t => t.DeviceId)); // Only take the deviceIds from triggers /*allDeviceIds.AddRange(automation.Conditions.Select(t => t.DeviceId)); @@ -50,5 +54,195 @@ namespace MyCore.Service.Services else return _AutomationDatabaseService.Update(automation.Id, automation).ToDTO(); } + + public static void HandleAutomation(List automations, Device deviceTrigger, string message, DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, GroupDatabaseService _GroupDatabaseService, string homeId) + { + foreach (var automation in automations) + { + try + { + // todo check if not null and if more than one element + if (deviceTrigger != null && automation.Triggers.Any(t => t.DeviceId == deviceTrigger.Id)) + { + var automationTriggers = automation.Triggers.Where(t => t.DeviceId == deviceTrigger.Id).ToList(); + + // Test for each automation trigger found + foreach (var automationTrigger in automationTriggers) + { + List exposes = DevicesHelper.GetDeviceExposes(deviceTrigger); + + var triggerStateValueCheck = (object)null; + + // Try to get automationTrigger.StateName of zigbee device + 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 + + try + { + // Try to get specific field from message + var dic = JsonConvert.DeserializeObject>(message); + triggerStateValueCheck = dic[automationTrigger.StateName]; // if action slide => get slide + } + catch (Exception ex) + { + validTrigger = false; + } + + + if (validTrigger && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) + { + // Correct trigger ! + System.Console.WriteLine($"Correct trigger for automation {automation.Name}"); + + var isConditionsRespected = CheckConditions(automation.Conditions); + + if (!isConditionsRespected.Any(cr => !cr)) + { + System.Console.WriteLine("Conditions respected !"); + foreach (var action in automation.Actions) + { + + var DeviceNameForAction = ""; + Device actionDeviceToTest = new Device(); + + // Retrieve action type + switch (action.Type) + { + case ActionType.DEVICE: + var deviceAction = _DeviceDatabaseService.GetById(action.DeviceId); + var providerActionTest = _ProviderDatabaseService.GetById(homeId, action.ProviderId); + + DeviceNameForAction = deviceAction.Name; + actionDeviceToTest = deviceAction; + System.Console.WriteLine($"We get a device action ! Name={deviceAction.Name} Type={deviceAction.Type}"); + System.Console.WriteLine($"Check action provider type ! Type={providerActionTest.Type} Name={providerActionTest.Name}"); + break; + case ActionType.GROUP: + var groupAction = _GroupDatabaseService.GetById(action.GroupId); + DeviceNameForAction = groupAction.Name; + + System.Console.WriteLine($"We get a group action ! Name={groupAction.Name} Type={groupAction.Type}"); + System.Console.WriteLine($"Check action zigbeeGroupAction type ! Type={groupAction.Type}"); + + // Check state of first device of a group + actionDeviceToTest = _DeviceDatabaseService.GetByGroup(groupAction.Id).FirstOrDefault(); // TODO : Send error if no device found ! + break; + case ActionType.MQTT: + // take raw request and send it ! + RawRequestMQTT rawRequestMQTT = JsonConvert.DeserializeObject(action.RawRequest); + + if (rawRequestMQTT != null) + { + // SEND REQUEST + System.Console.WriteLine($"Send raw request mqtt! topic:{rawRequestMQTT.topic} - message: {rawRequestMQTT.message}"); + MqttClientService.PublishMessage(rawRequestMQTT.topic, rawRequestMQTT.message); + } + break; + case ActionType.HTTP: // Correct way ? + // TODO + break; + } + + var providerAction = _ProviderDatabaseService.GetById(homeId, action.ProviderId); + + // Check if device exist + if (actionDeviceToTest != null && providerAction != null) + { + switch (providerAction.Type) + { + case ProviderType.zigbee2mqtt: + try + { + DevicesHelper.ActionOnZigbee2Mqtt(actionDeviceToTest, action, DeviceNameForAction); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnZigbee2Mqtt result in error: {ex}"); + } + break; + case ProviderType.meross: + try + { + DevicesHelper.ActionOnMeross(_DeviceDatabaseService, actionDeviceToTest, action, DeviceNameForAction); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnMeross result in error: {ex}"); + } + break; + case ProviderType.yeelight: + try + { + DevicesHelper.ActionOnYeelight(_DeviceDatabaseService, actionDeviceToTest, action); + } + catch (Exception ex) + { + System.Console.WriteLine($"ActionOnYeelight result in error: {ex}"); + } + break; + } + } + else + { + System.Console.WriteLine($"Device or group found in action incorrect"); + } + } + } + else + { + System.Console.WriteLine($"One or more condition aren't respected"); + } + } + } + } + } + catch (Exception ex) { System.Console.WriteLine($"Exeption in one of automation logic - {ex}"); } + } + } + + public 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; + } + } } diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index 723399a..b188107 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.Zigbee2Mqtt; +using MyCore.Service.Controllers.Helpers; using MyCore.Service.Services; using MyCore.Services.MyControlPanel; using Newtonsoft.Json; @@ -20,7 +21,7 @@ namespace MyCore.Services.Devices 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, RoomDatabaseService _RoomDatabaseService, AutomationDatabaseService _AutomationDatabaseService, string homeId) + public static async Task HandleActionFromMQTTAsync(string topic, string message, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, RoomDatabaseService _RoomDatabaseService, AutomationDatabaseService _AutomationDatabaseService, AlarmDatabaseService _AlarmDatabaseService, string homeId) { System.Console.WriteLine($"Received message {message}"); @@ -44,8 +45,7 @@ namespace MyCore.Services.Devices if (currentProvider != null) { - var automations = _AutomationDatabaseService.GetActiveByProvider(currentProvider.Id); - + deviceTrigger = _DeviceDatabaseService.GetByName(deviceServiceName).FirstOrDefault(); if (deviceTrigger != null) @@ -55,145 +55,17 @@ namespace MyCore.Services.Devices // if is not a set => check automation if (topicSplit.Length <= 2) { - foreach (var automation in automations) - { - try { - // todo check if not null and if more than one element - if (deviceTrigger != null && automation.Triggers.Any(t => t.DeviceId == deviceTrigger.Id)) - { - var automationTriggers = automation.Triggers.Where(t => t.DeviceId == deviceTrigger.Id).ToList(); + #region Automations + var automations = _AutomationDatabaseService.GetActiveByProvider(currentProvider.Id); + AutomationService.HandleAutomation(automations, deviceTrigger, message, _DeviceDatabaseService, _ProviderDatabaseService, _GroupDatabaseService, homeId); + #endregion - // Test for each automation trigger found - foreach (var automationTrigger in automationTriggers) - { - List exposes = GetDeviceExposes(deviceTrigger); + #region Alarm + AlarmMode alarmMode = _AlarmDatabaseService.GetCurrentActivatedAlarm(homeId); + if (alarmMode.DevicesIds.Contains(deviceTrigger.Id)) + AlarmService.HandleMessage(alarmMode, deviceTrigger, message, _DeviceDatabaseService, _ProviderDatabaseService, _GroupDatabaseService, homeId); + #endregion - var triggerStateValueCheck = (object)null; - - // Try to get automationTrigger.StateName of zigbee device - 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 - - try - { - // Try to get specific field from message - var dic = JsonConvert.DeserializeObject>(message); - triggerStateValueCheck = dic[automationTrigger.StateName]; // if action slide => get slide - } - catch (Exception ex) - { - validTrigger = false; - } - - - if (validTrigger && automationTrigger.StateValue.ToLower() == triggerStateValueCheck.ToString().ToLower()) - { - // Correct trigger ! - System.Console.WriteLine($"Correct trigger for automation {automation.Name}"); - - var isConditionsRespected = CheckConditions(automation.Conditions); - - if (!isConditionsRespected.Any(cr => !cr)) - { - System.Console.WriteLine("Conditions respected !"); - foreach (var action in automation.Actions) - { - - var DeviceNameForAction = ""; - Device actionDeviceToTest = new Device(); - - // Retrieve action type - switch (action.Type) - { - case ActionType.DEVICE: - var deviceAction = _DeviceDatabaseService.GetById(action.DeviceId); - var providerActionTest = _ProviderDatabaseService.GetById(homeId, action.ProviderId); - - DeviceNameForAction = deviceAction.Name; - actionDeviceToTest = deviceAction; - System.Console.WriteLine($"We get a device action ! Name={deviceAction.Name} Type={deviceAction.Type}"); - System.Console.WriteLine($"Check action provider type ! Type={providerActionTest.Type} Name={providerActionTest.Name}"); - break; - case ActionType.GROUP: - var groupAction = _GroupDatabaseService.GetById(action.GroupId); - DeviceNameForAction = groupAction.Name; - - System.Console.WriteLine($"We get a group action ! Name={groupAction.Name} Type={groupAction.Type}"); - System.Console.WriteLine($"Check action zigbeeGroupAction type ! Type={groupAction.Type}"); - - // Check state of first device of a group - actionDeviceToTest = _DeviceDatabaseService.GetByGroup(groupAction.Id).FirstOrDefault(); // TODO : Send error if no device found ! - break; - case ActionType.MQTT: - // take raw request and send it ! - RawRequestMQTT rawRequestMQTT = JsonConvert.DeserializeObject(action.RawRequest); - - if (rawRequestMQTT != null) - { - // SEND REQUEST - System.Console.WriteLine($"Send raw request mqtt! topic:{rawRequestMQTT.topic} - message: {rawRequestMQTT.message}"); - MqttClientService.PublishMessage(rawRequestMQTT.topic, rawRequestMQTT.message); - } - break; - case ActionType.HTTP: // Correct way ? - // TODO - break; - } - - var providerAction = _ProviderDatabaseService.GetById(homeId, action.ProviderId); - - // Check if device exist - if (actionDeviceToTest != null && providerAction != null) - { - switch (providerAction.Type) - { - case ProviderType.zigbee2mqtt: - try - { - ActionOnZigbee2Mqtt(actionDeviceToTest, action, DeviceNameForAction); - } - catch (Exception ex) - { - System.Console.WriteLine($"ActionOnZigbee2Mqtt result in error: {ex}"); - } - break; - case ProviderType.meross: - try - { - ActionOnMeross(_DeviceDatabaseService, actionDeviceToTest, action, DeviceNameForAction); - } - catch (Exception ex) - { - System.Console.WriteLine($"ActionOnMeross result in error: {ex}"); - } - break; - case ProviderType.yeelight: - try - { - ActionOnYeelight(_DeviceDatabaseService, actionDeviceToTest, action); - } - catch (Exception ex) - { - System.Console.WriteLine($"ActionOnYeelight result in error: {ex}"); - } - break; - } - } - else - { - System.Console.WriteLine($"Device or group found in action incorrect"); - } - } - } - else - { - System.Console.WriteLine($"One or more condition aren't respected"); - } - } - } - } - } catch (Exception ex) { System.Console.WriteLine($"Exeption in one of automation logic - {ex}"); } - } // Update last state of devices deviceTrigger.LastStateDate = DateTime.Now; deviceTrigger.LastState = message; @@ -218,416 +90,5 @@ namespace MyCore.Services.Devices System.Console.WriteLine($"Current provider not found - {providerFromTopic}"); } } - - 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($"Yeelight 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)) - { - // Comment this for test (ensure the request will be sent) - /*if (dic.Count > 0) - { - //if (device.Type == DeviceType.Light) {} - var test = dic["state"].ToString().ToLower(); - var test0 = state.Value.ToLower(); - if (dic["state"].ToString().ToLower() == state.Value.ToLower() && action.States.Count <= 1) // workaround if brightness not the same => For switch or light without brightness - { - throw new Exception($"Action device is already at the good state : {state.Name} {state.Value}"); - } - }*/ - - 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}"); - } - } - - // If any action ask for state update, check if device is already at the asked state - if (action.States.Any(s => s.Name == "state") && !action.IsForce) - { - bool sendRequest = true; - - // If action doesn't ask for brightness just test if device is already at the asked state - if (action.States.Any(s => s.Name == "brightness")) - { - // Check state and brightness - // if we got state value (current value) - if (dic.Any(d => d.Key == "state")) - { - var currentValue = dic["state"].ToString().ToLower(); - var askedValue = action.States.FirstOrDefault(s => s.Name == "state").Value.ToLower(); - if (currentValue == askedValue) - { - // Check brightness difference - if (dic.Any(d => d.Key == "brightness")) - { - try - { - var currentValueBrightness = int.Parse(dic["brightness"].ToString().ToLower()); - var askedValueBrightness = int.Parse(action.States.FirstOrDefault(s => s.Name == "brightness").Value.ToLower()); - if (Math.Abs(currentValueBrightness - askedValueBrightness) <= 5) // brightness diff is lower than 5 => don't change brightness - sendRequest = false; - else - sendRequest = true; - } - catch (Exception ex) - { - sendRequest = false; - System.Console.WriteLine($"int parse error in brightness check", ex.Message); - } - } - } - } - } - else - { - // Check only state value - // if we got state value (current value) - if (dic.Any(d => d.Key == "state")) - { - var currentValue = dic["state"].ToString().ToLower(); - var askedValue = action.States.FirstOrDefault(s => s.Name == "state").Value.ToLower(); - if (currentValue == askedValue) - sendRequest = false; - } - } - - if (!sendRequest) - throw new Exception($"Action device is already at the good state"); - } - } - 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(); - // Get Exposes for the zigbee device - foreach (var supportedOperation in device.SupportedOperations) - { - exposes.Add(JsonConvert.DeserializeObject(supportedOperation)); - } - return exposes; - } - - private static System.Type GetStateType(Expose expose) - { - switch (expose.type) - { - // e.g.: occupancy - case "binary": - if (expose.value_toggle == null) - { - // value are boolean - return true.GetType(); - } - else - { - // value are string - return "".GetType(); - } - case "numeric": - return new long().GetType(); - case "enum": - return expose.values.GetType(); - break; - /*case "text": - break; - case "light": - break; - case "composite": - break; - case "switch": - break;*/ - /*case "fan": - break; - case "cover": - break; - case "lock": - break; - case "climate": - break;*/ - default: - return null; - } - } } } diff --git a/MyCore/Services/GroupService.cs b/MyCore/Services/GroupService.cs index 270c505..5754771 100644 --- a/MyCore/Services/GroupService.cs +++ b/MyCore/Services/GroupService.cs @@ -140,24 +140,45 @@ namespace MyCore.Service.Services } 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 + // TO TEEEEST !! + /* // As user not change everytime.. => Hard refresh. - var groupToDelete = groups.Where(eg => eg.ServiceIdentification == existingGroupWithThisId.id).FirstOrDefault(); + var groupToUpdate = groups.Where(eg => eg.ServiceIdentification == existingGroupWithThisId.id).FirstOrDefault(); - // Delete in device first - var devicesInGroup = _DeviceDatabaseService.GetByGroup(groupToDelete.Id); + // Update in device first + var devicesInGroup = _DeviceDatabaseService.GetByGroup(groupToUpdate.Id); - foreach (var device in devicesInGroup) + // Devices to add to the group + List devicesToAdd = zigbee2MqttGroup.members.Select(m => m.ieee_address).Where(ia => !existingGroupWithThisId.members.Select(m => m.ieee_address).ToList().Contains(ia)).ToList(); + + foreach (var deviceId in devicesToAdd) { - device.GroupIds = device.GroupIds.Where(g => g != groupToDelete.Id).ToList(); + Device device = _DeviceDatabaseService.GetById(deviceId); + device.GroupIds.Add(groupToUpdate.Id); _DeviceDatabaseService.Update(device); } - // Delete group - _GroupDatabaseService.Remove(groupToDelete.Id); - // Create new group - CreateFromZigbeeAsync(_GroupDatabaseService, _DeviceDatabaseService, homeId, new List() { zigbee2MqttGroup }); + // Devices to add to the group + List devicesToRemove = existingGroupWithThisId.members.Select(m => m.ieee_address).Where(ia => !zigbee2MqttGroup.members.Select(m => m.ieee_address).ToList().Contains(ia)).ToList(); + + foreach (var deviceId in devicesToRemove) + { + Device device = _DeviceDatabaseService.GetById(deviceId); + device.GroupIds = device.GroupIds.Where(g => g != groupToUpdate.Id).ToList(); + _DeviceDatabaseService.Update(device); + } + + // Update group + groupToUpdate.DevicesIds = new List(); // Remove all for update + foreach (var deviceId in zigbee2MqttGroup.members.Select(m => m.ieee_address).ToList()) + { + Device device = _DeviceDatabaseService.GetById(deviceId); + groupToUpdate.DevicesIds.Add(device.Id); + } + + _GroupDatabaseService.Update(groupToUpdate); + */ } } // else do nothing diff --git a/MyCore/Services/MyControlPanel/Database/AlarmDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/AlarmDatabaseService.cs index 5b606fe..97332fc 100644 --- a/MyCore/Services/MyControlPanel/Database/AlarmDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/AlarmDatabaseService.cs @@ -45,6 +45,11 @@ namespace MyCore.Services return alarmIn; } + public AlarmMode GetCurrentActivatedAlarm(string homeId) + { + return _Alarms.Find(alarm => alarm.Activated && alarm.HomeId == homeId).FirstOrDefault(); + } + public void Remove(string id) { _Alarms.DeleteOne(alarm => alarm.Id == id); diff --git a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs index c0f62e9..aac4292 100644 --- a/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/DeviceDatabaseService.cs @@ -44,7 +44,7 @@ namespace MyCore.Services return _Devices.Find(h => h.HomeId == homeId).ToList(); } - public List GetByLocation(string homeId, string roomId) + public List GetByRoom(string homeId, string roomId) { return _Devices.Find(d => d.HomeId == homeId && d.RoomId == roomId).ToList(); } diff --git a/MyCore/Services/MyControlPanel/Database/HomeDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/HomeDatabaseService.cs index cf24449..d797bab 100644 --- a/MyCore/Services/MyControlPanel/Database/HomeDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/HomeDatabaseService.cs @@ -39,9 +39,9 @@ namespace MyCore.Services return home; } - public Home Update(string id, Home homeIn) + public Home Update(Home homeIn) { - _Homes.ReplaceOne(home => home.Id == id, homeIn); + _Homes.ReplaceOne(home => home.Id == homeIn.Id, homeIn); return homeIn; } diff --git a/MyCore/Services/MyControlPanel/HomeService.cs b/MyCore/Services/MyControlPanel/HomeService.cs index e0fc1da..dd72a73 100644 --- a/MyCore/Services/MyControlPanel/HomeService.cs +++ b/MyCore/Services/MyControlPanel/HomeService.cs @@ -39,7 +39,26 @@ namespace MyCore.Services if (create) return _HomeDatabaseService.Create(home); else - return _HomeDatabaseService.Update(home.Id, home); + return _HomeDatabaseService.Update(home); + } + + public static bool UpdateAlarmMode(HomeDatabaseService _HomeDatabaseService, AlarmMode alarmMode) + { + try + { + Home home = _HomeDatabaseService.GetById(alarmMode.HomeId); + home.CurrentAlarmMode = alarmMode; + + _HomeDatabaseService.Update(home); + + return true; + } + catch (Exception ex) + { + // Todo add log + System.Console.WriteLine($"An error has occurred during updating default alarm modes named {alarmMode.Name} : {ex.Message}"); + return false; + } } } }