diff --git a/README.md b/README.md index 924f7bed2..c39097c47 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ The current UI can display the car's names including SOC and SOC Limit + one But ### Charge Modes Currently there are three different charge modes available: -1. **PV only**: Only PV energy is used to charge unless there is a min SOC Level set. If so the software tries to start charging with maximum power to reach the desired SOC Level in time +1. **PV only**: Only solar energy is used to charge. You can set a SOC level which should be reached at a specific date and time. If solar energy is not enough to reach the set soc level in time, the car starts charging at full speed. Note: To let this work, you have to specify `usable kWh` in the car settings section. 1. **Maximum Power**: Car charges with maximum available power 1. **Min SoC + PV**: If plugged in the car starts charging with maximum power until set Min SoC is reached. After that only PV Power is used to charge the car. diff --git a/SmartTeslaAmpSetter.Tests/Services/GridService.cs b/SmartTeslaAmpSetter.Tests/Services/GridService.cs new file mode 100644 index 000000000..f019e85a7 --- /dev/null +++ b/SmartTeslaAmpSetter.Tests/Services/GridService.cs @@ -0,0 +1,26 @@ +using Xunit; +using Xunit.Abstractions; + +namespace SmartTeslaAmpSetter.Tests.Services; + +public class GridService : TestBase +{ + public GridService(ITestOutputHelper outputHelper) + : base(outputHelper) + { + } + + [Theory] + [InlineData("384.8746")] + [InlineData("384")] + [InlineData("384.0")] + [InlineData("384.147")] + public void Can_extract_Integers_From_String(string value) + { + var gridService = Mock.Create(); + var intValue = gridService.GetIntegerFromString(value); + + Assert.Equal(384, intValue); + } + +} \ No newline at end of file diff --git a/SmartTeslaAmpSetter/Client/Pages/Index.razor b/SmartTeslaAmpSetter/Client/Pages/Index.razor index 774bf0613..26c249215 100644 --- a/SmartTeslaAmpSetter/Client/Pages/Index.razor +++ b/SmartTeslaAmpSetter/Client/Pages/Index.razor @@ -46,12 +46,12 @@ else @if (car.CarConfiguration.ChargeMode == ChargeMode.PvOnly) {

- +

- +

