Add alarm type, controller service, update automation, alarm service + update group logic

This commit is contained in:
Thomas Fransolet 2021-11-07 02:40:34 +01:00
parent 479ef5784d
commit 6da8b6bb33
19 changed files with 1430 additions and 606 deletions

View File

@ -1,7 +1,9 @@
using System; using MyCore.Interfaces.Models;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static MyCore.Interfaces.Models.AlarmMode;
namespace MyCore.Interfaces.DTO namespace MyCore.Interfaces.DTO
{ {
@ -13,9 +15,27 @@ namespace MyCore.Interfaces.DTO
public string Id { get; set; } public string Id { get; set; }
public string HomeId { get; set; } public string HomeId { get; set; }
public string Name { get; set; } public string Name { get; set; }
public AlarmType Type { get; set; }
public bool Activated { get; set; }
public bool IsDefault { get; set; } public bool IsDefault { get; set; }
public bool Notification { get; set; }
public DateTime CreatedDate { get; set; } public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; } public DateTime UpdatedDate { get; set; }
public List<string> DevicesIds { get; set; } // Check if ok }
public class AlarmModeDetailDTO : AlarmModeDTO
{
public List<Trigger> Triggers { get; set; }
public List<DeviceDetailDTO> Devices { get; set; }
public ProgrammedMode ProgrammedMode { get; set; }
public GeolocalizedMode GeolocalizedMode { get; set; }
}
public class AlarmModeCreateOrUpdateDetailDTO : AlarmModeDTO
{
public List<Trigger> Triggers { get; set; }
public List<MyCore.Interfaces.Models.Action> Actions { get; set; }
public ProgrammedMode ProgrammedMode { get; set; }
public GeolocalizedMode GeolocalizedMode { get; set; }
} }
} }

View File

@ -23,12 +23,4 @@ namespace MyCore.Interfaces.DTO
public List<MyCore.Interfaces.Models.Action> Actions { get; set; } public List<MyCore.Interfaces.Models.Action> Actions { get; set; }
public List<string> DevicesIds { get; set; } public List<string> DevicesIds { get; set; }
} }
public class AutomationCreateOrUpdateDetailDTO : AutomationDTO
{
public List<Trigger> Triggers { get; set; }
public List<Condition> Conditions { get; set; }
public List<MyCore.Interfaces.Models.Action> Actions { get; set; }
public List<string> DeviceIds { get; set; }
}
} }

View File

@ -25,19 +25,52 @@ namespace MyCore.Interfaces.Models
[BsonRequired] [BsonRequired]
public string Name { get; set; } public string Name { get; set; }
[BsonElement("Activated")]
[BsonRequired]
public bool Activated { get; set; }
[BsonElement("IsDefault")] [BsonElement("IsDefault")]
[BsonRequired] [BsonRequired]
public bool IsDefault { get; set; } public bool IsDefault { get; set; }
[BsonElement("Notification")]
[BsonRequired]
public bool Notification { get; set; }
[BsonElement("CreatedDate")] [BsonElement("CreatedDate")]
public DateTime CreatedDate { get; set; } public DateTime CreatedDate { get; set; }
[BsonElement("UpdatedDate")] [BsonElement("UpdatedDate")]
public DateTime UpdatedDate { get; set; } 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<Trigger> Triggers { get; set; }
[BsonElement("Actions")]
public List<Action> Actions { get; set; }
[BsonElement("DevicesIds")] [BsonElement("DevicesIds")]
public List<string> DevicesIds { get; set; } public List<string> DevicesIds { get; set; }
public enum AlarmType
{
Home,
Absent,
Geolocalized,
Programmed,
Desarmed,
Custom
}
public AlarmModeDTO ToDTO() public AlarmModeDTO ToDTO()
{ {
return new AlarmModeDTO() return new AlarmModeDTO()
@ -45,11 +78,58 @@ namespace MyCore.Interfaces.Models
Id = Id, Id = Id,
HomeId = HomeId, HomeId = HomeId,
Name = Name, Name = Name,
Type = Type,
Activated = Activated,
IsDefault = IsDefault,
Notification = Notification,
CreatedDate = CreatedDate,
UpdatedDate = UpdatedDate,
};
}
public AlarmModeDetailDTO ToDetailDTO(List<DeviceDetailDTO> devicesDetail)
{
return new AlarmModeDetailDTO()
{
Id = Id,
HomeId = HomeId,
Name = Name,
Type = Type,
Activated = Activated,
IsDefault = IsDefault, IsDefault = IsDefault,
CreatedDate = CreatedDate, CreatedDate = CreatedDate,
UpdatedDate = UpdatedDate, UpdatedDate = UpdatedDate,
DevicesIds = DevicesIds, Triggers = Triggers,
Devices = devicesDetail,
ProgrammedMode = ProgrammedMode,
GeolocalizedMode = GeolocalizedMode
}; };
} }
} }
public class ProgrammedMode
{
public List<TimePeriodAlarm> Monday { get; set; }
public List<TimePeriodAlarm> Tuesday { get; set; }
public List<TimePeriodAlarm> Wednesday { get; set; }
public List<TimePeriodAlarm> Thursday { get; set; }
public List<TimePeriodAlarm> Friday { get; set; }
public List<TimePeriodAlarm> Saturday { get; set; }
public List<TimePeriodAlarm> 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; }
}
} }

