Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(TeslaFleetApiService): add latest value before last refresh to detect if calling Tesla API is required #1667

Merged
merged 2 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TeslaSolarCharger/Server/Scheduling/JobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
102 changes: 65 additions & 37 deletions TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -495,28 +495,53 @@ private async Task<bool> 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<CarValueLogTimeStampAndValues> 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
{
Expand Down Expand Up @@ -548,7 +573,7 @@ private async Task<bool> 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;
Expand Down Expand Up @@ -588,28 +613,31 @@ private async Task<bool> IsCarDataRefreshNeeded(DtoCar car)
return false;
}

private async Task<bool> 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<CarValueLogTimeStampAndValues> values)
private async Task<bool> 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<CarValueLogTimeStampAndValues>();
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<CarValueLogTimeStampAndValues> 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;
Expand All @@ -622,14 +650,14 @@ private bool AnyValueChanged(DateTime latestRefresh, List<CarValueLogTimeStampAn
return doubleValuesChanged || intValuesChanged || stringValuesChanged || unknownValuesChanged || booleanValuesChanged || invalidValuesChanged;
}

private async Task<List<CarValueLogTimeStampAndValues>> GetValuesSince(int carId, CarValueType carValueType, DateTime startTime)
private async Task<CarValueLogTimeStampAndValues?> GetLatestValueBeforeTimeStamp(int carId, CarValueType carValueType, DateTime timestamp)
{
logger.LogTrace("{method}({carId}, {carValueType}, {startTime})", nameof(GetValuesSince), carId, carValueType, startTime);
var values = await teslaSolarChargerContext.CarValueLogs
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
{
Expand All @@ -641,8 +669,8 @@ private async Task<List<CarValueLogTimeStampAndValues>> GetValuesSince(int carId
BooleanValue = c.BooleanValue,
InvalidValue = c.InvalidValue,
})
.ToListAsync();
return values;
.FirstOrDefaultAsync();
return lastBeforeStartTimeValue;
}


Expand Down
3 changes: 2 additions & 1 deletion TeslaSolarCharger/Server/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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?",
Expand Down
Loading