From 2ebb1dfd6033e5e0be8cb7d0ae2ef4421c27628f Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Fri, 11 Aug 2023 16:50:41 +0200 Subject: [PATCH] Add netatmo provider ! + clean code --- .../DTO/MyControlPanel/DeviceDTO.cs | 8 + .../DTO/MyControlPanel/ProviderDTO.cs | 3 + .../Models/MyControlPanel/Database/Device.cs | 16 ++ .../MyControlPanel/Database/Provider.cs | 10 +- .../Models/Providers/Netatmo/NetatmoDevice.cs | 112 ++++++++ .../Controllers/Devices/DeviceController.cs | 2 +- MyCore/Controllers/GroupController.cs | 2 +- MyCore/Controllers/RoomController.cs | 20 +- MyCore/Extensions/MqttClientService.cs | 6 +- MyCore/Services/AlarmService.cs | 4 +- MyCore/Services/AutomationService.cs | 4 +- MyCore/Services/Devices/ActionService.cs | 2 +- MyCore/Services/Devices/DeviceService.cs | 171 ++++++++++- .../SupportedDevices/NetatmoService.cs | 267 ++++++++++++++++++ .../Database/ProviderDatabaseService.cs | 8 +- .../MyControlPanel/ProviderService.cs | 9 +- 16 files changed, 623 insertions(+), 21 deletions(-) create mode 100644 MyCore.Interfaces/Models/Providers/Netatmo/NetatmoDevice.cs create mode 100644 MyCore/Services/Devices/SupportedDevices/NetatmoService.cs diff --git a/MyCore.Interfaces/DTO/MyControlPanel/DeviceDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/DeviceDTO.cs index 8745c5f..6d8f3b3 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/DeviceDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/DeviceDTO.cs @@ -157,5 +157,13 @@ namespace MyCore.Interfaces.DTO public bool IsLinkQuality { get; set; } // Example: 84 LQI public int LinkQuality { get; set; } // Example: 84 LQI + + public bool IsCO2 { get; set; } + + public int CO2 { get; set; } + + public bool IsNoise { get; set; } + + public int Noise { get; set; } // decibel } } diff --git a/MyCore.Interfaces/DTO/MyControlPanel/ProviderDTO.cs b/MyCore.Interfaces/DTO/MyControlPanel/ProviderDTO.cs index 30e92f3..e1d5afd 100644 --- a/MyCore.Interfaces/DTO/MyControlPanel/ProviderDTO.cs +++ b/MyCore.Interfaces/DTO/MyControlPanel/ProviderDTO.cs @@ -12,10 +12,13 @@ namespace MyCore.Interfaces.DTO public string Name { get; set; } public ProviderType Type { get; set; } public string HomeId { get; set; } + public string ServiceHomeId { get; set; } public string Endpoint { get; set; } public string Username { get; set; } public string Password { get; set; } // TODO ENCRYPTED public string ApiKey { get; set; } // TODO ENCRYPTED public bool Active { get; set; } + public string Value { get; set; } + public string ValueRefresh { get; set; } } } diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs index 3294a4d..47b16ed 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Device.cs @@ -240,6 +240,18 @@ namespace MyCore.Interfaces.Models [BsonElement("LinkQuality")] public int LinkQuality { get; set; } // Example: 84 LQI + [BsonElement("IsCO2")] + public bool IsCO2 { get; set; } + + [BsonElement("CO2")] + public int CO2 { get; set; } + + [BsonElement("IsNoise")] + public bool IsNoise { get; set; } + + [BsonElement("Noise")] + public int Noise { get; set; } // decibel + public DeviceSummaryDTO ToSummaryDTO() { @@ -319,6 +331,10 @@ namespace MyCore.Interfaces.Models Voltage = Voltage, IsLinkQuality = IsLinkQuality, LinkQuality = LinkQuality, + IsCO2 = IsCO2, + CO2 = CO2, + IsNoise = IsNoise, + Noise = Noise, }; } } diff --git a/MyCore.Interfaces/Models/MyControlPanel/Database/Provider.cs b/MyCore.Interfaces/Models/MyControlPanel/Database/Provider.cs index 00e4629..2525b8a 100644 --- a/MyCore.Interfaces/Models/MyControlPanel/Database/Provider.cs +++ b/MyCore.Interfaces/Models/MyControlPanel/Database/Provider.cs @@ -46,6 +46,12 @@ namespace MyCore.Interfaces.Models [BsonRequired] public string Value { get; set; } // AS in kiwix => Access Token, Code, etc // TODO ENCRYPTED Hash with peper from appSettings etc + [BsonElement("ValueRefresh")] + public string ValueRefresh { get; set; } // TODO Refresh token + + [BsonElement("ServiceHomeId")] + public string ServiceHomeId { get; set; } + [BsonElement("Active")] [BsonRequired] public bool Active { get; set; } @@ -58,6 +64,7 @@ namespace MyCore.Interfaces.Models Type = Type, Name = Name, HomeId = HomeId, + ServiceHomeId = ServiceHomeId, Endpoint = Endpoint, /*Username = Username, Password = Password, @@ -72,6 +79,7 @@ namespace MyCore.Interfaces.Models arlo, meross, yeelight, - zigbee2mqtt + zigbee2mqtt, + netatmo } } diff --git a/MyCore.Interfaces/Models/Providers/Netatmo/NetatmoDevice.cs b/MyCore.Interfaces/Models/Providers/Netatmo/NetatmoDevice.cs new file mode 100644 index 0000000..2df99c6 --- /dev/null +++ b/MyCore.Interfaces/Models/Providers/Netatmo/NetatmoDevice.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Interfaces.Models +{ + public class NetatmoDevice + { + public string id { get; set; } + public string type { get; set; } + public string name { get; set; } + public int setup_date { get; set; } + public string room_id { get; set; } + public string bridge { get; set; } + } + + public class NetatmoRoom + { + public string id { get; set; } + public string name { get; set; } + public string type { get; set; } + public List module_ids { get; set; } + } + + public class NetatmoUser + { + public string id { get; set; } + public string email { get; set; } + public string language { get; set; } + public string locale { get; set; } + public int feel_like_algorithm { get; set; } + public int unit_pressure { get; set; } + public int unit_system { get; set; } + public int unit_wind { get; set; } + } + + public class NetatmoPerson + { + public string id { get; set; } + public string pseudo { get; set; } + public string url { get; set; } + } + + public class NetatmoHome + { + public string id { get; set; } + public string name { get; set; } + public int altitude { get; set; } + public List coordinates { get; set; } + public string country { get; set; } + public string timezone { get; set; } + public List rooms { get; set; } + public List modules { get; set; } + public string temperature_control_mode { get; set; } + public string therm_mode { get; set; } + public int therm_setpoint_default_duration { get; set; } + public List persons { get; set; } + } + + public class NetatmoCoachDevice + { + public string _id { get; set; } + public string station_name { get; set; } + public int date_setup { get; set; } + public int last_setup { get; set; } + public string type { get; set; } + public int last_status_store { get; set; } + public string module_name { get; set; } + public int firmware { get; set; } + public int wifi_status { get; set; } + public bool reachable { get; set; } + public bool co2_calibrating { get; set; } + public List data_type { get; set; } + public NetatmoPlace place { get; set; } + public NetatmoDashboardData dashboard_data { get; set; } + } + + public class NetatmoPlace + { + public int altitude { get; set; } + public string city { get; set; } + public string country { get; set; } + public string timezone { get; set; } + public List location { get; set; } + } + + public class NetatmoDashboardData + { + public int time_utc { get; set; } + public decimal Temperature { get; set; } + public int CO2 { get; set; } + public decimal Humidity { get; set; } + public int Noise { get; set; } + public decimal Pressure { get; set; } + public decimal AbsolutePressure { get; set; } + public int health_idx { get; set; } + public decimal min_temp { get; set; } + public decimal max_temp { get; set; } + public int date_max_temp { get; set; } + public int date_min_temp { get; set; } + } + + public class NetatmoTokenResponse + { + public List scope { get; set; } + public string access_token { get; set; } + public string refresh_token { get; set; } + public int expires_in { get; set; } + public int expire_in { get; set; } + } +} diff --git a/MyCore/Controllers/Devices/DeviceController.cs b/MyCore/Controllers/Devices/DeviceController.cs index c780b5d..e2bcbcd 100644 --- a/MyCore/Controllers/Devices/DeviceController.cs +++ b/MyCore/Controllers/Devices/DeviceController.cs @@ -62,7 +62,7 @@ namespace MyCore.Controllers foreach (var device in devicesSummaryDTO) { - device.ProviderName = _ProviderDatabaseService.GetById(homeId, device.ProviderId).Name; + device.ProviderName = _ProviderDatabaseService.GetById(device.ProviderId).Name; } return new OkObjectResult(devicesSummaryDTO); diff --git a/MyCore/Controllers/GroupController.cs b/MyCore/Controllers/GroupController.cs index a87134b..8737305 100644 --- a/MyCore/Controllers/GroupController.cs +++ b/MyCore/Controllers/GroupController.cs @@ -226,7 +226,7 @@ namespace MyCore.Service.Controllers if (!HomeService.IsExist(_HomeDatabaseService, homeId)) throw new KeyNotFoundException("Home not found"); - Provider provider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); + Provider provider = _ProviderDatabaseService.GetByType(homeId, ProviderType.zigbee2mqtt); if (provider == null) throw new KeyNotFoundException("Zigbee2mqtt provider not found"); diff --git a/MyCore/Controllers/RoomController.cs b/MyCore/Controllers/RoomController.cs index 79e61d6..7da1980 100644 --- a/MyCore/Controllers/RoomController.cs +++ b/MyCore/Controllers/RoomController.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.Devices.SupportedDevices; using MyCore.Services; using MyCore.Services.Devices; using MyCore.Services.MyControlPanel; @@ -25,17 +26,19 @@ namespace MyCore.Service.Controllers private HomeDatabaseService _HomeDatabaseService; private RoomDatabaseService _RoomDatabaseService; private DeviceDatabaseService _DeviceDatabaseService; + private ProviderDatabaseService _ProviderDatabaseService; private RoomService _RoomService; private readonly IMqttClientService _mqttClientService; //private readonly IMqttOnlineClientService _mqttOnlineClientService; - public RoomController(HomeDatabaseService homeDatabaseService, RoomDatabaseService roomDatabaseService, DeviceDatabaseService deviceDatabaseService, RoomService roomService, MqttClientServiceProvider provider)//, MqttClientOnlineServiceProvider onlineProvider) + public RoomController(HomeDatabaseService homeDatabaseService, RoomDatabaseService roomDatabaseService, DeviceDatabaseService deviceDatabaseService, RoomService roomService, MqttClientServiceProvider provider, ProviderDatabaseService providerDatabaseService)//, MqttClientOnlineServiceProvider onlineProvider) { this._HomeDatabaseService = homeDatabaseService; this._RoomDatabaseService = roomDatabaseService; this._DeviceDatabaseService = deviceDatabaseService; this._RoomService = roomService; this._mqttClientService = provider.MqttClientService; + this._ProviderDatabaseService = providerDatabaseService; //this._mqttOnlineClientService = onlineProvider.MqttOnlineClientService; } @@ -74,7 +77,20 @@ namespace MyCore.Service.Controllers try { List rooms = _RoomDatabaseService.GetAll(homeId); - List roomMainDetailDTOs = new List(); + List roomMainDetailDTOs = new List(); + + Provider provider = _ProviderDatabaseService.GetByType(homeId, ProviderType.netatmo); + // refresh netatmo data + if (provider != null) + { + NetatmoService netatmoService = new NetatmoService(null, null, provider.Value, provider.ValueRefresh, provider, _ProviderDatabaseService); + List netatmoHomes = netatmoService.GetHomeData(); + List netatmoCoachDevices = netatmoService.GetHomeCoachsData(); + + // Update local data + DeviceService.CreateOrUpdateNetatmoDevices(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, netatmoHomes, netatmoCoachDevices, provider); + } + foreach (var room in rooms) { RoomMainDetailDTO roomMainDetailDTO = room.ToMainDetailsDTO(_DeviceDatabaseService.GetByRoom(room.Id).Select(d => d.ToDTO()).ToList()); diff --git a/MyCore/Extensions/MqttClientService.cs b/MyCore/Extensions/MqttClientService.cs index 052f687..8bec237 100644 --- a/MyCore/Extensions/MqttClientService.cs +++ b/MyCore/Extensions/MqttClientService.cs @@ -250,7 +250,7 @@ namespace Mqtt.Client.AspNetCore.Services try { var devices = JsonConvert.DeserializeObject>(message); - var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); + var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(homeId, ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { @@ -275,7 +275,7 @@ namespace Mqtt.Client.AspNetCore.Services try { var devices = JsonConvert.DeserializeObject>(message); - var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); + var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(homeId, ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { @@ -301,7 +301,7 @@ namespace Mqtt.Client.AspNetCore.Services { var groupsConvert = JsonConvert.DeserializeObject>(message); - var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); + var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(homeId, ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { diff --git a/MyCore/Services/AlarmService.cs b/MyCore/Services/AlarmService.cs index a322833..b72405c 100644 --- a/MyCore/Services/AlarmService.cs +++ b/MyCore/Services/AlarmService.cs @@ -192,7 +192,7 @@ namespace MyCore.Service.Services { case ActionType.DEVICE: var deviceAction = _DeviceDatabaseService.GetById(action.DeviceId); - var providerActionTest = _ProviderDatabaseService.GetById(home.Id, action.ProviderId); + var providerActionTest = _ProviderDatabaseService.GetById(action.ProviderId); DeviceNameForAction = deviceAction.Name; actionDeviceToTest = deviceAction; @@ -225,7 +225,7 @@ namespace MyCore.Service.Services break; } - var providerAction = _ProviderDatabaseService.GetById(home.Id, action.ProviderId); + var providerAction = _ProviderDatabaseService.GetById(action.ProviderId); // Check if device exist if (actionDeviceToTest != null && providerAction != null) diff --git a/MyCore/Services/AutomationService.cs b/MyCore/Services/AutomationService.cs index 4faf078..5830d34 100644 --- a/MyCore/Services/AutomationService.cs +++ b/MyCore/Services/AutomationService.cs @@ -128,7 +128,7 @@ namespace MyCore.Service.Services { case ActionType.DEVICE: var deviceAction = deviceDatabaseService.GetById(action.DeviceId); - var providerActionTest = providerDatabaseService.GetById(homeId, action.ProviderId); + var providerActionTest = providerDatabaseService.GetById(action.ProviderId); DeviceNameForAction = deviceAction.Name; actionDeviceToTest = deviceAction; @@ -161,7 +161,7 @@ namespace MyCore.Service.Services break; } - var providerAction = providerDatabaseService.GetById(homeId, action.ProviderId); + var providerAction = providerDatabaseService.GetById(action.ProviderId); // Check if device exist if (actionDeviceToTest != null && providerAction != null) diff --git a/MyCore/Services/Devices/ActionService.cs b/MyCore/Services/Devices/ActionService.cs index f500ce3..f059d30 100644 --- a/MyCore/Services/Devices/ActionService.cs +++ b/MyCore/Services/Devices/ActionService.cs @@ -37,7 +37,7 @@ namespace MyCore.Services.Devices break; } - currentProvider = Enum.IsDefined(typeof(ProviderType), providerFromTopic) ? _ProviderDatabaseService.GetByType((ProviderType) Enum.Parse(typeof(ProviderType), providerFromTopic.ToLower())) : null; + currentProvider = Enum.IsDefined(typeof(ProviderType), providerFromTopic) ? _ProviderDatabaseService.GetByType(home.Id, (ProviderType) Enum.Parse(typeof(ProviderType), providerFromTopic.ToLower())) : null; if (currentProvider != null) { diff --git a/MyCore/Services/Devices/DeviceService.cs b/MyCore/Services/Devices/DeviceService.cs index 0071589..238c3e3 100644 --- a/MyCore/Services/Devices/DeviceService.cs +++ b/MyCore/Services/Devices/DeviceService.cs @@ -1,17 +1,23 @@ -using Microsoft.AspNetCore.Mvc; +using DevExpress.Data.Mask; +using Microsoft.AspNetCore.Mvc; using Mqtt.Client.AspNetCore.Services; using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; using MyCore.Interfaces.Models.MyControlPanel; +using MyCore.Service.Services.Devices.SupportedDevices; using MyCore.Services.MyControlPanel; using Newtonsoft.Json; +using Swashbuckle.AspNetCore.Swagger; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; using System.Linq; using System.Net.Http; using System.Security.Authentication; +using System.Security.Claims; using System.Threading.Tasks; +using System.Xml.Linq; namespace MyCore.Services.Devices { @@ -125,6 +131,44 @@ namespace MyCore.Services.Devices // Todo structure SupportedOperations device.SupportedOperations = deviceDetailDTO.SupportedOperations; + // TODO CHECK HERE IF NOT ERASING DATA .. on zigbee connection + + device.IsContact = deviceDetailDTO.IsContact; + device.Contact = deviceDetailDTO.Contact; + device.IsIlluminance = deviceDetailDTO.IsIlluminance; + device.Illuminance = deviceDetailDTO.Illuminance != null ? deviceDetailDTO.Illuminance.Value : 0; + device.IsBrightness = deviceDetailDTO.IsBrightness; + device.Brightness = deviceDetailDTO.Brightness; + device.IsState = deviceDetailDTO.IsState; + device.State = deviceDetailDTO.State; + device.IsColorTemp = deviceDetailDTO.IsColorTemp; + device.ColorTemp = deviceDetailDTO.ColorTemp; + device.IsColorXY = deviceDetailDTO.IsColorXY; + device.ColorX = deviceDetailDTO.ColorX; + device.ColorY = deviceDetailDTO.ColorY; + device.IsOccupation = deviceDetailDTO.IsOccupation; + device.Occupation = deviceDetailDTO.Occupation; + device.IsAlarm = deviceDetailDTO.IsAlarm; + device.Alarm = deviceDetailDTO.Alarm; + device.IsTemperature = deviceDetailDTO.IsTemperature; + device.Temperature = deviceDetailDTO.Temperature != null ? deviceDetailDTO.Temperature.Value : 0; + device.IsHumidity = deviceDetailDTO.IsHumidity; + device.Humidity = deviceDetailDTO.Humidity != null ? deviceDetailDTO.Humidity.Value : 0; + device.IsPressure = deviceDetailDTO.IsPressure; + device.Pressure = deviceDetailDTO.Pressure != null ? deviceDetailDTO.Pressure.Value : 0; + device.IsConsumption = deviceDetailDTO.IsConsumption; + device.Consumption = deviceDetailDTO.Consumption; + device.IsCurrentPower = deviceDetailDTO.IsCurrentPower; + device.CurrentPower = deviceDetailDTO.CurrentPower; + device.IsVoltage = deviceDetailDTO.IsVoltage; + device.Voltage = deviceDetailDTO.Voltage; + device.IsLinkQuality = deviceDetailDTO.IsLinkQuality; + device.LinkQuality = deviceDetailDTO.LinkQuality; + device.IsCO2 = deviceDetailDTO.IsCO2; + device.CO2 = deviceDetailDTO.CO2; + device.IsNoise = deviceDetailDTO.IsNoise; + device.Noise = deviceDetailDTO.Noise; + if (deviceDetailDTO.SupportedOperations != null) { foreach (var supportedOperation in deviceDetailDTO.SupportedOperations) @@ -159,6 +203,12 @@ namespace MyCore.Services.Devices List yeelightDevices = await YeelightService.GetDevices(); devices = CreateYeelightDevices(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, yeelightDevices, provider); break; + case ProviderType.netatmo: + NetatmoService netatmoService = new NetatmoService(provider.Username, provider.Password, provider.Value, provider.ValueRefresh, provider, _ProviderDatabaseService); + List netatmoHomes = netatmoService.GetHomeData(); + List netatmoCoachDevices = netatmoService.GetHomeCoachsData(); + devices = CreateOrUpdateNetatmoDevices(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, netatmoHomes, netatmoCoachDevices, provider); + break; case ProviderType.zigbee2mqtt: if (MqttClientService.devices.Count > 0) { @@ -570,6 +620,125 @@ namespace MyCore.Services.Devices return new Dictionary>() { { "createdDevices", createdYeelightDevices } }; // TODO Check if exist not supported devices } + public static Dictionary> CreateOrUpdateNetatmoDevices(DeviceDatabaseService _DeviceDatabaseService, ProviderDatabaseService _ProviderDatabaseService, RoomDatabaseService _RoomDatabaseService, string homeId, List netatmoHomes, List netatmoCoachDevices, Provider provider) + { + List createdNetatmoDevices = new List(); + + List existingDevices = _DeviceDatabaseService.GetByProviderId(provider.Id); + + List netatmoDevices = new List(); + foreach (var netatmoHome in netatmoHomes) + { + netatmoDevices.AddRange(netatmoHome.modules); + } + + //netatmoDevices = netatmoDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd.id)).ToList(); + + foreach (var netatmoDevice in netatmoDevices) + { + bool isExist = existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(netatmoDevice.id); + var device = existingDevices.FirstOrDefault(ed => ed.ProviderId == provider.Id && ed.ServiceIdentification == netatmoDevice.id); + + DeviceDetailDTO deviceDetailDTO = isExist && device != null ? device.ToDTO() : new DeviceDetailDTO(); + + if (!isExist) + { + deviceDetailDTO.CreatedDate = DateTime.Now; + deviceDetailDTO.ServiceIdentification = netatmoDevice.id; + deviceDetailDTO.ProviderId = provider.Id; + deviceDetailDTO.ProviderName = provider.Name; + deviceDetailDTO.Name = netatmoDevice.name; + deviceDetailDTO.Description = netatmoDevice.name; // As description not exist, put name in description + deviceDetailDTO.Model = netatmoDevice.type; + deviceDetailDTO.IpAddress = netatmoDevice.bridge; + deviceDetailDTO.ManufacturerName = provider.Type.ToString(); + deviceDetailDTO.Model = netatmoDevice.type; + } + + deviceDetailDTO.ConnectionStatus = ConnectionStatus.Connected; + switch (netatmoDevice.type) + { + case "NCO": //Détecteur de Monoxyde de Carbone Intelligent + deviceDetailDTO.Type = DeviceType.Environment; + deviceDetailDTO.Battery = true; + break; + case "NACamera": + deviceDetailDTO.Type = DeviceType.Camera; + break; + case "NAPlug": + deviceDetailDTO.Type = DeviceType.Gateway; + break; + case "NATherm1": + deviceDetailDTO.Type = DeviceType.Thermostat; + deviceDetailDTO.IsTemperature = true; + break; + case "NRV": + deviceDetailDTO.Type = DeviceType.Valve; + deviceDetailDTO.IsTemperature = true; + break; + } + deviceDetailDTO.MeansOfCommunications = new List + { + MeansOfCommunication.Wifi + }; + deviceDetailDTO.UpdatedDate = DateTime.Now; + createdNetatmoDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, deviceDetailDTO, !isExist)); + } + + //netatmoCoachDevices = netatmoCoachDevices.Where(yd => !existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(yd._id)).ToList(); + + foreach (var netatmoCoachDevice in netatmoCoachDevices) + { + bool isExist = existingDevices.Select(ed => ed.ServiceIdentification).ToList().Contains(netatmoCoachDevice._id); + var device = existingDevices.FirstOrDefault(ed => ed.ProviderId == provider.Id && ed.ServiceIdentification == netatmoCoachDevice._id); + + DeviceDetailDTO deviceDetailDTO = isExist && device != null ? device.ToDTO() : new DeviceDetailDTO(); + + if (!isExist) + { + deviceDetailDTO.CreatedDate = DateTime.Now; + deviceDetailDTO.ServiceIdentification = netatmoCoachDevice._id; + deviceDetailDTO.ProviderId = provider.Id; + deviceDetailDTO.ProviderName = provider.Name; + deviceDetailDTO.Name = netatmoCoachDevice.station_name; + deviceDetailDTO.Description = netatmoCoachDevice.module_name; + deviceDetailDTO.Model = netatmoCoachDevice.type; + deviceDetailDTO.ManufacturerName = provider.Type.ToString(); + } + + deviceDetailDTO.ConnectionStatus = netatmoCoachDevice.reachable ? ConnectionStatus.Connected : ConnectionStatus.Unknown; + switch (netatmoCoachDevice.type) + { + case "NHC": //Home coach classique (?) + deviceDetailDTO.Type = DeviceType.Environment; + deviceDetailDTO.Battery = false; + deviceDetailDTO.FirmwareVersion = netatmoCoachDevice.firmware.ToString(); + //deviceDetailDTO.SupportedOperations = netatmoCoachDevice.data_type; + deviceDetailDTO.IsTemperature = true; + deviceDetailDTO.Temperature = netatmoCoachDevice.dashboard_data.Temperature; + deviceDetailDTO.IsHumidity = true; + deviceDetailDTO.Humidity = netatmoCoachDevice.dashboard_data.Humidity; + deviceDetailDTO.IsPressure = true; + deviceDetailDTO.Pressure = netatmoCoachDevice.dashboard_data.Pressure; + deviceDetailDTO.IsCO2 = true; + deviceDetailDTO.CO2 = netatmoCoachDevice.dashboard_data.CO2; + deviceDetailDTO.IsNoise = true; + deviceDetailDTO.Noise = netatmoCoachDevice.dashboard_data.Noise; + //TODO healthy index ! + break; + } + deviceDetailDTO.MeansOfCommunications = new List + { + MeansOfCommunication.Wifi + }; + + deviceDetailDTO.UpdatedDate = DateTime.Now; + createdNetatmoDevices.Add(CreateOrUpdate(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, deviceDetailDTO, !isExist)); + } + + return new Dictionary>() { { "createdDevices", createdNetatmoDevices } }; + } + public static Device UpdateDeviceFromZigbeeEvent(Device device, Zigbee2MqttRequest zigbee2MqttRequest) { if (zigbee2MqttRequest.state != null) diff --git a/MyCore/Services/Devices/SupportedDevices/NetatmoService.cs b/MyCore/Services/Devices/SupportedDevices/NetatmoService.cs new file mode 100644 index 0000000..8bb2f23 --- /dev/null +++ b/MyCore/Services/Devices/SupportedDevices/NetatmoService.cs @@ -0,0 +1,267 @@ +using EvtSource; +using MyCore.Interfaces.Models; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System; +using static MyCore.Services.ArloService; +using Newtonsoft.Json; +using System.Security.Authentication; +using MyCore.Interfaces.Models.Providers.Zigbee.Zigbee2Mqtt; +using DevExpress.Utils.OAuth; +using MyCore.Interfaces.DTO; +using System.Net; +using MyCore.Services; +using System.Linq; + +namespace MyCore.Service.Services.Devices.SupportedDevices +{ + public class NetatmoService + { + private const string _netatmoUrl = "https://api.netatmo.net"; + + private string _refreshTokenUrl = $"{_netatmoUrl}/oauth2/token"; + private string _userUrl = $"{_netatmoUrl}/api/getuser"; + private string _thermostatUrl = $"{_netatmoUrl}/api/getthermostatsdata"; + private string _homeCoachUrl = $"{_netatmoUrl}/api/gethomecoachsdata"; + private string _homeDataUrl = $"{_netatmoUrl}/api/homesdata"; + private string _homeStatusUrl = $"{_netatmoUrl}/api/homestatus"; + + private static string _token; + private static string _refreshToken; + private static int _expiresIn; + + private static string _clientId = "5d2c232d3693590011438281"; + private static string _clientSecret = "qNyKemRyFa73qgUdV3QnEn295TNa88ZX6"; + + private List netatmoHomes; + private List netatmoCoachDevices; + private List netatmoDevices; + private NetatmoUser netatmoUser; + + private Provider _provider; + private ProviderDatabaseService _ProviderDatabaseService; + + public NetatmoService(string username, string password, string token, string refreshToken, Provider provider, ProviderDatabaseService providerDatabaseService) // TODO + { + try + { + /*_email = username; + _password = password;*/ + + _ProviderDatabaseService = providerDatabaseService; + _provider = provider; + + if (token == null || refreshToken == null) { + // TODO get access token todo + } else { + _token = token; + _refreshToken = refreshToken; + } + + // LOGIN + /*var loginTask = Task.Run(() => RequestURI(new Uri(_refreshTokenUrl), RequestType.Post)); + loginTask.Wait(); + + if (loginTask.Result != "") + { + //RESULT TOKEN + var data = ((JObject)JsonConvert.DeserializeObject(loginTask.Result))["data"]; + //_token = JsonConvert.DeserializeObject(data.ToString()); + }/*/ + } + catch (Exception e) + { + _token = null; + _refreshToken = null; + throw new AuthenticationException("Bad service username or password"); + } + } + + public void RefreshToken() + { + var refreskTokentask = Task.Run(() => RequestURI(new Uri(_refreshTokenUrl), RequestType.Post, Request.Refresh, new List())); + refreskTokentask.Wait(); + + if (refreskTokentask.Result != "") + { + var result = ((JObject)JsonConvert.DeserializeObject(refreskTokentask.Result)); + NetatmoTokenResponse netatmoTokenResponse = JsonConvert.DeserializeObject(result.ToString()); + + _token = netatmoTokenResponse.access_token; + _refreshToken = netatmoTokenResponse.refresh_token; + _expiresIn = netatmoTokenResponse.expires_in; + + //UPDATE provider + var providerToUpdate = _ProviderDatabaseService.GetById(_provider.Id); + providerToUpdate.Value = _token; + providerToUpdate.ValueRefresh = _refreshToken; + _ProviderDatabaseService.Update(_provider.Id, providerToUpdate); + } + else + throw new HttpRequestException("Error refreshing the access token"); + } + + public List GetHomeData() + { + // GET Home data + var homeTask = Task.Run(() => RequestURI(new Uri(_homeDataUrl), RequestType.Get, Request.HomeData, new List())); + homeTask.Wait(); + + if (homeTask.Result != "") + { + var users = ((JObject)JsonConvert.DeserializeObject(homeTask.Result))["body"]["user"]; + netatmoUser = JsonConvert.DeserializeObject(users.ToString()); + + var homes = ((JObject)JsonConvert.DeserializeObject(homeTask.Result))["body"]["homes"]; + netatmoHomes = JsonConvert.DeserializeObject>(homes.ToString()); + + if (netatmoHomes.Count > 0) + { + var home = netatmoHomes.FirstOrDefault(); + + //UPDATE provider + var providerToUpdate = _ProviderDatabaseService.GetById(_provider.Id); + providerToUpdate.ServiceHomeId = home.id; + _ProviderDatabaseService.Update(_provider.Id, providerToUpdate); + } + } + else + throw new HttpRequestException("Error retrieving netatmo home data"); + + return netatmoHomes; + } + + public List GetHomeCoachsData() + { + // GET Home data + var homeCoachTask = Task.Run(() => RequestURI(new Uri(_homeCoachUrl), RequestType.Get, Request.HomeData, new List())); + homeCoachTask.Wait(); + + if (homeCoachTask.Result != "") + { + var user = ((JObject)JsonConvert.DeserializeObject(homeCoachTask.Result))["body"]["user"]; + netatmoUser = JsonConvert.DeserializeObject(user.ToString()); + + var devices = ((JObject)JsonConvert.DeserializeObject(homeCoachTask.Result))["body"]["devices"]; + netatmoCoachDevices = JsonConvert.DeserializeObject>(devices.ToString()); + } + else + throw new HttpRequestException("Error retrieving netatmo home data"); + + return netatmoCoachDevices; + } + + public List GetHomeStatus(string type) + { + // GET Home status + var homeStatusTask = Task.Run(() => RequestURI(new Uri(_homeStatusUrl), RequestType.Get, Request.HomeStatus, new List())); + homeStatusTask.Wait(); + + if (homeStatusTask.Result != "") + { + + var homes = ((JObject)JsonConvert.DeserializeObject(homeStatusTask.Result))["body"]["homes"]; + netatmoHomes = JsonConvert.DeserializeObject>(homes.ToString()); + } + else + throw new HttpRequestException("Error retrieving netatmo home status"); + + return null; + } + + public enum Request + { + Refresh, + HomeData, + HomeStatus + } + + async Task RequestURI(Uri u, RequestType requestType, Request request, List parames) // TODO dictonnary for params + { + try + { + var response = string.Empty; + var body = ""; + HttpContent c = null; + + var attempsLeft = request == Request.Refresh ? 1 : 2; + + while (attempsLeft > 0) + { + attempsLeft -= 1; + using (var client = new HttpClient()) + { + switch (request) + { + case Request.Refresh: + body = $"grant_type=refresh_token&client_id={_clientId}&client_secret={_clientSecret}&refresh_token={_refreshToken}"; + c = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"); + break; + case Request.HomeData: + body = $"home_id={_provider.ServiceHomeId}"; //{ _refreshToken} TODO + c = new StringContent(body, Encoding.UTF8, "multipart/form-data"); // TO TEST + break; + case Request.HomeStatus: + body = $"home_id={_provider.ServiceHomeId}&device_types=NAPlug"; //{ _refreshToken} TODO + c = new StringContent(body, Encoding.UTF8, "multipart/form-data"); // TO TEST + break; + } + + client.DefaultRequestHeaders.Add("User-Agent", "okhttp/3.6.0"); + + if (_token != null && request != Request.Refresh) + client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}"); + + if (requestType == RequestType.Get) + { + HttpResponseMessage result = await client.GetAsync(u); + if (result.StatusCode == HttpStatusCode.Forbidden) + { + RefreshToken(); + } + if (result.IsSuccessStatusCode) + { + attempsLeft = 0; // ok ! + response = await result.Content.ReadAsStringAsync(); + } + } + + if (requestType == RequestType.Post) + { + if (request == Request.Refresh) + { + //c.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); + } + else + { + c.Headers.Add("Content-Type", "application/json"); + } + + HttpResponseMessage result = await client.PostAsync(u, c); + if (result.StatusCode == HttpStatusCode.Forbidden) + { + RefreshToken(); + } + if (result.IsSuccessStatusCode) + { + attempsLeft = 0; // ok ! + response = await result.Content.ReadAsStringAsync(); + } + } + } + + } + + return response; + } + catch (Exception e) + { + Console.WriteLine("NetatmoService - An error occured in RequestURI"); + return null; + } + } + } +} diff --git a/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs b/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs index 5ae842f..645b216 100644 --- a/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs +++ b/MyCore/Services/MyControlPanel/Database/ProviderDatabaseService.cs @@ -23,9 +23,9 @@ namespace MyCore.Services return _Providers.Find(p => p.HomeId == homeId).ToList(); } - public Provider GetById(string homeId, string id) + public Provider GetById(string id) { - return _Providers.Find(p => p.Id == id && p.HomeId == homeId).FirstOrDefault(); + return _Providers.Find(p => p.Id == id).FirstOrDefault(); } public List GetByHomeId(string homeId) @@ -43,9 +43,9 @@ namespace MyCore.Services return _Providers.Find(p => p.Name == name).FirstOrDefault(); } - public Provider GetByType(ProviderType type) + public Provider GetByType(string homeId, ProviderType type) { - return _Providers.Find(p => p.Type == type).FirstOrDefault(); + return _Providers.Find(p => p.Type == type && p.HomeId == homeId).FirstOrDefault(); } public bool IsExist(string id) diff --git a/MyCore/Services/MyControlPanel/ProviderService.cs b/MyCore/Services/MyControlPanel/ProviderService.cs index 2f99714..9fe1ba4 100644 --- a/MyCore/Services/MyControlPanel/ProviderService.cs +++ b/MyCore/Services/MyControlPanel/ProviderService.cs @@ -12,7 +12,7 @@ namespace MyCore.Services.MyControlPanel { public static bool IsExist(ProviderDatabaseService _ProviderDatabaseService, string homeId, string providerId) { - return _ProviderDatabaseService.GetById(homeId, providerId) != null ? true : false; + return _ProviderDatabaseService.GetById(providerId) != null ? true : false; } public static List GetAll(ProviderDatabaseService _ProviderDatabaseService, string homeId) @@ -27,7 +27,7 @@ namespace MyCore.Services.MyControlPanel provider = new Provider(); else { - provider = _ProviderDatabaseService.GetById(homeId, providerDTO.Id); + provider = _ProviderDatabaseService.GetById(providerDTO.Id); } provider.Type = providerDTO.Type; @@ -36,7 +36,10 @@ namespace MyCore.Services.MyControlPanel provider.HomeId = providerDTO.HomeId; provider.Username = providerDTO.Username; provider.Password = providerDTO.Password; + provider.Value = providerDTO.Value; + provider.ValueRefresh = providerDTO.ValueRefresh; provider.ApiKey = providerDTO.ApiKey; + provider.ServiceHomeId = providerDTO.ServiceHomeId; provider.Active = true; if (create) @@ -47,7 +50,7 @@ namespace MyCore.Services.MyControlPanel public static Provider GetProviderById(ProviderDatabaseService _ProviderDatabaseService, string homeId, string providerId) { - return _ProviderDatabaseService.GetById(homeId, providerId); + return _ProviderDatabaseService.GetById(providerId); } } }