From c075979e766f1d77cf6bbc5a8c895dbaca6b08bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 03:47:17 +0000 Subject: [PATCH 01/20] build(deps): Bump MudBlazor from 6.14.0 to 6.15.0 Bumps [MudBlazor](https://github.com/MudBlazor/MudBlazor) from 6.14.0 to 6.15.0. - [Release notes](https://github.com/MudBlazor/MudBlazor/releases) - [Changelog](https://github.com/MudBlazor/MudBlazor/blob/dev/CHANGELOG.md) - [Commits](https://github.com/MudBlazor/MudBlazor/compare/v6.14.0...v6.15.0) --- updated-dependencies: - dependency-name: MudBlazor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj index 2320f4e6c..06ade325e 100644 --- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj +++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj @@ -18,7 +18,7 @@ - + From 301a7fadbbbbcce8f7d1082ed91743bf1750d3d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 03:23:49 +0000 Subject: [PATCH 02/20] build(deps): Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj index 3a22e5778..4a3fcab53 100644 --- a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj +++ b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj @@ -9,7 +9,7 @@ - + From 8e84eae1d1bc21adc8959ab77f35732ec7f9fa34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 03:54:58 +0000 Subject: [PATCH 03/20] build(deps): Bump AutoMapper from 12.0.1 to 13.0.1 Bumps [AutoMapper](https://github.com/AutoMapper/AutoMapper) from 12.0.1 to 13.0.1. - [Release notes](https://github.com/AutoMapper/AutoMapper/releases) - [Commits](https://github.com/AutoMapper/AutoMapper/compare/v12.0.1...v13.0.1) --- updated-dependencies: - dependency-name: AutoMapper dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 1e6ee379a..fc1f178e4 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -33,7 +33,7 @@ --> - + From 02942526246bd7458d2bb9544241319cfc20638c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 03:58:56 +0000 Subject: [PATCH 04/20] build(deps): Bump Microsoft.AspNetCore.Mvc.NewtonsoftJson Bumps [Microsoft.AspNetCore.Mvc.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 8.0.1 to 8.0.2. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Mvc.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 1e6ee379a..3a7b4d050 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -35,7 +35,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From a1253007c8309be840c3ea02d64e8c3b2491a0fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:00:13 +0000 Subject: [PATCH 05/20] build(deps): Bump Microsoft.AspNetCore.Components.WebAssembly and Microsoft.Extensions.Options Bumps [Microsoft.AspNetCore.Components.WebAssembly](https://github.com/dotnet/aspnetcore) and [Microsoft.Extensions.Options](https://github.com/dotnet/runtime). These dependencies needed to be updated together. Updates `Microsoft.AspNetCore.Components.WebAssembly` from 8.0.1 to 8.0.2 - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2) Updates `Microsoft.Extensions.Options` from 8.0.1 to 8.0.2 - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Components.WebAssembly dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.Extensions.Options dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj index 2320f4e6c..900b0f819 100644 --- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj +++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj @@ -16,7 +16,7 @@ - + From 1c320ed56089ba1d75b9073f38109f4015efe7d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:00:28 +0000 Subject: [PATCH 06/20] build(deps): Bump Microsoft.AspNetCore.OpenApi from 8.0.1 to 8.0.2 Bumps [Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/aspnetcore) from 8.0.1 to 8.0.2. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.OpenApi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Plugins.Solax/Plugins.Solax.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins.Solax/Plugins.Solax.csproj b/Plugins.Solax/Plugins.Solax.csproj index 746969dbf..ffa71da2c 100644 --- a/Plugins.Solax/Plugins.Solax.csproj +++ b/Plugins.Solax/Plugins.Solax.csproj @@ -8,7 +8,7 @@ - + From 815345577a643e5a8d007d5d494f897ebdeac65d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 03:26:48 +0000 Subject: [PATCH 07/20] build(deps): Bump xunit.runner.visualstudio from 2.5.6 to 2.5.7 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.5.6 to 2.5.7. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/compare/2.5.6...2.5.7) --- updated-dependencies: - dependency-name: xunit.runner.visualstudio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj index 3a22e5778..d6c602e2e 100644 --- a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj +++ b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj @@ -19,7 +19,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 9d74f8168129096e8037169e71078940c681376f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 18 Feb 2024 17:59:09 +0100 Subject: [PATCH 08/20] fix(TeslaFleetApiService): do not delete token if one time invalid message from tesla --- .../Server/Services/TeslaFleetApiService.cs | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index 50bbdca34..f9b3b2bd6 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -491,10 +491,15 @@ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameo } var teslaCommandResultResponse = JsonConvert.DeserializeObject>(responseString); - if (!response.IsSuccessStatusCode) + if (response.IsSuccessStatusCode) + { + await RemoveUnecessaryInvalidTokenKey().ConfigureAwait(false); + } + else { await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi), $"Sending command to Tesla API resulted in non succes status code: {response.StatusCode} : Command name:{fleetApiRequest.RequestUrl}, Content data:{contentData}. Response string: {responseString}").ConfigureAwait(false); + logger.LogError("Sending command to Tesla API resulted in non succes status code: {statusCode} : Command name:{commandName}, Content data:{contentData}. Response string: {responseString}", response.StatusCode, fleetApiRequest.RequestUrl, contentData, responseString); await HandleNonSuccessTeslaApiStatusCodes(response.StatusCode, accessToken, responseString, vin).ConfigureAwait(false); } @@ -505,6 +510,7 @@ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameo await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi), $"Result of command request is false {fleetApiRequest.RequestUrl}, {contentData}. Response string: {responseString}") .ConfigureAwait(false); + logger.LogError("Result of command request is false {fleetApiRequest.RequestUrl}, {contentData}. Response string: {responseString}", fleetApiRequest.RequestUrl, contentData, responseString); await HandleUnsignedCommands(vehicleCommandResult).ConfigureAwait(false); } } @@ -578,7 +584,7 @@ public async Task RefreshTokenAsync() return; case FleetApiTokenState.TokenUnauthorized: logger.LogError("Your refresh token is unauthorized, create a new token."); - return; + break; case FleetApiTokenState.NotReceived: break; case FleetApiTokenState.Expired: @@ -712,14 +718,6 @@ private async Task GetAccessTokenAndRefreshWhenNeededAsync() .OrderByDescending(t => t.ExpiresAtUtc) .FirstAsync().ConfigureAwait(false); var minimumTokenLifeTime = TimeSpan.FromMinutes(5); - var isCurrentRefreshTokenUnauthorized = await teslaSolarChargerContext.TscConfigurations - .Where(c => c.Key == constants.TokenRefreshUnauthorized) - .AnyAsync().ConfigureAwait(false); - if (isCurrentRefreshTokenUnauthorized) - { - logger.LogError("Token is unauthorized"); - throw new InvalidDataException("Current Tesla Fleet Api Token is unauthorized"); - } if (token.ExpiresAtUtc < (dateTimeProvider.UtcNow() + minimumTokenLifeTime)) { logger.LogInformation("Token is expired. Getting new token."); @@ -735,10 +733,15 @@ private async Task GetAccessTokenAndRefreshWhenNeededAsync() encodedContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var response = await httpClient.PostAsync(tokenUrl, encodedContent).ConfigureAwait(false); var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - if (!response.IsSuccessStatusCode) + if (response.IsSuccessStatusCode) + { + await RemoveUnecessaryInvalidTokenKey().ConfigureAwait(false); + } + else { await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi), $"Refreshing token did result in non success status code. Response status code: {response.StatusCode} Response string: {responseString}").ConfigureAwait(false); + logger.LogError("Refreshing token did result in non success status code. Response status code: {statusCode} Response string: {responseString}", response.StatusCode, responseString); await HandleNonSuccessTeslaApiStatusCodes(response.StatusCode, token, responseString).ConfigureAwait(false); } response.EnsureSuccessStatusCode(); @@ -760,8 +763,7 @@ private async Task HandleNonSuccessTeslaApiStatusCodes(HttpStatusCode statusCode if (statusCode == HttpStatusCode.Unauthorized) { logger.LogError( - "Your token or refresh token is invalid. Very likely you have changed your Tesla password. Response: {responseString}", responseString); - teslaSolarChargerContext.TeslaTokens.Remove(token); + "Your token or refresh token is invalid. Very likely you have changed your Tesla password. Should have been valid until: {expiresAt}, Response: {responseString}", token.ExpiresAtUtc, responseString); teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration() { Key = constants.TokenRefreshUnauthorized, Value = responseString, @@ -770,7 +772,6 @@ private async Task HandleNonSuccessTeslaApiStatusCodes(HttpStatusCode statusCode else if (statusCode == HttpStatusCode.Forbidden) { logger.LogError("You did not select all scopes, so TSC can't send commands to your car. Response: {responseString}", responseString); - teslaSolarChargerContext.TeslaTokens.Remove(token); teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration() { Key = constants.TokenMissingScopes, Value = responseString, @@ -794,4 +795,19 @@ private async Task HandleNonSuccessTeslaApiStatusCodes(HttpStatusCode statusCode await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); } + + + private async Task RemoveUnecessaryInvalidTokenKey() + { + logger.LogTrace("{method}()", nameof(RemoveUnecessaryInvalidTokenKey)); + var cconfigEntriesToRemove = await teslaSolarChargerContext.TscConfigurations + .Where(c => c.Key == constants.TokenRefreshUnauthorized) + .ToListAsync().ConfigureAwait(false); + if (cconfigEntriesToRemove.Any()) + { + logger.LogWarning("Token was marked as invalid although it is not."); + teslaSolarChargerContext.TscConfigurations.RemoveRange(cconfigEntriesToRemove); + await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); + } + } } From 9084e528eb8be2d08984a0e9d64492b780ea4ccc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:19:40 +0000 Subject: [PATCH 09/20] build(deps): Bump xunit from 2.6.6 to 2.7.0 Bumps [xunit](https://github.com/xunit/xunit) from 2.6.6 to 2.7.0. - [Commits](https://github.com/xunit/xunit/compare/2.6.6...2.7.0) --- updated-dependencies: - dependency-name: xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj index 38921246d..dff97e717 100644 --- a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj +++ b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj @@ -18,7 +18,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 5993f5f0fb2f00030cc4526d901474c30989e26f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:20:26 +0000 Subject: [PATCH 10/20] build(deps): Bump Microsoft.AspNetCore.Components.WebAssembly.Server Bumps [Microsoft.AspNetCore.Components.WebAssembly.Server](https://github.com/dotnet/aspnetcore) from 8.0.1 to 8.0.2. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Components.WebAssembly.Server dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 5dc0d8282..a3c2d5a42 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -34,7 +34,7 @@ - + all From 23a6ecaeaf97d38a19bc8b91972cb7a7ab28193a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:24:37 +0000 Subject: [PATCH 11/20] build(deps): Bump Microsoft.AspNetCore.Components.WebAssembly.DevServer Bumps [Microsoft.AspNetCore.Components.WebAssembly.DevServer](https://github.com/dotnet/aspnetcore) from 8.0.1 to 8.0.2. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Components.WebAssembly.DevServer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj index 312d3105d..1c4322cbd 100644 --- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj +++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj @@ -17,7 +17,7 @@ - + From 6238a277207a4a8c2de39d4e4177ed653314ed2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 18 Feb 2024 18:16:24 +0100 Subject: [PATCH 12/20] feat(IndexRazor): display warning to not share Installation ID --- .../Components/TextShortenComponent.razor | 81 +++++++++++++++++++ TeslaSolarCharger/Client/Pages/Index.razor | 13 ++- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 TeslaSolarCharger/Client/Components/TextShortenComponent.razor diff --git a/TeslaSolarCharger/Client/Components/TextShortenComponent.razor b/TeslaSolarCharger/Client/Components/TextShortenComponent.razor new file mode 100644 index 000000000..c4be789af --- /dev/null +++ b/TeslaSolarCharger/Client/Components/TextShortenComponent.razor @@ -0,0 +1,81 @@ +@inject IJSRuntime JavaScriptRuntime +@inject ISnackbar Snackbar + + +@if (string.IsNullOrWhiteSpace(InputString)) +{ + +} +else if (!_hasShortenedText) +{ + @InputString +} +else +{ + + + @_textToDisplay + + +
+ @TooltipText +
+
+
+} + +@code { + [Parameter] + public string? InputString { get; set; } + + [Parameter] + public int MaxLength { get; set; } = 10; + + [Parameter] + public bool ShouldDisplayTruncatedCharCount { get; set; } = true; + + [Parameter] + public string? TooltipText { get; set; } + + [Parameter] + public EventCallback OnCopyClicked { get; set; } + + + private bool _hasShortenedText; + + private string? _textToDisplay; + + protected override void OnInitialized() + { + _textToDisplay = TruncateAndAppend(InputString, MaxLength, ShouldDisplayTruncatedCharCount); + if (!string.Equals(_textToDisplay, InputString)) + { + _hasShortenedText = true; + } + } + + + private string? TruncateAndAppend(string? input, int maxLength, bool shouldDisplayTruncatedCharCount) + { + if (string.IsNullOrEmpty(input) || input.Length <= maxLength) + { + return input; + } + const int charsPerPlaceholder = 3; + var numberOfPlaceHolders = shouldDisplayTruncatedCharCount ? 2 : 1; + var baseTemplate = "{0}..."; + var suffixTemplate = shouldDisplayTruncatedCharCount ? baseTemplate + "(+{1})" : baseTemplate; + var suffixLength = shouldDisplayTruncatedCharCount ? suffixTemplate.Length - (numberOfPlaceHolders * charsPerPlaceholder) + (input.Length - (MaxLength - suffixTemplate.Length + numberOfPlaceHolders * charsPerPlaceholder)).ToString().Length : 3; + var truncatedLength = maxLength - suffixLength; + var truncatedString = input.Substring(0, truncatedLength); + return string.Format(suffixTemplate, truncatedString, input.Length - truncatedLength); + } + + private async Task CopyToClipBoard() + { + await JavaScriptRuntime.InvokeVoidAsync("navigator.clipboard.writeText", InputString); + Snackbar.Add("Copied to clipboard.", Severity.Success); + await OnCopyClicked.InvokeAsync(); + } + +} \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Pages/Index.razor b/TeslaSolarCharger/Client/Pages/Index.razor index c1b5ad51c..4efe76dd0 100644 --- a/TeslaSolarCharger/Client/Pages/Index.razor +++ b/TeslaSolarCharger/Client/Pages/Index.razor @@ -414,7 +414,13 @@ else

}
- Installation ID: @_installationId + Installation ID: +
Language settings: @CultureInfo.CurrentCulture @@ -695,4 +701,9 @@ else _testingFleetApiCarIds.Remove(carId); } + private void ShowInstallationIdWarning() + { + Snackbar.Add("Do not share the ID with anyone", Severity.Warning); + } + } \ No newline at end of file From a1f34fe6bc3e968f914c519dde8b1b7d1591c483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 18 Feb 2024 22:53:06 +0100 Subject: [PATCH 13/20] feat(TeslaFleetApiService): log warning on expired token --- 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 f9b3b2bd6..de4c3745b 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -720,7 +720,7 @@ private async Task GetAccessTokenAndRefreshWhenNeededAsync() var minimumTokenLifeTime = TimeSpan.FromMinutes(5); if (token.ExpiresAtUtc < (dateTimeProvider.UtcNow() + minimumTokenLifeTime)) { - logger.LogInformation("Token is expired. Getting new token."); + logger.LogWarning("Token is expired. Getting new token."); using var httpClient = new HttpClient(); var tokenUrl = "https://auth.tesla.com/oauth2/v3/token"; var requestData = new Dictionary From e02565694e2557b8a66bfb3e1227370a4a7d14d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 04:07:07 +0000 Subject: [PATCH 14/20] build(deps): Bump Quartz from 3.8.0 to 3.8.1 Bumps [Quartz](https://github.com/quartznet/quartznet) from 3.8.0 to 3.8.1. - [Release notes](https://github.com/quartznet/quartznet/releases) - [Changelog](https://github.com/quartznet/quartznet/blob/main/changelog.md) - [Commits](https://github.com/quartznet/quartznet/compare/v3.8.0...v3.8.1) --- updated-dependencies: - dependency-name: Quartz dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj | 2 +- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj index 1bf763b70..857a0fa27 100644 --- a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj +++ b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj @@ -11,7 +11,7 @@ - + diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index a3c2d5a42..adaba2d09 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -49,7 +49,7 @@ - + From 0a35ff712701e4b87c9d1c8013085223534f2ee3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 03:04:59 +0000 Subject: [PATCH 15/20] build(deps): Bump MudBlazor and Microsoft.Extensions.Options Bumps [MudBlazor](https://github.com/MudBlazor/MudBlazor) and [Microsoft.Extensions.Options](https://github.com/dotnet/runtime). These dependencies needed to be updated together. Updates `MudBlazor` from 6.15.0 to 6.16.0 - [Release notes](https://github.com/MudBlazor/MudBlazor/releases) - [Changelog](https://github.com/MudBlazor/MudBlazor/blob/dev/CHANGELOG.md) - [Commits](https://github.com/MudBlazor/MudBlazor/compare/v6.15.0...v6.16.0) Updates `Microsoft.Extensions.Options` from 8.0.1 to 8.0.2 - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: MudBlazor dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: Microsoft.Extensions.Options dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj index 1c4322cbd..cb666676e 100644 --- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj +++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj @@ -18,7 +18,7 @@ - + From c97d6546a07d7e68ec3b84fb513c891a27911383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Tue, 20 Feb 2024 19:45:11 +0100 Subject: [PATCH 16/20] fix(TextShortenComponent): use workaround for copy on non https connections --- .../Components/TextShortenComponent.razor | 14 ++++++++--- TeslaSolarCharger/Client/wwwroot/index.html | 1 + .../Client/wwwroot/js/copyToClipboard.js | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 TeslaSolarCharger/Client/wwwroot/js/copyToClipboard.js diff --git a/TeslaSolarCharger/Client/Components/TextShortenComponent.razor b/TeslaSolarCharger/Client/Components/TextShortenComponent.razor index c4be789af..47f1754cc 100644 --- a/TeslaSolarCharger/Client/Components/TextShortenComponent.razor +++ b/TeslaSolarCharger/Client/Components/TextShortenComponent.razor @@ -73,9 +73,17 @@ else private async Task CopyToClipBoard() { - await JavaScriptRuntime.InvokeVoidAsync("navigator.clipboard.writeText", InputString); - Snackbar.Add("Copied to clipboard.", Severity.Success); - await OnCopyClicked.InvokeAsync(); + try + { + await JavaScriptRuntime.InvokeVoidAsync("copyToClipboard", InputString); + Snackbar.Add("Copied to clipboard.", Severity.Success); + await OnCopyClicked.InvokeAsync(); + } + catch(Exception e) + { + Snackbar.Add("Failed to copy to clipboard.", Severity.Error); + } + } } \ No newline at end of file diff --git a/TeslaSolarCharger/Client/wwwroot/index.html b/TeslaSolarCharger/Client/wwwroot/index.html index 24ce11797..aa3e2c95c 100644 --- a/TeslaSolarCharger/Client/wwwroot/index.html +++ b/TeslaSolarCharger/Client/wwwroot/index.html @@ -32,6 +32,7 @@ 🗙
+ diff --git a/TeslaSolarCharger/Client/wwwroot/js/copyToClipboard.js b/TeslaSolarCharger/Client/wwwroot/js/copyToClipboard.js new file mode 100644 index 000000000..2e989d08f --- /dev/null +++ b/TeslaSolarCharger/Client/wwwroot/js/copyToClipboard.js @@ -0,0 +1,25 @@ +window.copyToClipboard = async (textToCopy) => { + // Navigator clipboard api needs a secure context (https) + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(textToCopy); + } else { + // Use the 'out of viewport hidden text area' trick + const textArea = document.createElement("textarea"); + textArea.value = textToCopy; + + // Move textarea out of the viewport so it's not visible + textArea.style.position = "absolute"; + textArea.style.left = "-999999px"; + + document.body.prepend(textArea); + textArea.select(); + + try { + document.execCommand('copy'); + } catch (error) { + console.error(error); + } finally { + textArea.remove(); + } + } +} From f1a53e489eb0993b38137928cd2af4ff8fcc1853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Wed, 21 Feb 2024 18:17:15 +0100 Subject: [PATCH 17/20] feat(TeslaFleetApiService): do not create token on token usage --- .../Entities/TeslaSolarCharger/TeslaToken.cs | 1 + ...28_AddTokenUnauthorizedCounter.Designer.cs | 252 ++++++++++++++++++ ...40221111628_AddTokenUnauthorizedCounter.cs | 29 ++ .../TeslaSolarChargerContextModelSnapshot.cs | 3 + .../Contracts/IConstants.cs | 4 +- .../Values/Constants.cs | 3 + .../Server/Controllers/FleetApiController.cs | 2 +- .../Jobs/FleetApiTokenRefreshJob.cs | 18 +- .../Server/Services/BackendApiService.cs | 3 +- .../Contracts/ITeslaFleetApiService.cs | 3 +- .../Server/Services/TeslaFleetApiService.cs | 216 +++++++-------- 11 files changed, 410 insertions(+), 124 deletions(-) create mode 100644 TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.Designer.cs create mode 100644 TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.cs diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/TeslaToken.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/TeslaToken.cs index 23356bf02..4a61b6153 100644 --- a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/TeslaToken.cs +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/TeslaToken.cs @@ -9,6 +9,7 @@ public class TeslaToken public string AccessToken { get; set; } public string RefreshToken { get; set; } public string IdToken { get; set; } + public int UnauthorizedCounter { get; set; } public DateTime ExpiresAtUtc { get; set; } public TeslaFleetApiRegion Region { get; set; } } diff --git a/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.Designer.cs new file mode 100644 index 000000000..997474ca6 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.Designer.cs @@ -0,0 +1,252 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240221111628_AddTokenUnauthorizedCounter")] + partial class AddTokenUnauthorizedCounter + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.cs b/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.cs new file mode 100644 index 000000000..e0a983ed6 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240221111628_AddTokenUnauthorizedCounter.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddTokenUnauthorizedCounter : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UnauthorizedCounter", + table: "TeslaTokens", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UnauthorizedCounter", + table: "TeslaTokens"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs index f6684975f..ada501b40 100644 --- a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs +++ b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs @@ -199,6 +199,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Region") .HasColumnType("INTEGER"); + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + b.HasKey("Id"); b.ToTable("TeslaTokens"); diff --git a/TeslaSolarCharger.SharedBackend/Contracts/IConstants.cs b/TeslaSolarCharger.SharedBackend/Contracts/IConstants.cs index d1ae1829d..4ca6ca02e 100644 --- a/TeslaSolarCharger.SharedBackend/Contracts/IConstants.cs +++ b/TeslaSolarCharger.SharedBackend/Contracts/IConstants.cs @@ -13,8 +13,10 @@ public interface IConstants string InstallationIdKey { get; } string FleetApiTokenRequested { get; } - string TokenRefreshUnauthorized { get; } string TokenMissingScopes { get; } string BackupZipBaseFileName { get; } string FleetApiProxyNeeded { get; } + TimeSpan MaxTokenRequestWaitTime { get; } + TimeSpan MinTokenRestLifetime { get; } + int MaxTokenUnauthorizedCount { get; } } diff --git a/TeslaSolarCharger.SharedBackend/Values/Constants.cs b/TeslaSolarCharger.SharedBackend/Values/Constants.cs index 9e7ed4890..4c4bbf805 100644 --- a/TeslaSolarCharger.SharedBackend/Values/Constants.cs +++ b/TeslaSolarCharger.SharedBackend/Values/Constants.cs @@ -16,4 +16,7 @@ public class Constants : IConstants public string TokenRefreshUnauthorized => "TokenRefreshUnauthorized"; public string TokenMissingScopes => "TokenMissingScopes"; public string FleetApiProxyNeeded => "FleetApiProxyNeeded"; + public TimeSpan MaxTokenRequestWaitTime => TimeSpan.FromMinutes(5); + public TimeSpan MinTokenRestLifetime => TimeSpan.FromMinutes(2); + public int MaxTokenUnauthorizedCount => 5; } diff --git a/TeslaSolarCharger/Server/Controllers/FleetApiController.cs b/TeslaSolarCharger/Server/Controllers/FleetApiController.cs index d95af29e5..408cfdc02 100644 --- a/TeslaSolarCharger/Server/Controllers/FleetApiController.cs +++ b/TeslaSolarCharger/Server/Controllers/FleetApiController.cs @@ -22,7 +22,7 @@ public class FleetApiController( public Task> GetOauthUrl(string locale, string baseUrl) => backendApiService.StartTeslaOAuth(locale, baseUrl); [HttpGet] - public Task RefreshFleetApiToken() => fleetApiService.RefreshTokenAsync(); + public Task RefreshFleetApiToken() => fleetApiService.GetNewTokenFromBackend(); /// /// Note: This endpoint is only available in development environment diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs index cf5a21377..35bd5cead 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs @@ -4,20 +4,14 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; [DisallowConcurrentExecution] -public class FleetApiTokenRefreshJob : IJob +public class FleetApiTokenRefreshJob(ILogger logger, + ITeslaFleetApiService service) + : IJob { - private readonly ILogger _logger; - private readonly ITeslaFleetApiService _service; - - public FleetApiTokenRefreshJob(ILogger logger, ITeslaFleetApiService service) - { - _logger = logger; - _service = service; - } - public async Task Execute(IJobExecutionContext context) { - _logger.LogTrace("{method}({context})", nameof(Execute), context); - await _service.RefreshTokenAsync().ConfigureAwait(false); + logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.GetNewTokenFromBackend().ConfigureAwait(false); + await service.RefreshTokensIfAllowedAndNeeded().ConfigureAwait(false); } } diff --git a/TeslaSolarCharger/Server/Services/BackendApiService.cs b/TeslaSolarCharger/Server/Services/BackendApiService.cs index defab83a2..b733d76fd 100644 --- a/TeslaSolarCharger/Server/Services/BackendApiService.cs +++ b/TeslaSolarCharger/Server/Services/BackendApiService.cs @@ -39,8 +39,7 @@ public async Task> StartTeslaOAuth(string locale, string baseUr var currentTokens = await _teslaSolarChargerContext.TeslaTokens.ToListAsync().ConfigureAwait(false); _teslaSolarChargerContext.TeslaTokens.RemoveRange(currentTokens); var cconfigEntriesToRemove = await _teslaSolarChargerContext.TscConfigurations - .Where(c => c.Key == _constants.TokenRefreshUnauthorized - || c.Key == _constants.TokenMissingScopes) + .Where(c => c.Key == _constants.TokenMissingScopes) .ToListAsync().ConfigureAwait(false); _teslaSolarChargerContext.TscConfigurations.RemoveRange(cconfigEntriesToRemove); await _teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); diff --git a/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs index fc6807280..72d3d649d 100644 --- a/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs @@ -8,11 +8,12 @@ public interface ITeslaFleetApiService { Task AddNewTokenAsync(DtoTeslaTscDeliveryToken token); Task> GetFleetApiTokenState(); - Task RefreshTokenAsync(); + Task GetNewTokenFromBackend(); Task OpenChargePortDoor(int carId); Task> TestFleetApiAccess(int carId); DtoValue IsFleetApiEnabled(); DtoValue IsFleetApiProxyEnabled(); Task IsFleetApiProxyNeededInDatabase(); Task RefreshCarData(); + Task RefreshTokensIfAllowedAndNeeded(); } diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index de4c3745b..3314d7a52 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -468,7 +468,7 @@ private async Task WakeUpCarIfNeeded(int carId, CarStateEnum? carState) private async Task?> SendCommandToTeslaApi(string vin, DtoFleetApiRequest fleetApiRequest, HttpMethod httpMethod, string contentData = "{}") where T : class { logger.LogTrace("{method}({vin}, {@fleetApiRequest}, {contentData})", nameof(SendCommandToTeslaApi), vin, fleetApiRequest, contentData); - var accessToken = await GetAccessTokenAndRefreshWhenNeededAsync().ConfigureAwait(false); + var accessToken = await GetAccessToken().ConfigureAwait(false); using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.AccessToken); var content = new StringContent(contentData, System.Text.Encoding.UTF8, "application/json"); @@ -493,7 +493,6 @@ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameo var teslaCommandResultResponse = JsonConvert.DeserializeObject>(responseString); if (response.IsSuccessStatusCode) { - await RemoveUnecessaryInvalidTokenKey().ConfigureAwait(false); } else { @@ -566,56 +565,100 @@ private string GetFleetApiBaseUrl(TeslaFleetApiRegion region, bool useProxyBaseU return $"https://fleet-api.prd.{regionCode}.vn.cloud.tesla.com/"; } - public async Task RefreshTokenAsync() + public async Task GetNewTokenFromBackend() { - logger.LogTrace("{method}()", nameof(RefreshTokenAsync)); - settings.AllowUnlimitedFleetApiRequests = await CheckIfFleetApiRequestsAreAllowed().ConfigureAwait(false); - var tokenState = (await GetFleetApiTokenState().ConfigureAwait(false)).Value; - switch (tokenState) - { - case FleetApiTokenState.NotNeeded: - logger.LogDebug("Refreshing token not needed."); - return; - case FleetApiTokenState.NotRequested: - logger.LogDebug("No token has been requested, yet."); - return; - case FleetApiTokenState.TokenRequestExpired: - logger.LogError("Your token request has expired, create a new one."); - return; - case FleetApiTokenState.TokenUnauthorized: - logger.LogError("Your refresh token is unauthorized, create a new token."); - break; - case FleetApiTokenState.NotReceived: - break; - case FleetApiTokenState.Expired: - break; - case FleetApiTokenState.UpToDate: - logger.LogDebug("Token is up to date."); - break; - case FleetApiTokenState.NoApiRequestsAllowed: - logger.LogError("No API requests allowed."); - return; - default: - throw new ArgumentOutOfRangeException(); - } + logger.LogTrace("{method}()", nameof(GetNewTokenFromBackend)); + //As all tokens get deleted when requesting a new one, we can assume that there is no token in the database. var token = await teslaSolarChargerContext.TeslaTokens.FirstOrDefaultAsync().ConfigureAwait(false); if (token == null) { + var tokenRequestedDate = await GetTokenRequestedDate().ConfigureAwait(false); + if (tokenRequestedDate == null) + { + logger.LogError("Token has not been requested. Fleet API currently not working"); + return; + } + if (tokenRequestedDate < dateTimeProvider.UtcNow().Add(TimeSpan.MaxValue)) + { + logger.LogError("Last token request is too old. Request a new token."); + return; + } using var httpClient = new HttpClient(); var installationId = await tscConfigurationService.GetInstallationId().ConfigureAwait(false); var url = configurationWrapper.BackendApiBaseUrl() + $"Tsc/DeliverAuthToken?installationId={installationId}"; var response = await httpClient.GetAsync(url).ConfigureAwait(false); var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!response.IsSuccessStatusCode) + { + logger.LogError("Error getting token from TSC Backend. Response status code: {statusCode}, Response string: {responseString}", + response.StatusCode, responseString); + } + else + { + var newToken = JsonConvert.DeserializeObject(responseString) ?? throw new InvalidDataException("Could not get token from string."); + await AddNewTokenAsync(newToken).ConfigureAwait(false); + } + + } + } + + public async Task RefreshTokensIfAllowedAndNeeded() + { + logger.LogTrace("{method}()", nameof(RefreshTokensIfAllowedAndNeeded)); + settings.AllowUnlimitedFleetApiRequests = await CheckIfFleetApiRequestsAreAllowed().ConfigureAwait(false); + var tokens = await teslaSolarChargerContext.TeslaTokens.ToListAsync().ConfigureAwait(false); + if (tokens.Count < 1) + { + logger.LogError("No token found. Cannot refresh token."); + return; + } + var tokensToRefresh = tokens.Where(t => t.ExpiresAtUtc < (dateTimeProvider.UtcNow() + TimeSpan.FromMinutes(2))).ToList(); + if (tokensToRefresh.Count < 1) + { + logger.LogTrace("No token needs to be refreshed."); + return; + } + //ToDo: needs to handle manual generated tokens. For now as soon as rate limits are introduced nobody gets refresh tokens even if they have a token not from www.teslasolarcharger.de + if (settings.AllowUnlimitedFleetApiRequests == false) + { + logger.LogError("Due to rate limitations fleet api requests are not allowed. As this version can not handle rate limits try updating to the latest version."); + return; + } + foreach (var tokenToRefresh in tokensToRefresh) + { + logger.LogWarning("Token {tokenId} needs to be refreshed as it expires on {expirationDateTime}", tokenToRefresh.Id, tokenToRefresh.ExpiresAtUtc); + using var httpClient = new HttpClient(); + var tokenUrl = "https://auth.tesla.com/oauth2/v3/token"; + var requestData = new Dictionary + { + { "grant_type", "refresh_token" }, + { "client_id", configurationWrapper.FleetApiClientId() }, + { "refresh_token", tokenToRefresh.RefreshToken }, + }; + var encodedContent = new FormUrlEncodedContent(requestData); + encodedContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); + var response = await httpClient.PostAsync(tokenUrl, encodedContent).ConfigureAwait(false); + var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + if (response.IsSuccessStatusCode) + { + } + else { await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi), - $"Getting token from TscBackend. Response status code: {response.StatusCode} Response string: {responseString}").ConfigureAwait(false); + $"Refreshing token did result in non success status code. Response status code: {response.StatusCode} Response string: {responseString}").ConfigureAwait(false); + logger.LogError("Refreshing token did result in non success status code. Response status code: {statusCode} Response string: {responseString}", response.StatusCode, responseString); + await HandleNonSuccessTeslaApiStatusCodes(response.StatusCode, tokenToRefresh, responseString).ConfigureAwait(false); } response.EnsureSuccessStatusCode(); - var newToken = JsonConvert.DeserializeObject(responseString) ?? throw new InvalidDataException("Could not get token from string."); - await AddNewTokenAsync(newToken).ConfigureAwait(false); + var newToken = JsonConvert.DeserializeObject(responseString) ?? throw new InvalidDataException("Could not get token from string."); + tokenToRefresh.AccessToken = newToken.AccessToken; + tokenToRefresh.RefreshToken = newToken.RefreshToken; + tokenToRefresh.IdToken = newToken.IdToken; + tokenToRefresh.ExpiresAtUtc = dateTimeProvider.UtcNow().AddSeconds(newToken.ExpiresIn); + tokenToRefresh.UnauthorizedCounter = 0; + await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); + logger.LogInformation("New Token saved to database."); } - var dbToken = await GetAccessTokenAndRefreshWhenNeededAsync().ConfigureAwait(false); } private async Task CheckIfFleetApiRequestsAreAllowed() @@ -660,6 +703,7 @@ public async Task AddNewTokenAsync(DtoTeslaTscDeliveryToken token) IdToken = token.IdToken, ExpiresAtUtc = dateTimeProvider.UtcNow().AddSeconds(token.ExpiresIn), Region = token.Region, + UnauthorizedCounter = 0, }); await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); } @@ -675,13 +719,6 @@ public async Task> GetFleetApiTokenState() { return new DtoValue(FleetApiTokenState.NoApiRequestsAllowed); } - var isCurrentRefreshTokenUnauthorized = await teslaSolarChargerContext.TscConfigurations - .Where(c => c.Key == constants.TokenRefreshUnauthorized) - .AnyAsync().ConfigureAwait(false); - if (isCurrentRefreshTokenUnauthorized) - { - return new DtoValue(FleetApiTokenState.TokenUnauthorized); - } var hasCurrentTokenMissingScopes = await teslaSolarChargerContext.TscConfigurations .Where(c => c.Key == constants.TokenMissingScopes) .AnyAsync().ConfigureAwait(false); @@ -692,66 +729,49 @@ public async Task> GetFleetApiTokenState() var token = await teslaSolarChargerContext.TeslaTokens.FirstOrDefaultAsync().ConfigureAwait(false); if (token != null) { + if (token.UnauthorizedCounter > constants.MaxTokenUnauthorizedCount) + { + return new DtoValue(FleetApiTokenState.TokenUnauthorized); + } return new DtoValue(token.ExpiresAtUtc < dateTimeProvider.UtcNow() ? FleetApiTokenState.Expired : FleetApiTokenState.UpToDate); } - var tokenRequestedDateString = await teslaSolarChargerContext.TscConfigurations - .Where(c => c.Key == constants.FleetApiTokenRequested) - .Select(c => c.Value) - .FirstOrDefaultAsync().ConfigureAwait(false); - if (tokenRequestedDateString == null) + var tokenRequestedDate = await GetTokenRequestedDate().ConfigureAwait(false); + if (tokenRequestedDate == null) { return new DtoValue(FleetApiTokenState.NotRequested); } - var tokenRequestedDate = DateTime.Parse(tokenRequestedDateString, null, DateTimeStyles.RoundtripKind); var currentDate = dateTimeProvider.UtcNow(); - if (tokenRequestedDate < currentDate.AddMinutes(-5)) + if (tokenRequestedDate < (currentDate - constants.MaxTokenRequestWaitTime)) { return new DtoValue(FleetApiTokenState.TokenRequestExpired); } return new DtoValue(FleetApiTokenState.NotReceived); } - private async Task GetAccessTokenAndRefreshWhenNeededAsync() + private async Task GetTokenRequestedDate() + { + var tokenRequestedDateString = await teslaSolarChargerContext.TscConfigurations + .Where(c => c.Key == constants.FleetApiTokenRequested) + .Select(c => c.Value) + .FirstOrDefaultAsync().ConfigureAwait(false); + if (tokenRequestedDateString == null) + { + return null; + } + var tokenRequestedDate = DateTime.Parse(tokenRequestedDateString, null, DateTimeStyles.RoundtripKind); + return tokenRequestedDate; + } + + private async Task GetAccessToken() { - logger.LogTrace("{method}()", nameof(GetAccessTokenAndRefreshWhenNeededAsync)); + logger.LogTrace("{method}()", nameof(GetAccessToken)); var token = await teslaSolarChargerContext.TeslaTokens .OrderByDescending(t => t.ExpiresAtUtc) .FirstAsync().ConfigureAwait(false); - var minimumTokenLifeTime = TimeSpan.FromMinutes(5); - if (token.ExpiresAtUtc < (dateTimeProvider.UtcNow() + minimumTokenLifeTime)) + if (token.UnauthorizedCounter > constants.MaxTokenUnauthorizedCount) { - logger.LogWarning("Token is expired. Getting new token."); - using var httpClient = new HttpClient(); - var tokenUrl = "https://auth.tesla.com/oauth2/v3/token"; - var requestData = new Dictionary - { - { "grant_type", "refresh_token" }, - { "client_id", configurationWrapper.FleetApiClientId() }, - { "refresh_token", token.RefreshToken }, - }; - var encodedContent = new FormUrlEncodedContent(requestData); - encodedContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); - var response = await httpClient.PostAsync(tokenUrl, encodedContent).ConfigureAwait(false); - var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - if (response.IsSuccessStatusCode) - { - await RemoveUnecessaryInvalidTokenKey().ConfigureAwait(false); - } - else - { - await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi), - $"Refreshing token did result in non success status code. Response status code: {response.StatusCode} Response string: {responseString}").ConfigureAwait(false); - logger.LogError("Refreshing token did result in non success status code. Response status code: {statusCode} Response string: {responseString}", response.StatusCode, responseString); - await HandleNonSuccessTeslaApiStatusCodes(response.StatusCode, token, responseString).ConfigureAwait(false); - } - response.EnsureSuccessStatusCode(); - var newToken = JsonConvert.DeserializeObject(responseString) ?? throw new InvalidDataException("Could not get token from string."); - token.AccessToken = newToken.AccessToken; - token.RefreshToken = newToken.RefreshToken; - token.IdToken = newToken.IdToken; - token.ExpiresAtUtc = dateTimeProvider.UtcNow().AddSeconds(newToken.ExpiresIn); - await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); - logger.LogInformation("New Token saved to database."); + logger.LogError("Token unauthorized counter is too high. Request a new token."); + throw new InvalidOperationException("Token unauthorized counter is too high. Request a new token."); } return token; } @@ -763,11 +783,8 @@ private async Task HandleNonSuccessTeslaApiStatusCodes(HttpStatusCode statusCode if (statusCode == HttpStatusCode.Unauthorized) { logger.LogError( - "Your token or refresh token is invalid. Very likely you have changed your Tesla password. Should have been valid until: {expiresAt}, Response: {responseString}", token.ExpiresAtUtc, responseString); - teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration() - { - Key = constants.TokenRefreshUnauthorized, Value = responseString, - }); + "Your token or refresh token is invalid. Very likely you have changed your Tesla password. Current unauthorized counter {unauthorizedCounter}, Should have been valid until: {expiresAt}, Response: {responseString}", + ++token.UnauthorizedCounter, token.ExpiresAtUtc, responseString); } else if (statusCode == HttpStatusCode.Forbidden) { @@ -795,19 +812,4 @@ private async Task HandleNonSuccessTeslaApiStatusCodes(HttpStatusCode statusCode await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); } - - - private async Task RemoveUnecessaryInvalidTokenKey() - { - logger.LogTrace("{method}()", nameof(RemoveUnecessaryInvalidTokenKey)); - var cconfigEntriesToRemove = await teslaSolarChargerContext.TscConfigurations - .Where(c => c.Key == constants.TokenRefreshUnauthorized) - .ToListAsync().ConfigureAwait(false); - if (cconfigEntriesToRemove.Any()) - { - logger.LogWarning("Token was marked as invalid although it is not."); - teslaSolarChargerContext.TscConfigurations.RemoveRange(cconfigEntriesToRemove); - await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); - } - } } From 676784c089586ffee022b8c8a41157c3a45422b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Wed, 21 Feb 2024 19:14:53 +0100 Subject: [PATCH 18/20] fix(TeslaFleetApiSerice): can get token from backend again --- .../Resources/PossibleIssues/PossibleIssues.cs | 2 +- .../Scheduling/Jobs/FleetApiTokenRefreshJob.cs | 1 + .../Services/Contracts/ITeslaFleetApiService.cs | 2 ++ .../Server/Services/TeslaFleetApiService.cs | 16 +++++++++------- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs b/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs index 42f94acef..9787b89d1 100644 --- a/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs +++ b/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs @@ -164,7 +164,7 @@ public PossibleIssues(IssueKeys issueKeys) issueKeys.FleetApiTokenRequestExpired, CreateIssue("The Tesla token could not be received.", IssueType.Error, "Open the Base Configuration and request a new token.", - "If this issue keeps occuring, feel free to open an issue on Github including the last 5 chars of your installation ID (bottom of the page). Do NOT include the whole ID." + "If this issue keeps occuring, feel free to open an issue on Github including the first 10 chars of your installation ID (bottom of the page). Do NOT include the whole ID." ) }, { diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs index 35bd5cead..d1bc0a18e 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetApiTokenRefreshJob.cs @@ -11,6 +11,7 @@ public class FleetApiTokenRefreshJob(ILogger logger, public async Task Execute(IJobExecutionContext context) { logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.RefreshFleetApiRequestsAreAllowed().ConfigureAwait(false); await service.GetNewTokenFromBackend().ConfigureAwait(false); await service.RefreshTokensIfAllowedAndNeeded().ConfigureAwait(false); } diff --git a/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs index 72d3d649d..3097349c9 100644 --- a/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/Contracts/ITeslaFleetApiService.cs @@ -16,4 +16,6 @@ public interface ITeslaFleetApiService Task IsFleetApiProxyNeededInDatabase(); Task RefreshCarData(); Task RefreshTokensIfAllowedAndNeeded(); + Task RefreshFleetApiRequestsAreAllowed(); + } diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index 3314d7a52..06cfddeb8 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -14,6 +14,7 @@ using TeslaSolarCharger.Shared.Contracts; using TeslaSolarCharger.Shared.Dtos; using TeslaSolarCharger.Shared.Dtos.Contracts; +using TeslaSolarCharger.Shared.Dtos.Settings; using TeslaSolarCharger.Shared.Enums; using TeslaSolarCharger.SharedBackend.Contracts; using TeslaSolarCharger.SharedBackend.Dtos; @@ -578,7 +579,7 @@ public async Task GetNewTokenFromBackend() logger.LogError("Token has not been requested. Fleet API currently not working"); return; } - if (tokenRequestedDate < dateTimeProvider.UtcNow().Add(TimeSpan.MaxValue)) + if (tokenRequestedDate < dateTimeProvider.UtcNow().Subtract(constants.MaxTokenRequestWaitTime)) { logger.LogError("Last token request is too old. Request a new token."); return; @@ -605,7 +606,6 @@ public async Task GetNewTokenFromBackend() public async Task RefreshTokensIfAllowedAndNeeded() { logger.LogTrace("{method}()", nameof(RefreshTokensIfAllowedAndNeeded)); - settings.AllowUnlimitedFleetApiRequests = await CheckIfFleetApiRequestsAreAllowed().ConfigureAwait(false); var tokens = await teslaSolarChargerContext.TeslaTokens.ToListAsync().ConfigureAwait(false); if (tokens.Count < 1) { @@ -661,11 +661,12 @@ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameo } } - private async Task CheckIfFleetApiRequestsAreAllowed() + public async Task RefreshFleetApiRequestsAreAllowed() { + logger.LogTrace("{method}()", nameof(RefreshFleetApiRequestsAreAllowed)); if (settings.AllowUnlimitedFleetApiRequests && (settings.LastFleetApiRequestAllowedCheck > dateTimeProvider.UtcNow().AddHours(-1))) { - return true; + return; } settings.LastFleetApiRequestAllowedCheck = dateTimeProvider.UtcNow(); using var httpClient = new HttpClient(); @@ -678,15 +679,16 @@ private async Task CheckIfFleetApiRequestsAreAllowed() var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!response.IsSuccessStatusCode) { - return true; + settings.AllowUnlimitedFleetApiRequests = true; + return; } var responseValue = JsonConvert.DeserializeObject>(responseString); - return responseValue?.Value != false; + settings.AllowUnlimitedFleetApiRequests = responseValue?.Value != false; } catch (Exception) { - return true; + settings.AllowUnlimitedFleetApiRequests = true; } } From c7ad55efbf0a00c246c4a8fc7c7d9de8a6d1d580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Wed, 21 Feb 2024 19:42:16 +0100 Subject: [PATCH 19/20] feat(TeslaFleetApiService): allow token refresh more often than token usage if unauthorized --- TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs index 06cfddeb8..99a0374db 100644 --- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs +++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs @@ -624,9 +624,17 @@ public async Task RefreshTokensIfAllowedAndNeeded() logger.LogError("Due to rate limitations fleet api requests are not allowed. As this version can not handle rate limits try updating to the latest version."); return; } + foreach (var tokenToRefresh in tokensToRefresh) { logger.LogWarning("Token {tokenId} needs to be refreshed as it expires on {expirationDateTime}", tokenToRefresh.Id, tokenToRefresh.ExpiresAtUtc); + + //DO NOTE REMOVE *2: As normal requests could result in reaching max unauthorized count, the max value is higher here, so even if token is unauthorized, refreshing it is still tried a couple of times. + if (tokenToRefresh.UnauthorizedCounter > (constants.MaxTokenUnauthorizedCount * 2)) + { + logger.LogError("Token {tokenId} has been unauthorized too often. Do not refresh token.", tokenToRefresh.Id); + continue; + } using var httpClient = new HttpClient(); var tokenUrl = "https://auth.tesla.com/oauth2/v3/token"; var requestData = new Dictionary From 0fa328871c41822a13da6e2380cd57d5a2eb7f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Thu, 22 Feb 2024 17:03:07 +0100 Subject: [PATCH 20/20] fix(README): do not display old Docker badges --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 024054c08..dbc7526b1 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ [![Docker version](https://img.shields.io/docker/v/pkuehnel/teslasolarcharger/latest)](https://hub.docker.com/r/pkuehnel/teslasolarcharger) [![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarcharger/latest)](https://hub.docker.com/r/pkuehnel/teslasolarcharger) -[![Docker pulls new name](https://img.shields.io/docker/pulls/pkuehnel/teslasolarcharger)](https://hub.docker.com/r/pkuehnel/teslasolarcharger) -[![Docker pulls old name](https://img.shields.io/docker/pulls/pkuehnel/smartteslaampsetter)](https://hub.docker.com/r/pkuehnel/smartteslaampsetter) +[![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarcharger)](https://hub.docker.com/r/pkuehnel/teslasolarcharger) [![](https://img.shields.io/badge/Donate-PayPal-ff69b4.svg)](https://www.paypal.com/donate/?hosted_button_id=S3CK8Q9KV3JUL) [![edgeRelease](https://github.com/pkuehnel/TeslaSolarCharger/actions/workflows/edgeRelease.yml/badge.svg)](https://github.com/pkuehnel/TeslaSolarCharger/actions/workflows/edgeRelease.yml) @@ -175,8 +174,7 @@ volumes: [![Docker version](https://img.shields.io/docker/v/pkuehnel/teslasolarchargersmaplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersmaplugin) [![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarchargersmaplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersmaplugin) -[![Docker pulls new name](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersmaplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersmaplugin) -[![Docker pulls old name](https://img.shields.io/docker/pulls/pkuehnel/smartteslaampsettersmaplugin)](https://hub.docker.com/r/pkuehnel/smartteslaampsettersmaplugin) +[![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersmaplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersmaplugin) The SMA plugin is used to access your EnergyMeter (or Sunny Home Manager 2.0) values. To use the plugin, add these lines to the bottom of your `docker-compose.yml`. @@ -327,8 +325,7 @@ volumes: [![Docker version](https://img.shields.io/docker/v/pkuehnel/teslasolarchargersolaredgeplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) [![Docker size](https://img.shields.io/docker/image-size/pkuehnel/teslasolarchargersolaredgeplugin/latest)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) -[![Docker pulls new name](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersolaredgeplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) -[![Docker pulls old name](https://img.shields.io/docker/pulls/pkuehnel/smartteslaampsettersolaredgeplugin)](https://hub.docker.com/r/pkuehnel/smartteslaampsettersolaredgeplugin) +[![Docker pulls](https://img.shields.io/docker/pulls/pkuehnel/teslasolarchargersolaredgeplugin)](https://hub.docker.com/r/pkuehnel/teslasolarchargersolaredgeplugin) The SolarEdge Plugin uses the cloud API, which is limited to 300 which is reset after 15 minutes. When the limit is reached the solaredge API does not gather any new values. This results in TSC displaying 0 grid and home battery power until 15 minutes are over.