View File

@ -11,6 +11,7 @@ using MongoDB.Bson;
using Mqtt.Client.AspNetCore.Services; using Mqtt.Client.AspNetCore.Services;
using MyCore.Interfaces.DTO; using MyCore.Interfaces.DTO;
using MyCore.Interfaces.Models; using MyCore.Interfaces.Models;
using MyCore.Service.Services;
using MyCore.Services; using MyCore.Services;
using MyCore.Services.Devices; using MyCore.Services.Devices;
using MyCore.Services.MyControlPanel; using MyCore.Services.MyControlPanel;
@ -25,16 +26,323 @@ namespace MyCore.Service.Controllers
private UserDatabaseService _UserDatabaseService; private UserDatabaseService _UserDatabaseService;
private RoomDatabaseService _RoomDatabaseService; private RoomDatabaseService _RoomDatabaseService;
private DeviceDatabaseService _DeviceDatabaseService; private DeviceDatabaseService _DeviceDatabaseService;
private AlarmDatabaseService _AlarmDatabaseService;
private HomeDatabaseService _HomeDatabaseService;
private readonly IMqttClientService _mqttClientService; private readonly IMqttClientService _mqttClientService;
//private readonly IMqttOnlineClientService _mqttOnlineClientService; //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._UserDatabaseService = userDatabaseService;
this._RoomDatabaseService = roomDatabaseService; this._RoomDatabaseService = roomDatabaseService;
this._DeviceDatabaseService = deviceDatabaseService; this._DeviceDatabaseService = deviceDatabaseService;
this._AlarmDatabaseService = alarmDatabaseService;
this._HomeDatabaseService = homeDatabaseService;
this._mqttClientService = provider.MqttClientService; this._mqttClientService = provider.MqttClientService;
//this._mqttOnlineClientService = onlineProvider.MqttOnlineClientService; //this._mqttOnlineClientService = onlineProvider.MqttOnlineClientService;
} }
/// <summary>
/// Get all alarm modes for the specified home
/// </summary>
/// <param name="homeId">Home Id</param>
[ProducesResponseType(typeof(List<AlarmModeDTO>), 200)]
[ProducesResponseType(typeof(string), 500)]
[HttpGet("{homeId}")]
public ObjectResult GetAll(string homeId)
{
try
{
List<AlarmMode> alarmModes = _AlarmDatabaseService.GetAll(homeId);
List<AlarmModeDTO> alarmModesDTO = alarmModes.Select(a => a.ToDTO()).ToList();
return new OkObjectResult(alarmModesDTO);
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
/// <summary>
/// Get detail info of a specified alarm mode
/// </summary>
/// <param name="alarmModeId">alarm id</param>
[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<Device> devices = new List<Device>();
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 };
}
}
/// <summary>
/// Create an alarm mode
/// </summary>
/// <param name="alarmModeCreateOrUpdateDetailDTO">Alarm mode to create</param>
[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 };
}
}
/// <summary>
/// Create default alarm modes
/// </summary>
/// <param name="homeId">Home Id</param>
[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<AlarmMode> 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 };
}
}
/// <summary>
/// Activate current alarm mode
/// </summary>
/// <param name="alarmModeId">Alarm mode to activate</param>
[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 };
}
}
/// <summary>
/// Update an alarm mode
/// </summary>
/// <param name="alarmModeCreateOrUpdateDetailDTO">alarm mode to update</param>
[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 };
}
}
/// <summary>
/// Delete an alarm mode
/// </summary>
/// <param name="alarmModeId">Id of alarm mode to delete</param>
[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 };
}
}
/// <summary>
/// Delete all alarm mode for a specified home
/// </summary>
/// <param name="homeId">Home Id</param>
[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 };
}
}
} }
} }

