From b2804db7f156d852bfc2967497a343af9f7586d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sat, 30 Nov 2024 17:29:02 +0100 Subject: [PATCH 1/3] fix(TeslaFleetApiService): update logging text on charging state change --- TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index a1dfe9acc..7f084b1fc 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -501,7 +501,7 @@ private async Task IsCarDataRefreshNeeded(DtoCar car) logger.LogTrace("Fleet Telemetry Client connected, check fleet telemetry changes and do not request Fleet API after commands."); if (await FleetTelemetryValueChanged(car.Id, CarValueType.IsCharging, latestRefresh, earliestDetectedChange).ConfigureAwait(false)) { - logger.LogDebug("Send a request as Fleet Telemetry detected a change in is charging in state."); + logger.LogDebug("Send a request as Fleet Telemetry detected a change in is charging state."); return true; } From 74445f8a168f9731300258fa9022e0516d8d4758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 1 Dec 2024 14:13:50 +0100 Subject: [PATCH 2/3] fix(TeslaFleetApiService): add latest value before last refresh to detect if calling Tesla API is required --- .../Server/Services/TeslaFleetApiService.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index 7f084b1fc..60acd2343 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -642,7 +642,31 @@ private async Task> GetValuesSince(int carId InvalidValue = c.InvalidValue, }) .ToListAsync(); - return values; + + var lastBeforeStartTimeValue = await teslaSolarChargerContext.CarValueLogs + .Where(c => c.Type == carValueType + && c.Source == CarValueSource.FleetTelemetry + && c.CarId == carId + && c.Timestamp <= startTime) + .OrderByDescending(c => c.Timestamp) + .Select(c => new CarValueLogTimeStampAndValues + { + Timestamp = c.Timestamp, + DoubleValue = c.DoubleValue, + IntValue = c.IntValue, + StringValue = c.StringValue, + UnknownValue = c.UnknownValue, + BooleanValue = c.BooleanValue, + InvalidValue = c.InvalidValue, + }) + .FirstOrDefaultAsync(); // Use FirstOrDefault to get the single latest value before startTime + + // Combine the results + if (lastBeforeStartTimeValue != null) + { + values.Add(lastBeforeStartTimeValue); // Add the value before startTime if it exists + } + return values.OrderByDescending(v => v.Timestamp).ToList(); } From bb4261b49f65dd93b8d37ce80047b5e4b8dfe51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 1 Dec 2024 15:45:59 +0100 Subject: [PATCH 3/3] fix(TeslaFleetApiService): improve car data requests --- .../Server/Scheduling/JobManager.cs | 2 +- .../Server/Services/TeslaFleetApiService.cs | 124 +++++++++--------- TeslaSolarCharger/Server/appsettings.json | 3 +- 3 files changed, 67 insertions(+), 62 deletions(-) diff --git a/TeslaSolarCharger/Server/Scheduling/JobManager.cs b/TeslaSolarCharger/Server/Scheduling/JobManager.cs index f31f76a0c..d00212425 100644 --- a/TeslaSolarCharger/Server/Scheduling/JobManager.cs +++ b/TeslaSolarCharger/Server/Scheduling/JobManager.cs @@ -100,7 +100,7 @@ public async Task StartJobs() .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(59)).Build(); var vehicleDataRefreshTrigger = TriggerBuilder.Create().WithIdentity("vehicleDataRefreshTrigger") - .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(11)).Build(); + .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(configurationWrapper.CarRefreshAfterCommandSeconds())).Build(); var teslaMateChargeCostUpdateTrigger = TriggerBuilder.Create() .WithIdentity("teslaMateChargeCostUpdateTrigger") diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index 60acd2343..2bd67a421 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -495,28 +495,53 @@ private async Task IsCarDataRefreshNeeded(DtoCar car) } logger.LogDebug("Latest car refresh: {latestRefresh}", latestRefresh); var currentUtcDate = dateTimeProvider.UtcNow(); - var earliestDetectedChange = currentUtcDate.AddSeconds(-configurationWrapper.CarRefreshAfterCommandSeconds()); + var latestChangeToDetect = currentUtcDate.AddSeconds(-configurationWrapper.CarRefreshAfterCommandSeconds()); if (fleetTelemetryWebSocketService.IsClientConnected(car.Vin)) { + var latestDetectedChange = latestRefresh.AddSeconds(-configurationWrapper.CarRefreshAfterCommandSeconds()); logger.LogTrace("Fleet Telemetry Client connected, check fleet telemetry changes and do not request Fleet API after commands."); - if (await FleetTelemetryValueChanged(car.Id, CarValueType.IsCharging, latestRefresh, earliestDetectedChange).ConfigureAwait(false)) + if (latestRefresh > latestChangeToDetect) { - logger.LogDebug("Send a request as Fleet Telemetry detected a change in is charging state."); - return true; + logger.LogDebug("Do not refresh data for car {vin} as latest refresh is {latestRefresh} and earliest change to detect is {earliestChangeToDetect}", car.Vin, latestRefresh, latestChangeToDetect); } - - if (await FleetTelemetryValueChanged(car.Id, CarValueType.IsPluggedIn, latestRefresh, earliestDetectedChange).ConfigureAwait(false)) + else { - logger.LogDebug("Send a request as Fleet Telemetry detected a change in plugged in state."); - return true; - } + if (await FleetTelemetryValueChanged(car.Id, CarValueType.IsCharging, latestDetectedChange, latestChangeToDetect).ConfigureAwait(false)) + { + logger.LogDebug("Send a request as Fleet Telemetry detected a change in is charging state."); + return true; + } - var values = await GetValuesSince(car.Id, CarValueType.ChargeAmps, earliestDetectedChange).ConfigureAwait(false); - if (AnyValueChanged(latestRefresh, values) && values.Any(v => v.DoubleValue == 0)) - { - logger.LogDebug("Send a request as Fleet Telemetry detected at least one 0 value in charging amps."); - return true; + if (await FleetTelemetryValueChanged(car.Id, CarValueType.IsPluggedIn, latestDetectedChange, latestChangeToDetect).ConfigureAwait(false)) + { + logger.LogDebug("Send a request as Fleet Telemetry detected a change in plugged in state."); + return true; + } + + var latestValueBeforeLatestRefresh = await GetLatestValueBeforeTimeStamp(car.Id, CarValueType.ChargeAmps, latestDetectedChange).ConfigureAwait(false); + var latestValue = await GetLatestValueBeforeTimeStamp(car.Id, CarValueType.ChargeAmps, latestChangeToDetect).ConfigureAwait(false); + if (latestValue != default && latestValueBeforeLatestRefresh == default) + { + logger.LogDebug("Send a request as the first charging amps value was detected."); + return true; + } + + if (latestValue != default && latestValueBeforeLatestRefresh != default) + { + List values = + [ + latestValue, + latestValueBeforeLatestRefresh, + ]; + if (AnyValueChanged(values) && values.Any(v => v.DoubleValue == 0)) + { + logger.LogDebug("Send a request as Fleet Telemetry detected at least one 0 value in charging amps."); + return true; + } + } + } + } else { @@ -548,7 +573,7 @@ private async Task IsCarDataRefreshNeeded(DtoCar car) logger.LogDebug("Latest command Timestamp: {latestCommandTimeStamp}", latestCommandTimeStamp); //Do not waste a request if the latest command was in the last few seconds. Request the next time instead - if (latestCommandTimeStamp > earliestDetectedChange) + if (latestCommandTimeStamp > latestChangeToDetect) { logger.LogDebug("Do not refresh data as on {latestCommandTimeStamp} there was a command sent to the car.", latestCommandTimeStamp); return false; @@ -588,28 +613,31 @@ private async Task IsCarDataRefreshNeeded(DtoCar car) return false; } - private async Task FleetTelemetryValueChanged(int carId, CarValueType carValueType, DateTime latestRefresh, DateTime currentUtcDate) - { - logger.LogTrace("{method}({carId}, {carValueType}, {latestRefresh}, {currentUtcDate})", nameof(FleetTelemetryValueChanged), carId, carValueType, latestRefresh, currentUtcDate); - var values = await GetValuesSince(carId, carValueType, currentUtcDate.AddSeconds(-configurationWrapper.CarRefreshAfterCommandSeconds())).ConfigureAwait(false); - return AnyValueChanged(latestRefresh, values); - } - - private bool AnyValueChanged(DateTime latestRefresh, List values) + private async Task FleetTelemetryValueChanged(int carId, CarValueType carValueType, DateTime latestRefresh, DateTime latestChangeToDetect) { - logger.LogTrace("{method}({latestRefresh}, {@values})", nameof(AnyValueChanged), latestRefresh, values); - // Ensure there are at least two values to compare - if (values.Count < 2) + logger.LogTrace("{method}({carId}, {carValueType}, {latestRefresh}, {latestChangeToDetect})", nameof(FleetTelemetryValueChanged), carId, carValueType, latestRefresh, latestChangeToDetect); + var values = new List(); + var latestValueBeforeLatestRefresh = await GetLatestValueBeforeTimeStamp(carId, carValueType, latestRefresh).ConfigureAwait(false); + var latestValue = await GetLatestValueBeforeTimeStamp(carId, carValueType, latestChangeToDetect).ConfigureAwait(false); + if (latestValue != default && latestValueBeforeLatestRefresh == default) { - return false; + //Return true if before the latest refresh there was no value, this is only relevant on new TSC installations + return true; } - - // Check if the latest value is after the latest refresh time - if (values[0].Timestamp <= latestRefresh) + if (latestValueBeforeLatestRefresh != default) { - return false; + values.Add(latestValueBeforeLatestRefresh); } + if (latestValue != default) + { + values.Add(latestValue); + } + return AnyValueChanged(values); + } + private bool AnyValueChanged(List values) + { + logger.LogTrace("{method}({@values})", nameof(AnyValueChanged), values); // Check if any of the properties have changed among all values var doubleValuesChanged = values.Select(v => v.DoubleValue).Distinct().Count() > 1; var intValuesChanged = values.Select(v => v.IntValue).Distinct().Count() > 1; @@ -622,32 +650,14 @@ private bool AnyValueChanged(DateTime latestRefresh, List> GetValuesSince(int carId, CarValueType carValueType, DateTime startTime) + private async Task GetLatestValueBeforeTimeStamp(int carId, CarValueType carValueType, DateTime timestamp) { - logger.LogTrace("{method}({carId}, {carValueType}, {startTime})", nameof(GetValuesSince), carId, carValueType, startTime); - var values = await teslaSolarChargerContext.CarValueLogs - .Where(c => c.Type == carValueType - && c.Source == CarValueSource.FleetTelemetry - && c.CarId == carId - && c.Timestamp > startTime) - .OrderByDescending(c => c.Timestamp) - .Select(c => new CarValueLogTimeStampAndValues - { - Timestamp = c.Timestamp, - DoubleValue = c.DoubleValue, - IntValue = c.IntValue, - StringValue = c.StringValue, - UnknownValue = c.UnknownValue, - BooleanValue = c.BooleanValue, - InvalidValue = c.InvalidValue, - }) - .ToListAsync(); - + logger.LogTrace("{method}({carId}, {carValueType}, {timestamp})", nameof(GetLatestValueBeforeTimeStamp), carId, carValueType, timestamp); var lastBeforeStartTimeValue = await teslaSolarChargerContext.CarValueLogs .Where(c => c.Type == carValueType && c.Source == CarValueSource.FleetTelemetry && c.CarId == carId - && c.Timestamp <= startTime) + && c.Timestamp < timestamp) .OrderByDescending(c => c.Timestamp) .Select(c => new CarValueLogTimeStampAndValues { @@ -659,14 +669,8 @@ private async Task> GetValuesSince(int carId BooleanValue = c.BooleanValue, InvalidValue = c.InvalidValue, }) - .FirstOrDefaultAsync(); // Use FirstOrDefault to get the single latest value before startTime - - // Combine the results - if (lastBeforeStartTimeValue != null) - { - values.Add(lastBeforeStartTimeValue); // Add the value before startTime if it exists - } - return values.OrderByDescending(v => v.Timestamp).ToList(); + .FirstOrDefaultAsync(); + return lastBeforeStartTimeValue; } diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index f0893cfda..4da2bbefa 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -69,7 +69,8 @@ "AwattarBaseUrl": "https://api.awattar.de/v1/marketdata", "BleBaseUrl": null, "MaxTravelSpeedMetersPerSecond": 30, - "CarRefreshAfterCommandSeconds": 12, + //This is also the intervall for the car refresh + "CarRefreshAfterCommandSeconds": 11, "BleUsageStopAfterErrorSeconds": 300, "FleetApiRefreshIntervalSeconds": 500, "FleetTelemetryApiUrl": "wss://api.fleet-telemetry.teslasolarcharger.de/ws?",