using Mqtt.Client.AspNetCore.Services; using MyCore.Framework.Business; 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 devicesIdsTriggers = alarmModeCreateOrUpdateDetailDTO.Triggers.Select(t => t.DeviceId).ToList(); alarmMode.DevicesIds = devicesIdsTriggers; List devicesIdsActions = alarmModeCreateOrUpdateDetailDTO.Actions.Select(t => t.DeviceId).ToList(); alarmMode.DevicesIds.AddRange(devicesIdsActions); if (create) alarmMode = _AlarmDatabaseService.Create(alarmMode); else alarmMode = _AlarmDatabaseService.Update(alarmMode); // List devices for DTO (only) List devices = new List(); foreach (var deviceId in alarmMode.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, Actions = new List(), Triggers = new List(), DevicesIds = new List() }; 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, Triggers = new List(), DevicesIds = new List() }; 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(), Triggers = new List(), DevicesIds = new List() }; 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(), Triggers = new List(), DevicesIds = new List() }; 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, Triggers = new List(), DevicesIds = new List() }; 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 async Task HandleMessageAsync(AlarmMode alarmMode, Device deviceTrigger, string message, DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, GroupDatabaseService _GroupDatabaseService, EventDatabaseService _EventDatabaseService, Home home) { 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}"); isAction = true; #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(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(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 - save event if (isAction) { // Create an event EventDetailDTO eventDetailDTO = new EventDetailDTO() { Type = EventType.AlarmTriggered, HomeId = home.Id, RoomId = deviceTrigger.RoomId, AlarmTriggered = new AlarmTriggered() { AlarmModeId = alarmMode.Id, AlarmModeName = alarmMode.Name }, DeviceState = new DeviceState() { DeviceId = deviceTrigger.Id, DeviceName = deviceTrigger.Name, DeviceType = deviceTrigger.Type, Message = message } }; EventService.CreateOrUpdate(_EventDatabaseService, home.Id, eventDetailDTO, true); // Send notification if exist if (alarmMode.Notification) { System.Console.WriteLine($"Send notification"); NotificationDTO notificationDTO = new NotificationDTO(); notificationDTO.notificationTitle = "Alarme déclenchée"; notificationDTO.notificationMessage = $"Le capteur {deviceTrigger.Name} a fait déclencher l'alarme à {DateTime.Now.ToShortTimeString()}"; await NotificationLogic.PushFCMNotification(notificationDTO, home); } } #endregion } #endregion } }