View File

@ -100,19 +100,19 @@ namespace MyCore.Service.Controllers
/// <summary> /// <summary>
/// Create an automation /// Create an automation
/// </summary> /// </summary>
/// <param name="automationCreateOrUpdateDetail">Automation to create</param> /// <param name="automationDetailDTO">Automation to create</param>
[ProducesResponseType(typeof(AutomationDTO), 200)] [ProducesResponseType(typeof(AutomationDTO), 200)]
[ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 500)] [ProducesResponseType(typeof(string), 500)]
[HttpPost] [HttpPost]
public ObjectResult Create([FromBody] AutomationCreateOrUpdateDetailDTO automationCreateOrUpdateDetail) public ObjectResult Create([FromBody] AutomationDetailDTO automationDetailDTO)
{ {
try try
{ {
if (automationCreateOrUpdateDetail == null) if (automationDetailDTO == null)
throw new ArgumentNullException("Incorrect parameters"); 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); return new OkObjectResult(automationCreated);
} }
@ -130,23 +130,23 @@ namespace MyCore.Service.Controllers
/// <summary> /// <summary>
/// Update an automation /// Update an automation
/// </summary> /// </summary>
/// <param name="automationCreateOrUpdateDetail">automation to update</param> /// <param name="automationDetailDTO">automation to update</param>
[ProducesResponseType(typeof(AutomationCreateOrUpdateDetailDTO), 200)] [ProducesResponseType(typeof(AutomationDetailDTO), 200)]
[ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)] [ProducesResponseType(typeof(string), 500)]
[HttpPut] [HttpPut]
public ObjectResult Update([FromBody] AutomationCreateOrUpdateDetailDTO automationCreateOrUpdateDetail) public ObjectResult Update([FromBody] AutomationDetailDTO automationDetailDTO)
{ {
try try
{ {
if (automationCreateOrUpdateDetail == null) if (automationDetailDTO == null)
throw new ArgumentNullException("Incorrect parameters"); throw new ArgumentNullException("Incorrect parameters");
if (!_AutomationDatabaseService.IsExist(automationCreateOrUpdateDetail.Id)) if (!_AutomationDatabaseService.IsExist(automationDetailDTO.Id))
throw new KeyNotFoundException("Automation does not exist"); 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); return new OkObjectResult(automationUpdated);
} }

View File

@ -91,7 +91,7 @@ namespace MyCore.Service.Controllers
if (group == null) if (group == null)
throw new KeyNotFoundException("Group not found"); throw new KeyNotFoundException("Group not found");
List<Device> devices = _DeviceDatabaseService.GetByLocation(group.HomeId, groupId); List<Device> devices = _DeviceDatabaseService.GetByRoom(group.HomeId, groupId);
return new OkObjectResult(group.ToDTO(devices.Select(d => d.ToDTO()).ToList())); return new OkObjectResult(group.ToDTO(devices.Select(d => d.ToDTO()).ToList()));
@ -360,7 +360,7 @@ namespace MyCore.Service.Controllers
Group group = _GroupDatabaseService.GetById(groupId); Group group = _GroupDatabaseService.GetById(groupId);
// Delete group from all devices // Delete group from all devices
List<Device> devices = _DeviceDatabaseService.GetByLocation(group.HomeId, groupId); List<Device> devices = _DeviceDatabaseService.GetByRoom(group.HomeId, groupId);
foreach (var device in devices) foreach (var device in devices)
{ {
device.GroupIds = device.GroupIds.Where(g => g != groupId).ToList(); device.GroupIds = device.GroupIds.Where(g => g != groupId).ToList();

View File

@ -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<Expose> GetDeviceExposes(Device device)
{
List<Expose> exposes = new List<Expose>();
// Get Exposes for the zigbee device
foreach (var supportedOperation in device.SupportedOperations)
{
exposes.Add(JsonConvert.DeserializeObject<Expose>(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<AutomationState>();
if (actionDeviceToTest.LastState != null)
{
dic = JsonConvert.DeserializeObject<List<AutomationState>>(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<List<int>>(channels.Value);
List<ToggleStatus> lastStatePlug = new List<ToggleStatus>(); // Off by default
// => TODO GET ALL POSSIBILITIES FROM DEVICE (number of channels)
if (actionDeviceToTest.LastState != null) // Get last state of device
{
try
{
lastStatePlug = JsonConvert.DeserializeObject<List<ToggleStatus>>(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<ToggleStatus> statuses = new List<ToggleStatus>();
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<string, object>();
System.Console.WriteLine($"zigbee2mqtt type !");
var actionDeviceExpose = DevicesHelper.GetDeviceExposes(device);
// Get device last state
var dic = new Dictionary<string, object>();
if (device.LastState != null)
{
dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(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
}
}

View File

@ -11,6 +11,7 @@ using MongoDB.Bson;
using Mqtt.Client.AspNetCore.Services; using Mqtt.Client.AspNetCore.Services;
using MyCore.Interfaces.DTO; using MyCore.Interfaces.DTO;
using MyCore.Interfaces.Models; using MyCore.Interfaces.Models;
using MyCore.Service.Services;
using MyCore.Services; using MyCore.Services;
using MyCore.Services.Devices; using MyCore.Services.Devices;
using MyCore.Services.MyControlPanel; using MyCore.Services.MyControlPanel;
@ -153,6 +154,10 @@ namespace MyCore.Service.Controllers
Home homeCreated = HomeService.CreateOrUpdate(this._HomeDatabaseService, this._AlarmDatabaseService, createOrUpdateHomeDTO, true); Home homeCreated = HomeService.CreateOrUpdate(this._HomeDatabaseService, this._AlarmDatabaseService, createOrUpdateHomeDTO, true);
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) foreach (var userId in createOrUpdateHomeDTO.UsersIds)
{ {
UserInfo user = _UserDatabaseService.GetById(userId); UserInfo user = _UserDatabaseService.GetById(userId);
@ -228,7 +233,7 @@ namespace MyCore.Service.Controllers
throw new KeyNotFoundException("Home does not exist"); throw new KeyNotFoundException("Home does not exist");
Home home = _HomeDatabaseService.GetById(homeId); 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 // Delete home
_HomeDatabaseService.Remove(homeId); _HomeDatabaseService.Remove(homeId);

View File

@ -79,7 +79,7 @@ namespace MyCore.Service.Controllers
Room room = _RoomDatabaseService.GetById(roomId); Room room = _RoomDatabaseService.GetById(roomId);
if (room == null) if (room == null)
throw new KeyNotFoundException("Room does not exist"); throw new KeyNotFoundException("Room does not exist");
List<Device> devices = _DeviceDatabaseService.GetByLocation(room.HomeId, roomId); List<Device> devices = _DeviceDatabaseService.GetByRoom(room.HomeId, roomId);
return new OkObjectResult(room.ToDTO(devices.Select(d => d.ToDTO()).ToList())); return new OkObjectResult(room.ToDTO(devices.Select(d => d.ToDTO()).ToList()));
@ -230,7 +230,7 @@ namespace MyCore.Service.Controllers
Room room = _RoomDatabaseService.GetById(roomId); Room room = _RoomDatabaseService.GetById(roomId);
// Delete location from all devices // Delete location from all devices
List<Device> devices = _DeviceDatabaseService.GetByLocation(room.HomeId, roomId); List<Device> devices = _DeviceDatabaseService.GetByRoom(room.HomeId, roomId);
foreach (var device in devices) foreach (var device in devices)
{ {
device.RoomId = null; device.RoomId = null;

View File

@ -35,11 +35,12 @@ namespace Mqtt.Client.AspNetCore.Services
private AutomationDatabaseService _automationDatabaseService; private AutomationDatabaseService _automationDatabaseService;
private ActionService _actionService; private ActionService _actionService;
private HomeDatabaseService _homeDatabaseService; private HomeDatabaseService _homeDatabaseService;
private AlarmDatabaseService _alarmDatabaseService;
public static string lastTopic; public static string lastTopic;
public static long lastTimeTopic; 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._homeDatabaseService = homeDatabaseService;
this._deviceDatabaseService = deviceDatabaseService; this._deviceDatabaseService = deviceDatabaseService;
@ -47,6 +48,7 @@ namespace Mqtt.Client.AspNetCore.Services
this._providerDatabaseService = providerDatabaseService; this._providerDatabaseService = providerDatabaseService;
this._roomDatabaseService = roomDatabaseService; this._roomDatabaseService = roomDatabaseService;
this._automationDatabaseService = automationDatabaseService; this._automationDatabaseService = automationDatabaseService;
this._alarmDatabaseService = alarmDatabaseService;
this._actionService = actionService; this._actionService = actionService;
Home home = this._homeDatabaseService.GetAll().ToList().Where(h => h.IsDefault).FirstOrDefault(); 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) 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 else
@ -298,9 +300,7 @@ namespace Mqtt.Client.AspNetCore.Services
if (zigbee2mqttProvider != null) if (zigbee2mqttProvider != null)
{ {
var groups = _GroupDatabaseService.GetByType(homeId, "zigbee2mqtt"); 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); GroupService.CompareGroupsFromZigbee2Mqtt(homeId, groups, groupsConvert, _DeviceDatabaseService, _GroupDatabaseService);
} }
} }

View File

@ -74,6 +74,7 @@ namespace MyCore.Service.Extensions
services.AddSingleton<ProviderDatabaseService>(); services.AddSingleton<ProviderDatabaseService>();
services.AddSingleton<RoomDatabaseService>(); services.AddSingleton<RoomDatabaseService>();
services.AddSingleton<AutomationDatabaseService>(); services.AddSingleton<AutomationDatabaseService>();
services.AddSingleton<AlarmDatabaseService>();
services.AddSingleton<ActionService>(); services.AddSingleton<ActionService>();
services.AddSingleton<MqttClientService>(); services.AddSingleton<MqttClientService>();
services.AddSingleton<IHostedService>(serviceProvider => services.AddSingleton<IHostedService>(serviceProvider =>

View File

@ -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<string> 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<Device> devices = new List<Device>();
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<Expose> 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<Dictionary<string, string>>(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<RawRequestMQTT>(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<TimePeriodAlarm> CurrentDayLogic = new List<TimePeriodAlarm>();
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
}
}

View File

@ -1,34 +1,38 @@
using MyCore.Interfaces.DTO; using Mqtt.Client.AspNetCore.Services;
using MyCore.Interfaces.DTO;
using MyCore.Interfaces.Models; using MyCore.Interfaces.Models;
using MyCore.Service.Controllers.Helpers;
using MyCore.Services; using MyCore.Services;
using MyCore.Services.MyControlPanel; using MyCore.Services.MyControlPanel;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Mqtt.Client.AspNetCore.Services.MqttClientMerossService;
namespace MyCore.Service.Services namespace MyCore.Service.Services
{ {
public class AutomationService 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; Automation automation;
if (create) if (create)
automation = new Automation(); automation = new Automation();
else else
{ {
automation = _AutomationDatabaseService.GetById(automationCreateOrUpdateDetailDTO.Id); automation = _AutomationDatabaseService.GetById(automationDetailDTO.Id);
} }
automation.HomeId = homeId; automation.HomeId = homeId;
automation.Name = automationCreateOrUpdateDetailDTO.Name; automation.Name = automationDetailDTO.Name;
automation.Active = automationCreateOrUpdateDetailDTO.Active; automation.Active = automationDetailDTO.Active;
automation.CreatedDate = create ? DateTime.Now : automation.CreatedDate; automation.CreatedDate = create ? DateTime.Now : automation.CreatedDate;
automation.UpdatedDate = DateTime.Now; automation.UpdatedDate = DateTime.Now;
automation.Triggers = automationCreateOrUpdateDetailDTO.Triggers; automation.Triggers = automationDetailDTO.Triggers;
automation.Conditions = automationCreateOrUpdateDetailDTO.Conditions; automation.Conditions = automationDetailDTO.Conditions;
automation.Actions = automationCreateOrUpdateDetailDTO.Actions; automation.Actions = automationDetailDTO.Actions;
var allDeviceIds = new List<string>(); var allDeviceIds = new List<string>();
allDeviceIds.AddRange(automation.Triggers.Select(t => t.DeviceId)); // Only take the deviceIds from triggers allDeviceIds.AddRange(automation.Triggers.Select(t => t.DeviceId)); // Only take the deviceIds from triggers
/*allDeviceIds.AddRange(automation.Conditions.Select(t => t.DeviceId)); /*allDeviceIds.AddRange(automation.Conditions.Select(t => t.DeviceId));
@ -50,5 +54,195 @@ namespace MyCore.Service.Services
else else
return _AutomationDatabaseService.Update(automation.Id, automation).ToDTO(); return _AutomationDatabaseService.Update(automation.Id, automation).ToDTO();
} }
public static void HandleAutomation(List<Automation> 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<Expose> 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<Dictionary<string, string>>(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<RawRequestMQTT>(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<bool> CheckConditions(List<Condition> conditions)
{
var isConditionsRespected = conditions.Count <= 0 ? new List<bool>() { true } : new List<bool>(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<TimeSpan>(condition.StartTime);
TimeSpan end = JsonConvert.DeserializeObject<TimeSpan>(condition.EndTime);
// => Test if ok with night !
if (start <= end)
{
// start and stop times are in the same day
if (now >= start && now <= end)
{
isConditionsRespected[i] = true;
}
}
else
{
// start and stop times are in different days
if (now >= start || now <= end)
{
isConditionsRespected[i] = true;
}
}
break;
}
i++;
}
return isConditionsRespected;
}
} }
} }

View File

@ -2,6 +2,7 @@
using MyCore.Interfaces.DTO; using MyCore.Interfaces.DTO;
using MyCore.Interfaces.Models; using MyCore.Interfaces.Models;
using MyCore.Interfaces.Models.Providers.Zigbee.Zigbee2Mqtt; using MyCore.Interfaces.Models.Providers.Zigbee.Zigbee2Mqtt;
using MyCore.Service.Controllers.Helpers;
using MyCore.Service.Services; using MyCore.Service.Services;
using MyCore.Services.MyControlPanel; using MyCore.Services.MyControlPanel;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -20,7 +21,7 @@ namespace MyCore.Services.Devices
private static Device deviceTrigger; private static Device deviceTrigger;
private static string providerFromTopic; private static string providerFromTopic;
private static string deviceServiceName; 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}"); System.Console.WriteLine($"Received message {message}");
@ -44,7 +45,6 @@ namespace MyCore.Services.Devices
if (currentProvider != null) if (currentProvider != null)
{ {
var automations = _AutomationDatabaseService.GetActiveByProvider(currentProvider.Id);
deviceTrigger = _DeviceDatabaseService.GetByName(deviceServiceName).FirstOrDefault(); deviceTrigger = _DeviceDatabaseService.GetByName(deviceServiceName).FirstOrDefault();
@ -55,145 +55,17 @@ namespace MyCore.Services.Devices
// if is not a set => check automation // if is not a set => check automation
if (topicSplit.Length <= 2) if (topicSplit.Length <= 2)
{ {
foreach (var automation in automations) #region Automations
{ var automations = _AutomationDatabaseService.GetActiveByProvider(currentProvider.Id);
try { AutomationService.HandleAutomation(automations, deviceTrigger, message, _DeviceDatabaseService, _ProviderDatabaseService, _GroupDatabaseService, homeId);
// todo check if not null and if more than one element #endregion
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 #region Alarm
foreach (var automationTrigger in automationTriggers) AlarmMode alarmMode = _AlarmDatabaseService.GetCurrentActivatedAlarm(homeId);
{ if (alarmMode.DevicesIds.Contains(deviceTrigger.Id))
List<Expose> exposes = GetDeviceExposes(deviceTrigger); 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<Dictionary<string, string>>(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<RawRequestMQTT>(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 // Update last state of devices
deviceTrigger.LastStateDate = DateTime.Now; deviceTrigger.LastStateDate = DateTime.Now;
deviceTrigger.LastState = message; deviceTrigger.LastState = message;
@ -218,416 +90,5 @@ namespace MyCore.Services.Devices
System.Console.WriteLine($"Current provider not found - {providerFromTopic}"); 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<AutomationState>();
if (actionDeviceToTest.LastState != null)
{
dic = JsonConvert.DeserializeObject<List<AutomationState>>(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<List<int>>(channels.Value);
List<ToggleStatus> lastStatePlug = new List<ToggleStatus>(); // Off by default
// => TODO GET ALL POSSIBILITIES FROM DEVICE (number of channels)
if (actionDeviceToTest.LastState != null) // Get last state of device
{
try
{
lastStatePlug = JsonConvert.DeserializeObject<List<ToggleStatus>>(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<ToggleStatus> statuses = new List<ToggleStatus>();
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<string, object>();
System.Console.WriteLine($"zigbee2mqtt type !");
var actionDeviceExpose = GetDeviceExposes(device);
// Get device last state
var dic = new Dictionary<string, object>();
if (device.LastState != null)
{
dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(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<bool> CheckConditions(List<Condition> conditions)
{
var isConditionsRespected = conditions.Count <= 0 ? new List<bool>() { true } : new List<bool>(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<TimeSpan>(condition.StartTime);
TimeSpan end = JsonConvert.DeserializeObject<TimeSpan>(condition.EndTime);
// => Test if ok with night !
if (start <= end)
{
// start and stop times are in the same day
if (now >= start && now <= end)
{
isConditionsRespected[i] = true;
}
}
else
{
// start and stop times are in different days
if (now >= start || now <= end)
{
isConditionsRespected[i] = true;
}
}
break;
}
i++;
}
return isConditionsRespected;
}
private static List<Expose> GetDeviceExposes(Device device)
{
List<Expose> exposes = new List<Expose>();
// Get Exposes for the zigbee device
foreach (var supportedOperation in device.SupportedOperations)
{
exposes.Add(JsonConvert.DeserializeObject<Expose>(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;
}
}
} }
} }

View File

@ -140,24 +140,45 @@ namespace MyCore.Service.Services
} else { } else {
if (!Enumerable.SequenceEqual(zigbee2MqttGroup.members.Select(m => m.ieee_address).OrderBy(t => t), existingGroupWithThisId.members.Select(m => m.ieee_address).OrderBy(t => t))) 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. // 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 // Update in device first
var devicesInGroup = _DeviceDatabaseService.GetByGroup(groupToDelete.Id); var devicesInGroup = _DeviceDatabaseService.GetByGroup(groupToUpdate.Id);
foreach (var device in devicesInGroup) // Devices to add to the group
List<string> 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); _DeviceDatabaseService.Update(device);
} }
// Delete group
_GroupDatabaseService.Remove(groupToDelete.Id);
// Create new group // Devices to add to the group
CreateFromZigbeeAsync(_GroupDatabaseService, _DeviceDatabaseService, homeId, new List<Zigbee2MqttGroup>() { zigbee2MqttGroup }); List<string> 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<string>(); // 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 // else do nothing

View File

@ -45,6 +45,11 @@ namespace MyCore.Services
return alarmIn; return alarmIn;
} }
public AlarmMode GetCurrentActivatedAlarm(string homeId)
{
return _Alarms.Find<AlarmMode>(alarm => alarm.Activated && alarm.HomeId == homeId).FirstOrDefault();
}
public void Remove(string id) public void Remove(string id)
{ {
_Alarms.DeleteOne(alarm => alarm.Id == id); _Alarms.DeleteOne(alarm => alarm.Id == id);

View File

@ -44,7 +44,7 @@ namespace MyCore.Services
return _Devices.Find<Device>(h => h.HomeId == homeId).ToList(); return _Devices.Find<Device>(h => h.HomeId == homeId).ToList();
} }
public List<Device> GetByLocation(string homeId, string roomId) public List<Device> GetByRoom(string homeId, string roomId)
{ {
return _Devices.Find<Device>(d => d.HomeId == homeId && d.RoomId == roomId).ToList(); return _Devices.Find<Device>(d => d.HomeId == homeId && d.RoomId == roomId).ToList();
} }

View File

@ -39,9 +39,9 @@ namespace MyCore.Services
return home; 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; return homeIn;
} }

View File

@ -39,7 +39,26 @@ namespace MyCore.Services
if (create) if (create)
return _HomeDatabaseService.Create(home); return _HomeDatabaseService.Create(home);
else 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;
}
} }
} }
} }