diff --git a/SmartTeslaAmpSetter/Server/Contracts/IConfigService.cs b/SmartTeslaAmpSetter/Server/Contracts/IConfigService.cs index 78d10007b..3d6cc7d8b 100644 --- a/SmartTeslaAmpSetter/Server/Contracts/IConfigService.cs +++ b/SmartTeslaAmpSetter/Server/Contracts/IConfigService.cs @@ -8,7 +8,7 @@ public interface IConfigService { Task GetSettings(); ChargeMode ChangeChargeMode(int carId); - void UpdateCarConfiguration(int id, CarConfiguration carConfiguration); + void UpdateCarConfiguration(int carId, CarConfiguration carConfiguration); List GetCarBasicConfigurations(); void UpdateCarBasicConfiguration(int carId, CarBasicConfiguration carBasicConfiguration); } \ No newline at end of file diff --git a/SmartTeslaAmpSetter/Server/MqttHelper.cs b/SmartTeslaAmpSetter/Server/MqttHelper.cs index 50e491a51..b77cbf2c5 100644 --- a/SmartTeslaAmpSetter/Server/MqttHelper.cs +++ b/SmartTeslaAmpSetter/Server/MqttHelper.cs @@ -236,6 +236,9 @@ private void UpdateCar(TeslaMateValue value) case "driving": car.CarState.State = CarState.Driving; break; + case "updating": + car.CarState.State = CarState.Updating; + break; default: _logger.LogWarning("Unknown car state deteckted: {carState}", value.Value); car.CarState.State = CarState.Unknown; diff --git a/SmartTeslaAmpSetter/Server/Services/ChargingService.cs b/SmartTeslaAmpSetter/Server/Services/ChargingService.cs index 18e399fc9..0d95ee0d1 100644 --- a/SmartTeslaAmpSetter/Server/Services/ChargingService.cs +++ b/SmartTeslaAmpSetter/Server/Services/ChargingService.cs @@ -65,6 +65,9 @@ public async Task SetNewChargingValues(bool onlyUpdateValues = false) var relevantCars = _settings.Cars.Where(c => relevantCarIds.Any(r => c.Id == r)).ToList(); + _logger.LogTrace("Relevant cars: {@relevantCars}", relevantCars); + _logger.LogTrace("Irrelevant cars: {@irrlevantCars}", irrelevantCars); + foreach (var relevantCar in relevantCars) { relevantCar.CarState.ChargingPowerAtHome = relevantCar.CarState.ChargingPower; @@ -174,7 +177,8 @@ private async Task ChangeCarAmp(Car relevantCar, int ampToRegulate) //Falls MaxPower als Charge Mode: Leistung auf maximal if (relevantCar.CarConfiguration.ChargeMode == ChargeMode.MaxPower || relevantCar.CarState.AutoFullSpeedCharge) { - _logger.LogDebug("Max Power Charging"); + _logger.LogDebug("Max Power Charging: ChargeMode: {chargeMode}, AutoFullSpeedCharge: {autofullspeedCharge}", + relevantCar.CarConfiguration.ChargeMode, relevantCar.CarState.AutoFullSpeedCharge); if (relevantCar.CarState.ChargerActualCurrent < maxAmpPerCar) { var ampToSet = maxAmpPerCar; @@ -234,7 +238,7 @@ private async Task ChangeCarAmp(Car relevantCar, int ampToRegulate) UpdateEarliestTimesAfterSwitch(relevantCar.Id); } //Falls nicht ladend, aber laden soll beginnen - else if (finalAmpsToSet > minAmpPerCar && relevantCar.CarState.ChargerActualCurrent == 0) + else if (finalAmpsToSet >= minAmpPerCar && relevantCar.CarState.ChargerActualCurrent == 0) { _logger.LogDebug("Charging should start"); var earliestSwitchOn = EarliestSwitchOn(relevantCar.Id); diff --git a/SmartTeslaAmpSetter/Server/Services/ConfigService.cs b/SmartTeslaAmpSetter/Server/Services/ConfigService.cs index eeda0deb5..debfca457 100644 --- a/SmartTeslaAmpSetter/Server/Services/ConfigService.cs +++ b/SmartTeslaAmpSetter/Server/Services/ConfigService.cs @@ -28,20 +28,23 @@ public async Task GetSettings() public ChargeMode ChangeChargeMode(int carId) { + _logger.LogTrace("{method},({param1})", nameof(ChangeChargeMode), carId); var car = _settings.Cars.First(c => c.Id == carId); car.CarConfiguration.ChargeMode = car.CarConfiguration.ChargeMode.Next(); car.CarState.AutoFullSpeedCharge = false; return car.CarConfiguration.ChargeMode; } - public void UpdateCarConfiguration(int id, CarConfiguration carConfiguration) + public void UpdateCarConfiguration(int carId, CarConfiguration carConfiguration) { - var existingCarIndex = _settings.Cars.FindIndex(c => c.Id == id); + _logger.LogTrace("{method}({param1}, {@param2})", nameof(UpdateCarConfiguration), carId, carConfiguration); + var existingCarIndex = _settings.Cars.FindIndex(c => c.Id == carId); _settings.Cars[existingCarIndex].CarConfiguration = carConfiguration; } public List GetCarBasicConfigurations() { + _logger.LogTrace("{method}()", nameof(GetCarBasicConfigurations)); var carSettings = new List(); foreach (var car in _settings.Cars) @@ -59,6 +62,7 @@ public List GetCarBasicConfigurations() public void UpdateCarBasicConfiguration(int carId, CarBasicConfiguration carBasicConfiguration) { + _logger.LogTrace("{method}({param1}, {@param2})", nameof(UpdateCarBasicConfiguration), carId, carBasicConfiguration); var car = _settings.Cars.First(c => c.Id == carId); car.CarConfiguration.MinimumAmpere = carBasicConfiguration.MinimumAmpere; car.CarConfiguration.MaximumAmpere = carBasicConfiguration.MaximumAmpere; diff --git a/SmartTeslaAmpSetter/Server/Services/GridService.cs b/SmartTeslaAmpSetter/Server/Services/GridService.cs index 57eed98ad..b94ac379f 100644 --- a/SmartTeslaAmpSetter/Server/Services/GridService.cs +++ b/SmartTeslaAmpSetter/Server/Services/GridService.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using System.Globalization; +using Newtonsoft.Json.Linq; using SmartTeslaAmpSetter.Server.Contracts; namespace SmartTeslaAmpSetter.Server.Services; @@ -7,11 +8,13 @@ public class GridService : IGridService { private readonly ILogger _logger; private readonly IConfiguration _configuration; + private readonly ITelegramService _telegramService; - public GridService(ILogger logger, IConfiguration configuration) + public GridService(ILogger logger, IConfiguration configuration, ITelegramService telegramService) { _logger = logger; _configuration = configuration; + _telegramService = telegramService; } public async Task GetCurrentOverage() @@ -19,10 +22,17 @@ public async Task GetCurrentOverage() _logger.LogTrace("{method}()", nameof(GetCurrentOverage)); using var httpClient = new HttpClient(); var requestUri = _configuration.GetValue("CurrentPowerToGridUrl"); + _logger.LogDebug("Using {uri} to get current overage.", requestUri); var response = await httpClient.GetAsync( requestUri) .ConfigureAwait(false); - response.EnsureSuccessStatusCode(); + + if (!response.IsSuccessStatusCode) + { + _logger.LogError("Could not get current overage. {statusCode}, {reasonPhrase}", response.StatusCode, response.ReasonPhrase); + response.EnsureSuccessStatusCode(); + } + var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var jsonPattern = _configuration.GetValue("CurrentPowerToGridJsonPattern"); @@ -34,16 +44,26 @@ public async Task GetCurrentOverage() throw new InvalidOperationException("Extracted Json Value is null")).Value(); } - if (int.TryParse(result, out var overage)) + try { + var overage = GetIntegerFromString(result); if (_configuration.GetValue("CurrentPowerToGridInvertValue")) { overage = -overage; } - return overage; + return overage ; } + catch (Exception) + { + throw new InvalidCastException($"Could not parse result {result} from uri {requestUri} to integer"); + } + + } - throw new InvalidCastException($"Could not parse result {result} from uri {requestUri} to integer"); + internal int GetIntegerFromString(string? inputString) + { + _logger.LogTrace("{method}({param})", nameof(GetIntegerFromString), inputString); + return (int) double.Parse(inputString ?? throw new ArgumentNullException(nameof(inputString)), CultureInfo.InvariantCulture); } public async Task GetCurrentInverterPower() @@ -58,14 +78,23 @@ public async Task GetCurrentOverage() var response = await httpClient.GetAsync( requestUri) .ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - if (int.TryParse(result, out var overage)) + if (!response.IsSuccessStatusCode) { - return overage; + _logger.LogWarning("Getting current inverter power did result in statuscode {statusCode} with reason {reasonPhrase}", response.StatusCode, response.ReasonPhrase); + await _telegramService.SendMessage( + $"Getting current inverter power did result in statuscode {response.StatusCode} with reason {response.ReasonPhrase}"); + return null; } + var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new InvalidCastException($"Could not parse result {result} from uri {requestUri} to integer"); + try + { + return GetIntegerFromString(result); + } + catch (Exception) + { + throw new InvalidCastException($"Could not parse result {result} from uri {requestUri} to integer"); + } } } \ No newline at end of file diff --git a/SmartTeslaAmpSetter/Server/Services/TelegramService.cs b/SmartTeslaAmpSetter/Server/Services/TelegramService.cs index 5c831da3a..2344e04a5 100644 --- a/SmartTeslaAmpSetter/Server/Services/TelegramService.cs +++ b/SmartTeslaAmpSetter/Server/Services/TelegramService.cs @@ -33,6 +33,9 @@ public async Task SendMessage(string message) } var requestUri = CreateRequestUri(message, botKey, channel); + + httpClient.Timeout = TimeSpan.FromSeconds(1); + var response = await httpClient.GetAsync( requestUri); diff --git a/SmartTeslaAmpSetter/Shared/Enums/CarState.cs b/SmartTeslaAmpSetter/Shared/Enums/CarState.cs index 20e57e4a5..28dfac9b4 100644 --- a/SmartTeslaAmpSetter/Shared/Enums/CarState.cs +++ b/SmartTeslaAmpSetter/Shared/Enums/CarState.cs @@ -8,5 +8,6 @@ public enum CarState Charging, Suspended, Driving, + Updating, Unknown, } \ No newline at end of file