From 60ee0f0c24931dbb60308836892164d86036457b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 20:25:07 +0100 Subject: [PATCH 01/27] feat(logging): log into memory and remove seq --- .../Server/Controllers/DebugController.cs | 15 +++++++++++++++ .../Server/ServiceCollectionExtensions.cs | 1 + .../Server/Services/Contracts/ILogService.cs | 6 ++++++ TeslaSolarCharger/Server/Services/LogService.cs | 15 +++++++++++++++ .../Server/TeslaSolarCharger.Server.csproj | 1 + .../Server/appsettings.Development.json | 13 +++++++------ TeslaSolarCharger/Server/appsettings.json | 17 +++++++++-------- 7 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 TeslaSolarCharger/Server/Controllers/DebugController.cs create mode 100644 TeslaSolarCharger/Server/Services/Contracts/ILogService.cs create mode 100644 TeslaSolarCharger/Server/Services/LogService.cs diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs new file mode 100644 index 000000000..29114d5d4 --- /dev/null +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Dtos; +using TeslaSolarCharger.SharedBackend.Abstracts; + +namespace TeslaSolarCharger.Server.Controllers; + +public class DebugController(ILogService logService) : ApiBaseController +{ + [HttpGet] + public IActionResult GetLogs() + { + return Ok(new DtoValue(logService.GetLogs())); + } +} diff --git a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs index e008e785c..4828aea75 100644 --- a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs +++ b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs @@ -115,6 +115,7 @@ public static IServiceCollection AddMyDependencies(this IServiceCollection servi .AddTransient() .AddTransient() .AddTransient() + .AddTransient() //Needs to be Singleton due to WebSocketConnections and property updated dictionary .AddSingleton() .AddSingleton() diff --git a/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs b/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs new file mode 100644 index 000000000..978608b9c --- /dev/null +++ b/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs @@ -0,0 +1,6 @@ +namespace TeslaSolarCharger.Server.Services.Contracts; + +public interface ILogService +{ + string GetLogs(); +} diff --git a/TeslaSolarCharger/Server/Services/LogService.cs b/TeslaSolarCharger/Server/Services/LogService.cs new file mode 100644 index 000000000..57310bd83 --- /dev/null +++ b/TeslaSolarCharger/Server/Services/LogService.cs @@ -0,0 +1,15 @@ +using Serilog.Sinks.InMemory; +using TeslaSolarCharger.Server.Services.Contracts; + +namespace TeslaSolarCharger.Server.Services; + +public class LogService(ILogger logger) : ILogService +{ + public string GetLogs() + { + logger.LogTrace("{method}", nameof(GetLogs)); + var events = InMemorySink.Instance.LogEvents; + var logs = events.Select(e => e.RenderMessage()).ToList(); + return string.Join(Environment.NewLine, logs); + } +} diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index e57fc5703..7df4cbb9c 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -66,6 +66,7 @@ + diff --git a/TeslaSolarCharger/Server/appsettings.Development.json b/TeslaSolarCharger/Server/appsettings.Development.json index 424d49e0f..cf4874f1c 100644 --- a/TeslaSolarCharger/Server/appsettings.Development.json +++ b/TeslaSolarCharger/Server/appsettings.Development.json @@ -2,7 +2,7 @@ "Serilog": { "Using": [ "Serilog.Sinks.Console", - "Serilog.Sinks.Seq" + "Serilog.Sinks.InMemory" ], "MinimumLevel": { "Default": "Verbose", @@ -22,16 +22,17 @@ } }, { - "Name": "Seq", + "Name": "InMemory", "Args": { - "serverUrl": "https://www.teslasolarcharger.de/seq-logging", - "restrictedToMinimumLevel": "Warning", - "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext} {InstallationId} {Version}] {Message:lj}{NewLine}{Exception}" + "maxLogCount": 1000000, + "shared": true, + "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } ], "Enrich": [ - "FromLogContext" + "FromLogContext", + "SourceContext" ] }, "AllowedHosts": "*", diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index e7b12f8fd..c57f15a29 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -2,11 +2,10 @@ "Serilog": { "Using": [ "Serilog.Sinks.Console", - "Serilog.Sinks.Seq" + "Serilog.Sinks.InMemory" ], "MinimumLevel": { - "Default": "Debug", - "Enrich": [ "SourceContext" ], + "Default": "Verbose", "Override": { "Microsoft": "Warning", "System": "Error", @@ -19,20 +18,22 @@ { "Name": "Console", "Args": { + "restrictedToMinimumLevel": "Information", "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } }, { - "Name": "Seq", + "Name": "InMemory", "Args": { - "serverUrl": "https://seq-logging.teslasolarcharger.de/", - "restrictedToMinimumLevel": "Warning", - "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext} {InstallationId} {Version}] {Message:lj}{NewLine}{Exception}" + "maxLogCount": 1000000, + "shared": true, + "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } ], "Enrich": [ - "FromLogContext" + "FromLogContext", + "SourceContext" ] }, "AllowedHosts": "*", From 79d29504e41039d773acc67603bb299817df4d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 20:42:28 +0100 Subject: [PATCH 02/27] feat(chore): remove unused sinks --- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 2 -- TeslaSolarCharger/Server/appsettings.Development.json | 3 +-- TeslaSolarCharger/Server/appsettings.json | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 7df4cbb9c..3cfa86d34 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -65,8 +65,6 @@ - - diff --git a/TeslaSolarCharger/Server/appsettings.Development.json b/TeslaSolarCharger/Server/appsettings.Development.json index cf4874f1c..e4850ee4a 100644 --- a/TeslaSolarCharger/Server/appsettings.Development.json +++ b/TeslaSolarCharger/Server/appsettings.Development.json @@ -1,8 +1,7 @@ { "Serilog": { "Using": [ - "Serilog.Sinks.Console", - "Serilog.Sinks.InMemory" + "Serilog.Sinks.Console" ], "MinimumLevel": { "Default": "Verbose", diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index c57f15a29..31fce6598 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -1,8 +1,7 @@ { "Serilog": { "Using": [ - "Serilog.Sinks.Console", - "Serilog.Sinks.InMemory" + "Serilog.Sinks.Console" ], "MinimumLevel": { "Default": "Verbose", @@ -25,8 +24,6 @@ { "Name": "InMemory", "Args": { - "maxLogCount": 1000000, - "shared": true, "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } From c17ffe3c46545f27f329eb57da300930b07243ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 21:06:05 +0100 Subject: [PATCH 03/27] refactor(chore): remove unnecessary code --- .../Server/Controllers/DebugController.cs | 15 --------------- .../Server/ServiceCollectionExtensions.cs | 1 - .../Server/Services/Contracts/ILogService.cs | 6 ------ TeslaSolarCharger/Server/Services/LogService.cs | 15 --------------- .../Server/appsettings.Development.json | 8 -------- TeslaSolarCharger/Server/appsettings.json | 8 +------- 6 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 TeslaSolarCharger/Server/Controllers/DebugController.cs delete mode 100644 TeslaSolarCharger/Server/Services/Contracts/ILogService.cs delete mode 100644 TeslaSolarCharger/Server/Services/LogService.cs diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs deleted file mode 100644 index 29114d5d4..000000000 --- a/TeslaSolarCharger/Server/Controllers/DebugController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using TeslaSolarCharger.Server.Services.Contracts; -using TeslaSolarCharger.Shared.Dtos; -using TeslaSolarCharger.SharedBackend.Abstracts; - -namespace TeslaSolarCharger.Server.Controllers; - -public class DebugController(ILogService logService) : ApiBaseController -{ - [HttpGet] - public IActionResult GetLogs() - { - return Ok(new DtoValue(logService.GetLogs())); - } -} diff --git a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs index 4828aea75..e008e785c 100644 --- a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs +++ b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs @@ -115,7 +115,6 @@ public static IServiceCollection AddMyDependencies(this IServiceCollection servi .AddTransient() .AddTransient() .AddTransient() - .AddTransient() //Needs to be Singleton due to WebSocketConnections and property updated dictionary .AddSingleton() .AddSingleton() diff --git a/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs b/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs deleted file mode 100644 index 978608b9c..000000000 --- a/TeslaSolarCharger/Server/Services/Contracts/ILogService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace TeslaSolarCharger.Server.Services.Contracts; - -public interface ILogService -{ - string GetLogs(); -} diff --git a/TeslaSolarCharger/Server/Services/LogService.cs b/TeslaSolarCharger/Server/Services/LogService.cs deleted file mode 100644 index 57310bd83..000000000 --- a/TeslaSolarCharger/Server/Services/LogService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Serilog.Sinks.InMemory; -using TeslaSolarCharger.Server.Services.Contracts; - -namespace TeslaSolarCharger.Server.Services; - -public class LogService(ILogger logger) : ILogService -{ - public string GetLogs() - { - logger.LogTrace("{method}", nameof(GetLogs)); - var events = InMemorySink.Instance.LogEvents; - var logs = events.Select(e => e.RenderMessage()).ToList(); - return string.Join(Environment.NewLine, logs); - } -} diff --git a/TeslaSolarCharger/Server/appsettings.Development.json b/TeslaSolarCharger/Server/appsettings.Development.json index e4850ee4a..5eb5cfcb2 100644 --- a/TeslaSolarCharger/Server/appsettings.Development.json +++ b/TeslaSolarCharger/Server/appsettings.Development.json @@ -19,14 +19,6 @@ "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } - }, - { - "Name": "InMemory", - "Args": { - "maxLogCount": 1000000, - "shared": true, - "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" - } } ], "Enrich": [ diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index 31fce6598..a53b329b7 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -4,7 +4,7 @@ "Serilog.Sinks.Console" ], "MinimumLevel": { - "Default": "Verbose", + "Default": "Debug", "Override": { "Microsoft": "Warning", "System": "Error", @@ -20,12 +20,6 @@ "restrictedToMinimumLevel": "Information", "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } - }, - { - "Name": "InMemory", - "Args": { - "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" - } } ], "Enrich": [ From e09f7c9d46175f5653ec4bc2bd4de011db6747d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 21:07:50 +0100 Subject: [PATCH 04/27] refactor(packages): remove seq --- .../Server/Controllers/HelloController.cs | 1 - .../Server/TeslaSolarCharger.Server.csproj | 1 - TeslaSolarCharger/Server/appsettings.json | 58 ------------------- 3 files changed, 60 deletions(-) diff --git a/TeslaSolarCharger/Server/Controllers/HelloController.cs b/TeslaSolarCharger/Server/Controllers/HelloController.cs index 704f55d74..5e4616fc6 100644 --- a/TeslaSolarCharger/Server/Controllers/HelloController.cs +++ b/TeslaSolarCharger/Server/Controllers/HelloController.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Mvc; using TeslaSolarCharger.Server.Contracts; -using TeslaSolarCharger.Server.Services.Contracts; using TeslaSolarCharger.Server.Services.GridPrice.Dtos; using TeslaSolarCharger.Shared.Dtos; using TeslaSolarCharger.SharedBackend.Abstracts; diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 3cfa86d34..75b1fbd7a 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -65,7 +65,6 @@ - diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index a53b329b7..ad3044f55 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -17,7 +17,6 @@ { "Name": "Console", "Args": { - "restrictedToMinimumLevel": "Information", "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } @@ -26,62 +25,5 @@ "FromLogContext", "SourceContext" ] - }, - "AllowedHosts": "*", - "ConfigFileLocation": "configs", - "BackupCopyDestinationDirectory": "backups", - "BackupZipDirectory": "backupZips", - "AutoBackupZipDirectory": "autoBackupZips", - "RestoreTempDirectory": "restores", - "CarConfigFilename": "carConfig.json", - "BaseConfigFileName": "baseConfig.json", - "SqliteFileName": "TeslaSolarCharger.db", - "CarPriorities": "1", - "UpdateIntervalSeconds": 30, - "PvValueUpdateIntervalSeconds": 15, - "MqqtClientId": "TeslaSolarCharger", - "MosquitoServer": "mosquitto", - "TeslaMateDbServer": "database", - "TeslaMateDbPort": "5432", - "TeslaMateDbDatabaseName": "teslamate", - "TeslaMateDbUser": "teslamate", - "TeslaMateDbPassword": "secret", - "TeslaMateApiBaseUrl": "http://teslamateapi:8080", - "GeoFence": "Home", - "IgnoreSslErrors": false, - "FleetApiClientId": "f29f71d6285a-4873-8b6b-80f15854892e", - "BackendApiBaseUrl": "https://api.solar4car.com/api/", - "UseFleetApiProxy": false, - "LogLocationData": false, - "SendTeslaApiStatsToBackend": true, - "GetVehicleDataFromTesla": true, - "GetVehicleDataFromTeslaDebug": false, - "ShouldUseFakeSolarValues": false, - "AwattarBaseUrl": "https://api.awattar.de/v1/marketdata", - "BleBaseUrl": null, - "MaxTravelSpeedMetersPerSecond": 30, - //This is also the intervall for the car refresh - "CarRefreshAfterCommandSeconds": 11, - "BleUsageStopAfterErrorSeconds": 300, - "FleetApiRefreshIntervalSeconds": 500, - "FleetTelemetryApiUrl": "wss://api.fleet-telemetry.solar4car.com/ws?", - "BackendPasswordDefaultLength": 25, - "GridPriceProvider": { - "EnergyProvider": "FixedPrice", - "Octopus": { - "BaseUrl": "https://api.octopus.energy/v1", - "ProductCode": "AGILE-18-02-21", - "TariffCode": "E-1R-AGILE-18-02-21" - }, - "Tibber": { - "BaseUrl": "https://api.tibber.com/v1-beta/gql" - }, - "Awattar": { - "BaseUrl": "https://api.awattar.de/v1", - "VATMultiplier": 1.19 - }, - "Energinet": { - "BaseUrl": "https://api.energidataservice.dk/dataset/" - } } } \ No newline at end of file From 7b8bef7b36ae6411799cc1844a3f7b332581cf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 21:26:00 +0100 Subject: [PATCH 05/27] fix(chore): fix logging --- TeslaSolarCharger/Server/Controllers/HelloController.cs | 1 + TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 1 + TeslaSolarCharger/Server/appsettings.json | 1 + 3 files changed, 3 insertions(+) diff --git a/TeslaSolarCharger/Server/Controllers/HelloController.cs b/TeslaSolarCharger/Server/Controllers/HelloController.cs index 5e4616fc6..704f55d74 100644 --- a/TeslaSolarCharger/Server/Controllers/HelloController.cs +++ b/TeslaSolarCharger/Server/Controllers/HelloController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using TeslaSolarCharger.Server.Contracts; +using TeslaSolarCharger.Server.Services.Contracts; using TeslaSolarCharger.Server.Services.GridPrice.Dtos; using TeslaSolarCharger.Shared.Dtos; using TeslaSolarCharger.SharedBackend.Abstracts; diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 75b1fbd7a..3cfa86d34 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -65,6 +65,7 @@ + diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index ad3044f55..c387b0fe1 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -17,6 +17,7 @@ { "Name": "Console", "Args": { + "restrictedToMinimumLevel": "Information", "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } From 67a2dc6b1b414e4aa9e1087fb4f910d6c2d909b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 21:33:41 +0100 Subject: [PATCH 06/27] fix(appsettings.json) restore deleted entries --- TeslaSolarCharger/Server/appsettings.json | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index c387b0fe1..a53b329b7 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -26,5 +26,62 @@ "FromLogContext", "SourceContext" ] + }, + "AllowedHosts": "*", + "ConfigFileLocation": "configs", + "BackupCopyDestinationDirectory": "backups", + "BackupZipDirectory": "backupZips", + "AutoBackupZipDirectory": "autoBackupZips", + "RestoreTempDirectory": "restores", + "CarConfigFilename": "carConfig.json", + "BaseConfigFileName": "baseConfig.json", + "SqliteFileName": "TeslaSolarCharger.db", + "CarPriorities": "1", + "UpdateIntervalSeconds": 30, + "PvValueUpdateIntervalSeconds": 15, + "MqqtClientId": "TeslaSolarCharger", + "MosquitoServer": "mosquitto", + "TeslaMateDbServer": "database", + "TeslaMateDbPort": "5432", + "TeslaMateDbDatabaseName": "teslamate", + "TeslaMateDbUser": "teslamate", + "TeslaMateDbPassword": "secret", + "TeslaMateApiBaseUrl": "http://teslamateapi:8080", + "GeoFence": "Home", + "IgnoreSslErrors": false, + "FleetApiClientId": "f29f71d6285a-4873-8b6b-80f15854892e", + "BackendApiBaseUrl": "https://api.solar4car.com/api/", + "UseFleetApiProxy": false, + "LogLocationData": false, + "SendTeslaApiStatsToBackend": true, + "GetVehicleDataFromTesla": true, + "GetVehicleDataFromTeslaDebug": false, + "ShouldUseFakeSolarValues": false, + "AwattarBaseUrl": "https://api.awattar.de/v1/marketdata", + "BleBaseUrl": null, + "MaxTravelSpeedMetersPerSecond": 30, + //This is also the intervall for the car refresh + "CarRefreshAfterCommandSeconds": 11, + "BleUsageStopAfterErrorSeconds": 300, + "FleetApiRefreshIntervalSeconds": 500, + "FleetTelemetryApiUrl": "wss://api.fleet-telemetry.solar4car.com/ws?", + "BackendPasswordDefaultLength": 25, + "GridPriceProvider": { + "EnergyProvider": "FixedPrice", + "Octopus": { + "BaseUrl": "https://api.octopus.energy/v1", + "ProductCode": "AGILE-18-02-21", + "TariffCode": "E-1R-AGILE-18-02-21" + }, + "Tibber": { + "BaseUrl": "https://api.tibber.com/v1-beta/gql" + }, + "Awattar": { + "BaseUrl": "https://api.awattar.de/v1", + "VATMultiplier": 1.19 + }, + "Energinet": { + "BaseUrl": "https://api.energidataservice.dk/dataset/" + } } } \ No newline at end of file From 70b2d9a0b5cef23a2e99ede1da0db5d1170ac2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 21:46:11 +0100 Subject: [PATCH 07/27] fix(appsettings.json): remove restricetedToMinimumLevel --- TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj | 3 +-- TeslaSolarCharger/Server/appsettings.json | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 3cfa86d34..832c28c58 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -65,7 +65,6 @@ - diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index a53b329b7..69463ee23 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -17,7 +17,6 @@ { "Name": "Console", "Args": { - "restrictedToMinimumLevel": "Information", "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" } } From 6b2fb7f8a8beb3e1099ba81d20686d26f9bf45d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 22:11:03 +0100 Subject: [PATCH 08/27] feat(Serilog): add in Memory sink and configure logging in code --- .../InMemorySink.cs | 66 +++++++++++++++++++ .../PkSoftwareService.Custom.Backend.csproj | 13 ++++ TeslaSolarCharger.sln | 6 ++ .../Server/Controllers/DebugController.cs | 15 +++++ TeslaSolarCharger/Server/Program.cs | 26 ++++++-- .../Server/TeslaSolarCharger.Server.csproj | 1 + .../Server/appsettings.Development.json | 27 -------- TeslaSolarCharger/Server/appsettings.json | 27 -------- 8 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 PkSoftwareService.Custom.Backend/InMemorySink.cs create mode 100644 PkSoftwareService.Custom.Backend/PkSoftwareService.Custom.Backend.csproj create mode 100644 TeslaSolarCharger/Server/Controllers/DebugController.cs diff --git a/PkSoftwareService.Custom.Backend/InMemorySink.cs b/PkSoftwareService.Custom.Backend/InMemorySink.cs new file mode 100644 index 000000000..816deae6e --- /dev/null +++ b/PkSoftwareService.Custom.Backend/InMemorySink.cs @@ -0,0 +1,66 @@ +using Serilog.Core; +using Serilog.Events; +using Serilog.Formatting.Display; + +namespace PkSoftwareService.Custom.Backend; + +public class InMemorySink : ILogEventSink +{ + private readonly int _capacity; + private readonly Queue _logMessages; + private readonly object _syncRoot = new object(); + private readonly MessageTemplateTextFormatter _formatter; + + /// + /// Creates a new InMemorySink. + /// + /// The output template (should match your Console sink). + /// Optional format provider. + /// Max number of messages to store. + public InMemorySink(string outputTemplate, IFormatProvider? formatProvider = null, int capacity = 10000) + { + _capacity = capacity; + _logMessages = new Queue(capacity); + _formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + } + + public void Emit(LogEvent logEvent) + { + // Format the log event into a string. + using var writer = new StringWriter(); + _formatter.Format(logEvent, writer); + var message = writer.ToString(); + + // Ensure thread safety while enqueuing/dequeuing. + lock (_syncRoot) + { + if (_logMessages.Count >= _capacity) + { + _logMessages.Dequeue(); // remove oldest + } + _logMessages.Enqueue(message); + } + } + + /// + /// Returns a snapshot of the current log messages. + /// + public List GetLogs() + { + lock (_syncRoot) + { + return _logMessages.ToList(); + } + } + + /// + /// Optionally clear all logs. + /// + public void Clear() + { + lock (_syncRoot) + { + _logMessages.Clear(); + } + } +} diff --git a/PkSoftwareService.Custom.Backend/PkSoftwareService.Custom.Backend.csproj b/PkSoftwareService.Custom.Backend/PkSoftwareService.Custom.Backend.csproj new file mode 100644 index 000000000..6e5fff9b8 --- /dev/null +++ b/PkSoftwareService.Custom.Backend/PkSoftwareService.Custom.Backend.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/TeslaSolarCharger.sln b/TeslaSolarCharger.sln index ada531858..a41ae7550 100644 --- a/TeslaSolarCharger.sln +++ b/TeslaSolarCharger.sln @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeslaSolarCharger.SharedMod EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeslaSolarCharger.Services", "TeslaSolarCharger.Services\TeslaSolarCharger.Services.csproj", "{21A8DB64-E449-474E-94DD-360C30D1756A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PkSoftwareService.Custom.Backend", "PkSoftwareService.Custom.Backend\PkSoftwareService.Custom.Backend.csproj", "{B9331EFC-2B31-4447-B6C3-3E898B560946}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +89,10 @@ Global {21A8DB64-E449-474E-94DD-360C30D1756A}.Debug|Any CPU.Build.0 = Debug|Any CPU {21A8DB64-E449-474E-94DD-360C30D1756A}.Release|Any CPU.ActiveCfg = Release|Any CPU {21A8DB64-E449-474E-94DD-360C30D1756A}.Release|Any CPU.Build.0 = Release|Any CPU + {B9331EFC-2B31-4447-B6C3-3E898B560946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9331EFC-2B31-4447-B6C3-3E898B560946}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9331EFC-2B31-4447-B6C3-3E898B560946}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9331EFC-2B31-4447-B6C3-3E898B560946}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs new file mode 100644 index 000000000..3212997ac --- /dev/null +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using PkSoftwareService.Custom.Backend; +using TeslaSolarCharger.SharedBackend.Abstracts; + +namespace TeslaSolarCharger.Server.Controllers; + +public class DebugController(InMemorySink inMemorySink, Serilog.Core.LoggingLevelSwitch inMemoryLogLevelSwitch/*chaning log level switch is not tested*/) : ApiBaseController +{ + [HttpGet] + public IActionResult GetLogs() + { + var logs = inMemorySink.GetLogs(); + return Ok(logs); + } +} diff --git a/TeslaSolarCharger/Server/Program.cs b/TeslaSolarCharger/Server/Program.cs index 35ab5f5ac..101d9492f 100644 --- a/TeslaSolarCharger/Server/Program.cs +++ b/TeslaSolarCharger/Server/Program.cs @@ -1,7 +1,10 @@ using FluentValidation; using Microsoft.EntityFrameworkCore; +using PkSoftwareService.Custom.Backend; using Serilog; using Serilog.Context; +using Serilog.Core; +using Serilog.Events; using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions; using SharpGrip.FluentValidation.AutoValidation.Mvc.Interceptors; using System.Reflection; @@ -42,18 +45,31 @@ builder.Services.AddSharedDependencies(); builder.Services.AddServicesDependencies(); -builder.Host.UseSerilog((context, configuration) => configuration - .ReadFrom.Configuration(context.Configuration)); - builder.Services.AddFluentValidationAutoValidation(); builder.Services.AddValidatorsFromAssemblyContaining(); +builder.Host.UseSerilog(); +const string outputTemplate = "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}"; +var inMemoryLevelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose); +var inMemorySink = new InMemorySink(outputTemplate); + +builder.Services.AddSingleton(inMemoryLevelSwitch); +builder.Services.AddSingleton(inMemorySink); var app = builder.Build(); Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(app.Services.GetRequiredService()) - .Enrich.FromLogContext() + .MinimumLevel.Verbose()// overall minimum + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("System", LogEventLevel.Error) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Warning) + .MinimumLevel.Override("TeslaSolarCharger.Shared.Wrappers.ConfigurationWrapper", LogEventLevel.Information) + .MinimumLevel.Override("TeslaSolarCharger.Model.EntityFramework.DbConnectionStringHelper", LogEventLevel.Information) + .WriteTo.Console(outputTemplate: outputTemplate, restrictedToMinimumLevel: LogEventLevel.Debug) + // Send events to the in–memory sink using a sub–logger and the dynamic level switch. + .WriteTo.Logger(lc => lc + .MinimumLevel.ControlledBy(inMemoryLevelSwitch) + .WriteTo.Sink(inMemorySink)) .CreateLogger(); diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj index 832c28c58..0e318cb45 100644 --- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj +++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj @@ -72,6 +72,7 @@ + diff --git a/TeslaSolarCharger/Server/appsettings.Development.json b/TeslaSolarCharger/Server/appsettings.Development.json index 5eb5cfcb2..5c77f7eb1 100644 --- a/TeslaSolarCharger/Server/appsettings.Development.json +++ b/TeslaSolarCharger/Server/appsettings.Development.json @@ -1,31 +1,4 @@ { - "Serilog": { - "Using": [ - "Serilog.Sinks.Console" - ], - "MinimumLevel": { - "Default": "Verbose", - "Override": { - "Microsoft": "Information", - "System": "Error", - "Microsoft.EntityFrameworkCore.Database.Command": "Information", - "Quartz": "Warning", - "TeslaSolarCharger.Server.Scheduling": "Information" - } - }, - "WriteTo": [ - { - "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" - } - } - ], - "Enrich": [ - "FromLogContext", - "SourceContext" - ] - }, "AllowedHosts": "*", "AllowCORS": true, "DisplayApiRequestCounter": true, diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index 69463ee23..a191799fa 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -1,31 +1,4 @@ { - "Serilog": { - "Using": [ - "Serilog.Sinks.Console" - ], - "MinimumLevel": { - "Default": "Debug", - "Override": { - "Microsoft": "Warning", - "System": "Error", - "Microsoft.EntityFrameworkCore.Database.Command": "Warning", - "TeslaSolarCharger.Shared.Wrappers.ConfigurationWrapper": "Information", - "TeslaSolarCharger.Model.EntityFramework.DbConnectionStringHelper": "Information" - } - }, - "WriteTo": [ - { - "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:dd-MMM-yyyy HH:mm:ss.fff} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}" - } - } - ], - "Enrich": [ - "FromLogContext", - "SourceContext" - ] - }, "AllowedHosts": "*", "ConfigFileLocation": "configs", "BackupCopyDestinationDirectory": "backups", From 42be754b4574e047897a295aa611a5a2865c762c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 22:14:19 +0100 Subject: [PATCH 09/27] feat(DebugController): download logs as file --- .../Server/Controllers/DebugController.cs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs index 3212997ac..97802b920 100644 --- a/TeslaSolarCharger/Server/Controllers/DebugController.cs +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Mvc; using PkSoftwareService.Custom.Backend; +using Serilog.Events; +using System.Text; using TeslaSolarCharger.SharedBackend.Abstracts; namespace TeslaSolarCharger.Server.Controllers; @@ -7,9 +9,34 @@ namespace TeslaSolarCharger.Server.Controllers; public class DebugController(InMemorySink inMemorySink, Serilog.Core.LoggingLevelSwitch inMemoryLogLevelSwitch/*chaning log level switch is not tested*/) : ApiBaseController { [HttpGet] - public IActionResult GetLogs() + public IActionResult DownloadLogs() { + // Get the logs from the in-memory sink. var logs = inMemorySink.GetLogs(); - return Ok(logs); + + // Join the log entries into a single string, separated by new lines. + var content = string.Join(Environment.NewLine, logs); + + // Convert the string content to a byte array (UTF8 encoding). + var bytes = Encoding.UTF8.GetBytes(content); + + // Return the file with the appropriate content type and file name. + return File(bytes, "text/plain", "logs.txt"); + } + + /// + /// Adjusts the minimum log level for the in-memory sink. + /// + /// The new log level (e.g. Verbose, Debug, Information, Warning, Error, Fatal). + /// Status message. + [HttpPost] + public IActionResult SetLogLevel([FromQuery] string level) + { + if (!Enum.TryParse(level, true, out var newLevel)) + { + return BadRequest("Invalid log level. Use one of: Verbose, Debug, Information, Warning, Error, Fatal"); + } + inMemoryLogLevelSwitch.MinimumLevel = newLevel; + return Ok($"In-memory sink log level changed to {newLevel}"); } } From cc7a2ce5cc8d4d63eb32c8b94eec0821edc1da87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Fri, 7 Feb 2025 22:21:56 +0100 Subject: [PATCH 10/27] feat(Support): add download logs button --- TeslaSolarCharger/Client/Pages/Support.razor | 9 +++++++++ TeslaSolarCharger/Client/Shared/NavMenu.razor | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 TeslaSolarCharger/Client/Pages/Support.razor diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor new file mode 100644 index 000000000..f76454bb1 --- /dev/null +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -0,0 +1,9 @@ +@page "/support" + +

Support

+ +Download Logs + +@code { + +} diff --git a/TeslaSolarCharger/Client/Shared/NavMenu.razor b/TeslaSolarCharger/Client/Shared/NavMenu.razor index fc02cbdc9..1b0937a23 100644 --- a/TeslaSolarCharger/Client/Shared/NavMenu.razor +++ b/TeslaSolarCharger/Client/Shared/NavMenu.razor @@ -36,6 +36,11 @@ Base Configuration + } @@ -152,6 +167,7 @@ else private Dictionary _pairingResults = new(); private Dictionary _bleTestResults = new(); private Dictionary _bleWakeUpTestResults = new(); + private Dictionary _fleetTelemetryConfigs = new(); private HashSet _loadingVins = new(); @@ -252,4 +268,19 @@ else } } + private async Task GetFleetTelemetryConfig(string vin) + { + var result = await HttpClientHelper.SendGetRequestAsync>($"api/Config/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}"); + string stringToDisplay; + if (result.HasError) + { + stringToDisplay = result.ErrorMessage ?? "No error message"; + } + else + { + stringToDisplay = result.Data?.Value ?? "No data"; + } + _fleetTelemetryConfigs[vin] = stringToDisplay; + } + } \ No newline at end of file diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor index e0ae43d4c..3dc086e37 100644 --- a/TeslaSolarCharger/Client/Pages/Support.razor +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -1,4 +1,10 @@ @page "/support" +@using TeslaSolarCharger.Client.Helper.Contracts +@using TeslaSolarCharger.Shared.Dtos +@using TeslaSolarCharger.Shared.Dtos.Support + +@inject IHttpClientHelper HttpClientHelper +@inject ISnackbar Snackbar

Support

@@ -11,6 +17,76 @@ Download Logs +

Car Debug Details

+@if (_debugCars == default) +{ + +} +else +{ + + @foreach (var car in _debugCars) + { + +
ID: @car.Key
+
VIN: @car.Value.Vin
+
Name: @car.Value.Name
+ +

Fleet Telemetry Config

+ @if (car.Value.Vin != default && _fleetTelemetryConfigs.TryGetValue(car.Value.Vin, out var config)) + { +
@config
+ } + + +
+ } +
+} + + + @code { + private readonly Dictionary _fleetTelemetryConfigs = new(); + + private Dictionary? _debugCars; + + private bool _isFleetTelemetryLoading; + + + protected override async Task OnInitializedAsync() + { + var cars = await HttpClientHelper.SendGetRequestWithSnackbarAsync>("api/Debug/GetCars"); + if (cars != default) + { + _debugCars = cars; + } + } + + + private async Task GetFleetTelemetryConfig(string? vin) + { + if (vin == default) + { + Snackbar.Add("VIN is unknown", Severity.Error); + return; + } + _isFleetTelemetryLoading = true; + var result = await HttpClientHelper.SendGetRequestAsync>($"api/Config/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}"); + string stringToDisplay; + if (result.HasError) + { + stringToDisplay = result.ErrorMessage ?? "No error message"; + } + else + { + stringToDisplay = result.Data?.Value ?? "No data"; + } + _fleetTelemetryConfigs[vin] = stringToDisplay; + _isFleetTelemetryLoading = false; + } } diff --git a/TeslaSolarCharger/Server/Controllers/ConfigController.cs b/TeslaSolarCharger/Server/Controllers/ConfigController.cs index 5204d79ff..90e91fb9c 100644 --- a/TeslaSolarCharger/Server/Controllers/ConfigController.cs +++ b/TeslaSolarCharger/Server/Controllers/ConfigController.cs @@ -1,37 +1,29 @@ using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using TeslaSolarCharger.Server.Contracts; -using TeslaSolarCharger.Server.Dtos.TscBackend; -using TeslaSolarCharger.Server.Services; using TeslaSolarCharger.Server.Services.Contracts; using TeslaSolarCharger.Shared.Dtos; using TeslaSolarCharger.Shared.Dtos.Contracts; -using TeslaSolarCharger.Shared.Dtos.Settings; using TeslaSolarCharger.SharedBackend.Abstracts; namespace TeslaSolarCharger.Server.Controllers { - public class ConfigController : ApiBaseController + public class ConfigController(IConfigJsonService configJsonService, + IFleetTelemetryConfigurationService fleetTelemetryConfigurationService) + : ApiBaseController { - private readonly IConfigJsonService _configJsonService; - private readonly ITeslaFleetApiService _teslaFleetApiService; - - public ConfigController(IConfigJsonService configJsonService, ITeslaFleetApiService teslaFleetApiService) - { - _configJsonService = configJsonService; - _teslaFleetApiService = teslaFleetApiService; - } /// /// Get all settings and status of all cars /// [HttpGet] - public ISettings GetSettings() => _configJsonService.GetSettings(); + public ISettings GetSettings() => configJsonService.GetSettings(); /// /// Get basic Configuration of cars, which are not often changed /// [HttpGet] - public Task> GetCarBasicConfigurations() => _configJsonService.GetCarBasicConfigurations(); + public Task> GetCarBasicConfigurations() => configJsonService.GetCarBasicConfigurations(); /// /// Update Car's configuration @@ -41,7 +33,15 @@ public ConfigController(IConfigJsonService configJsonService, ITeslaFleetApiServ [HttpPost] public Task UpdateCarBasicConfiguration(int carId, [FromBody] CarBasicConfiguration carBasicConfiguration) { - return _configJsonService.UpdateCarBasicConfiguration(carId, carBasicConfiguration); + return configJsonService.UpdateCarBasicConfiguration(carId, carBasicConfiguration); + } + + [HttpGet] + public async Task GetFleetTelemetryConfiguration(string vin) + { + var config = await fleetTelemetryConfigurationService.GetFleetTelemetryConfiguration(vin); + var configString = JsonConvert.SerializeObject(config, Formatting.Indented); + return Ok(new DtoValue(configString)); } } } diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs index f35242dce..b9db2b6e7 100644 --- a/TeslaSolarCharger/Server/Controllers/DebugController.cs +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using PkSoftwareService.Custom.Backend; using Serilog.Events; using System.Text; +using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Dtos; +using TeslaSolarCharger.Shared.Dtos.Contracts; using TeslaSolarCharger.SharedBackend.Abstracts; namespace TeslaSolarCharger.Server.Controllers; -public class DebugController(InMemorySink inMemorySink, Serilog.Core.LoggingLevelSwitch inMemoryLogLevelSwitch/*chaning log level switch is not tested*/) : ApiBaseController +public class DebugController(InMemorySink inMemorySink, + Serilog.Core.LoggingLevelSwitch inMemoryLogLevelSwitch, + IFleetTelemetryConfigurationService fleetTelemetryConfigurationService,/*chaning log level switch is not tested*/ + ISettings settings, + IDebugService debugService) : ApiBaseController { [HttpGet] public IActionResult DownloadLogs() @@ -39,4 +47,19 @@ public IActionResult SetLogLevel([FromQuery] string level) inMemoryLogLevelSwitch.MinimumLevel = newLevel; return Ok($"In-memory sink log level changed to {newLevel}"); } + + [HttpGet] + public async Task GetFleetTelemetryConfiguration(string vin) + { + var config = await fleetTelemetryConfigurationService.GetFleetTelemetryConfiguration(vin); + var configString = JsonConvert.SerializeObject(config, Formatting.Indented); + return Ok(new DtoValue(configString)); + } + + [HttpGet] + public async Task GetCars() + { + var cars = await debugService.GetCars(); + return Ok(cars); + } } diff --git a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoGetFleetTelemetryConfiguration.cs b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoGetFleetTelemetryConfiguration.cs new file mode 100644 index 000000000..2765a3354 --- /dev/null +++ b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoGetFleetTelemetryConfiguration.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; + +namespace TeslaSolarCharger.Server.Dtos.FleetTelemetry; + +public class DtoGetFleetTelemetryConfiguration +{ + [JsonProperty("synced")] + public bool Synced { get; set; } + [JsonProperty("config")] + public Config? Config { get; set; } +} + +public class Config(string hostname, string ca, long exp, int port, bool preferTyped) +{ + [JsonProperty("hostname")] + public string Hostname { get; set; } = hostname; + + [JsonProperty("ca")] + public string Ca { get; set; } = ca; + + [JsonProperty("exp")] + public long Exp { get; set; } = exp; + + [JsonProperty("fields")] + public Dictionary Fields { get; set; } = new(); + [JsonProperty("alert_types")] + public List AlertTypes { get; set; } = new(); + [JsonProperty("port")] + public int Port { get; set; } = port; + [JsonProperty("prefer_typed")] + public bool PreferTyped { get; set; } = preferTyped; +} + +public class IntervalSetting +{ + [JsonConstructor] + // ReSharper disable once ConvertToPrimaryConstructor + public IntervalSetting(int intervalSeconds, int? resendIntervalSeconds = null, int? minimumDelta = null) + { + IntervalSeconds = intervalSeconds; + ResendIntervalSeconds = resendIntervalSeconds; + MinimumDelta = minimumDelta; + } + + public IntervalSetting(TimeSpan interval) + { + IntervalSeconds = (int)interval.TotalSeconds; + } + public IntervalSetting(TimeSpan interval, TimeSpan resendInterval) + { + IntervalSeconds = (int)interval.TotalSeconds; + ResendIntervalSeconds = (int)resendInterval.TotalSeconds; + } + public IntervalSetting(TimeSpan interval, int minimumDelta) + { + IntervalSeconds = (int)interval.TotalSeconds; + MinimumDelta = minimumDelta; + } + public IntervalSetting(TimeSpan interval, TimeSpan resendInterval, int minimumDelta) + { + IntervalSeconds = (int)interval.TotalSeconds; + ResendIntervalSeconds = (int)resendInterval.TotalSeconds; + MinimumDelta = minimumDelta; + } + + [JsonProperty("interval_seconds")] + public int IntervalSeconds { get; set; } + [JsonProperty("resend_interval_seconds", NullValueHandling = NullValueHandling.Ignore)] + public int? ResendIntervalSeconds { get; set; } + [JsonProperty("minimum_delta", NullValueHandling = NullValueHandling.Ignore)] + public int? MinimumDelta { get; set; } +} diff --git a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs index e008e785c..af20d6326 100644 --- a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs +++ b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs @@ -115,6 +115,8 @@ public static IServiceCollection AddMyDependencies(this IServiceCollection servi .AddTransient() .AddTransient() .AddTransient() + .AddTransient() + .AddTransient() //Needs to be Singleton due to WebSocketConnections and property updated dictionary .AddSingleton() .AddSingleton() diff --git a/TeslaSolarCharger/Server/Services/Contracts/IDebugService.cs b/TeslaSolarCharger/Server/Services/Contracts/IDebugService.cs new file mode 100644 index 000000000..ae9da1632 --- /dev/null +++ b/TeslaSolarCharger/Server/Services/Contracts/IDebugService.cs @@ -0,0 +1,8 @@ +using TeslaSolarCharger.Shared.Dtos.Support; + +namespace TeslaSolarCharger.Server.Services.Contracts; + +public interface IDebugService +{ + Task> GetCars(); +} diff --git a/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs new file mode 100644 index 000000000..5a9e8f840 --- /dev/null +++ b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs @@ -0,0 +1,8 @@ +using TeslaSolarCharger.Server.Dtos.FleetTelemetry; + +namespace TeslaSolarCharger.Server.Services.Contracts; + +public interface IFleetTelemetryConfigurationService +{ + Task GetFleetTelemetryConfiguration(string vin); +} diff --git a/TeslaSolarCharger/Server/Services/DebugService.cs b/TeslaSolarCharger/Server/Services/DebugService.cs new file mode 100644 index 000000000..c2dbf56c3 --- /dev/null +++ b/TeslaSolarCharger/Server/Services/DebugService.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using TeslaSolarCharger.Model.Contracts; +using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Dtos.Support; + +namespace TeslaSolarCharger.Server.Services; + +public class DebugService(ILogger logger, + ITeslaSolarChargerContext context) : IDebugService +{ + public async Task> GetCars() + { + logger.LogTrace("{method}", nameof(GetCars)); + var cars = await context.Cars + .Where(x => x.Vin != null) + .ToDictionaryAsync(x => x.Id, x => new DtoDebugCar() + { + Name = x.Name, + Vin = x.Vin, + ShouldBeManaged = x.ShouldBeManaged == true, + }).ConfigureAwait(false); + logger.LogDebug("Found {carCount} cars", cars.Count); + return cars; + } +} diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs new file mode 100644 index 000000000..fe4813a8a --- /dev/null +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using TeslaSolarCharger.Model.Contracts; +using TeslaSolarCharger.Server.Dtos.FleetTelemetry; +using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Resources.Contracts; + +namespace TeslaSolarCharger.Server.Services; + +public class FleetTelemetryConfigurationService(ILogger logger, + IBackendApiService backendApiService, + ITeslaSolarChargerContext teslaSolarChargerContext, + ITscConfigurationService tscConfigurationService, + ITeslaFleetApiService teslaFleetApiService, + IConstants constants) : IFleetTelemetryConfigurationService +{ + public async Task GetFleetTelemetryConfiguration(string vin) + { + logger.LogTrace("{method}({vin})", nameof(GetFleetTelemetryConfiguration), vin); + var token = await teslaSolarChargerContext.BackendTokens.SingleAsync(); + var fleetApiProxyRequired = await teslaFleetApiService.IsFleetApiProxyEnabled(vin).ConfigureAwait(false); + var decryptionKey = await tscConfigurationService.GetConfigurationValueByKey(constants.TeslaTokenEncryptionKeyKey); + if (decryptionKey == default) + { + logger.LogError("Decryption key not found do not send command"); + throw new InvalidOperationException("Decryption key not found do not send command"); + } + var result = await backendApiService.SendRequestToBackend(HttpMethod.Get, token.AccessToken, + $"FleetTelemetryConfiguration/GetFleetTelemetryConfiguration?encryptionKey={Uri.EscapeDataString(decryptionKey)}&vin={vin}&carRequiresProxy={fleetApiProxyRequired.Value}", null); + if (result.HasError) + { + throw new InvalidOperationException(result.ErrorMessage); + } + + if (result.Data == default) + { + throw new InvalidOperationException("No data returned from backend"); + } + return result.Data; + } +} diff --git a/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs b/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs new file mode 100644 index 000000000..0cf9e1fd7 --- /dev/null +++ b/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs @@ -0,0 +1,8 @@ +namespace TeslaSolarCharger.Shared.Dtos.Support; + +public class DtoDebugCar +{ + public string? Vin { get; set; } + public string? Name { get; set; } + public bool ShouldBeManaged { get; set; } +} diff --git a/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj b/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj index cd080f382..ee0aa8a3d 100644 --- a/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj +++ b/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj @@ -12,6 +12,7 @@ + From 223a03f0fb0b2fe2add04822d0329ce20f890aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sat, 8 Feb 2025 17:55:38 +0100 Subject: [PATCH 18/27] fix(ConfigJsonService): set tesla mate car ID if not set correctly --- .../Server/Services/ConfigJsonService.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/TeslaSolarCharger/Server/Services/ConfigJsonService.cs b/TeslaSolarCharger/Server/Services/ConfigJsonService.cs index 7c5c74384..c0fdc21a9 100644 --- a/TeslaSolarCharger/Server/Services/ConfigJsonService.cs +++ b/TeslaSolarCharger/Server/Services/ConfigJsonService.cs @@ -360,6 +360,27 @@ private async Task> GetCars() OtherCommandCalls = c.OtherCommandCalls, }) .ToListAsync().ConfigureAwait(false); + var teslaMateContext = teslaMateDbContextWrapper.GetTeslaMateContextIfAvailable(); + if (configurationWrapper.UseTeslaMateIntegration() && (teslaMateContext != default)) + { + foreach (var car in cars) + { + if (!string.IsNullOrEmpty(car.Vin)) + { + var teslaMateCarId = await teslaMateContext.Cars + .Where(c => c.Vin == car.Vin) + .Select(c => c.Id) + .FirstOrDefaultAsync(); + if (teslaMateCarId != default && car.TeslaMateCarId != teslaMateCarId) + { + var dbCar = await teslaSolarChargerContext.Cars.FirstAsync(c => c.Id == car.Id); + dbCar.TeslaMateCarId = teslaMateCarId; + await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); + car.TeslaMateCarId = teslaMateCarId; + } + } + } + } foreach (var car in cars) { var fleetTelemetryConfiguration = await teslaSolarChargerContext.Cars From 2bca3d852654dd31e98d6217d24cc50627950e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 11:03:37 +0100 Subject: [PATCH 19/27] fix(BaseConfigurationRazor): allow edit of teslamate db name --- .../Client/Pages/BaseConfiguration.razor | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor index 2f4573b6a..f18168e25 100644 --- a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor +++ b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor @@ -51,13 +51,21 @@ else + LabelText="TeslaMate Database Server Port" + UnitText="" + HelpText="You can use the internal port of the TeslaMate database container"> + + + + + Date: Sun, 9 Feb 2025 11:36:57 +0100 Subject: [PATCH 20/27] feat(SupportRazor): display if car is available in Tesla account --- TeslaSolarCharger/Client/Pages/Support.razor | 2 ++ TeslaSolarCharger/Server/Services/DebugService.cs | 1 + TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor index 3dc086e37..50ac3d709 100644 --- a/TeslaSolarCharger/Client/Pages/Support.razor +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -31,6 +31,8 @@ else
ID: @car.Key
VIN: @car.Value.Vin
Name: @car.Value.Name
+
Is Available in Tesla account: @car.Value.IsAvailableInTeslaAccount
+
Should be managed: @car.Value.ShouldBeManaged

Fleet Telemetry Config

@if (car.Value.Vin != default && _fleetTelemetryConfigs.TryGetValue(car.Value.Vin, out var config)) diff --git a/TeslaSolarCharger/Server/Services/DebugService.cs b/TeslaSolarCharger/Server/Services/DebugService.cs index c2dbf56c3..3e9fad4e4 100644 --- a/TeslaSolarCharger/Server/Services/DebugService.cs +++ b/TeslaSolarCharger/Server/Services/DebugService.cs @@ -18,6 +18,7 @@ public async Task> GetCars() Name = x.Name, Vin = x.Vin, ShouldBeManaged = x.ShouldBeManaged == true, + IsAvailableInTeslaAccount = x.IsAvailableInTeslaAccount, }).ConfigureAwait(false); logger.LogDebug("Found {carCount} cars", cars.Count); return cars; diff --git a/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs b/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs index 0cf9e1fd7..e493e251e 100644 --- a/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs +++ b/TeslaSolarCharger/Shared/Dtos/Support/DtoDebugCar.cs @@ -5,4 +5,5 @@ public class DtoDebugCar public string? Vin { get; set; } public string? Name { get; set; } public bool ShouldBeManaged { get; set; } + public bool IsAvailableInTeslaAccount { get; set; } } From b47ef0812ba9de263af066e422a8cc9375410984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 16:26:33 +0100 Subject: [PATCH 21/27] feat(FleetTelemetryWebSocketService): can handle max configs error message --- .../FleetTelemetry/DtoFleetTelemetryErrorMessage.cs | 1 + .../Server/Services/FleetTelemetryWebSocketService.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryErrorMessage.cs b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryErrorMessage.cs index 3e2dc2740..93073d022 100644 --- a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryErrorMessage.cs +++ b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryErrorMessage.cs @@ -5,4 +5,5 @@ public class DtoFleetTelemetryErrorMessage public List MissingKeyVins { get; set; } = new List(); public List UnsupportedHardwareVins { get; set; } = new List(); public List UnsupportedFirmwareVins { get; set; } = new List(); + public List MaxConfigsVins { get; set; } = new List(); } diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs index 6f314ba29..82a7f0887 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs @@ -388,24 +388,29 @@ private async Task HandleErrorMessage(string jsonMessage) { continue; } - logger.LogInformation("Set Fleet API state for car {vin} to not working", vin); + logger.LogError("Set Fleet API state for car {vin} to not working", vin); car.TeslaFleetApiState = TeslaCarFleetApiState.NotWorking; await context.SaveChangesAsync(); } foreach (var vin in message.UnsupportedFirmwareVins) { - logger.LogInformation("Disable Fleet Telemetry for car {vin} as firmware is not supported", vin); + logger.LogError("Disable Fleet Telemetry for car {vin} as firmware is not supported", vin); await DisableFleetTelemetryForCar(vin).ConfigureAwait(false); } foreach (var vin in message.UnsupportedHardwareVins) { - logger.LogInformation("Disable Fleet Telemetry for car {vin} as hardware is not supported", vin); + logger.LogError("Disable Fleet Telemetry for car {vin} as hardware is not supported", vin); await SetCarToFleetTelemetryHardwareIncompatible(vin).ConfigureAwait(false); await DisableFleetTelemetryForCar(vin).ConfigureAwait(false); } + foreach (var vin in message.MaxConfigsVins) + { + logger.LogError("Car {vin} has already has max allowed Fleet Telemetry configs", vin); + } + return true; } From 55b35977414ad99a48369a3f04af1dae8d8e204d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 16:29:07 +0100 Subject: [PATCH 22/27] feat(SupportRazor): show tooltip on car not part of account --- TeslaSolarCharger/Client/Pages/Support.razor | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor index 50ac3d709..3e907cce2 100644 --- a/TeslaSolarCharger/Client/Pages/Support.razor +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -42,7 +42,8 @@ else } From 188c64f00e1b874f52fd4e42aa2689b8988e4f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 17:17:33 +0100 Subject: [PATCH 23/27] feat(FleetTelemetryConfigurationService): can configure fleet telemetry manually --- .../Server/Controllers/ConfigController.cs | 8 --- .../Server/Controllers/DebugController.cs | 8 +++ .../DtoFleetTelemetryConfigurationResult.cs | 13 ++++ ...slaFleetTelemetryConfigurationErrorType.cs | 9 +++ .../Server/Services/BackendApiService.cs | 2 +- .../IFleetTelemetryConfigurationService.cs | 1 + .../FleetTelemetryConfigurationService.cs | 62 +++++++++++++++++++ 7 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs create mode 100644 TeslaSolarCharger/Server/Enums/TeslaFleetTelemetryConfigurationErrorType.cs diff --git a/TeslaSolarCharger/Server/Controllers/ConfigController.cs b/TeslaSolarCharger/Server/Controllers/ConfigController.cs index 90e91fb9c..c74b8a7b0 100644 --- a/TeslaSolarCharger/Server/Controllers/ConfigController.cs +++ b/TeslaSolarCharger/Server/Controllers/ConfigController.cs @@ -35,13 +35,5 @@ public Task UpdateCarBasicConfiguration(int carId, [FromBody] CarBasicConfigurat { return configJsonService.UpdateCarBasicConfiguration(carId, carBasicConfiguration); } - - [HttpGet] - public async Task GetFleetTelemetryConfiguration(string vin) - { - var config = await fleetTelemetryConfigurationService.GetFleetTelemetryConfiguration(vin); - var configString = JsonConvert.SerializeObject(config, Formatting.Indented); - return Ok(new DtoValue(configString)); - } } } diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs index b9db2b6e7..c9f701d79 100644 --- a/TeslaSolarCharger/Server/Controllers/DebugController.cs +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -56,6 +56,14 @@ public async Task GetFleetTelemetryConfiguration(string vin) return Ok(new DtoValue(configString)); } + [HttpGet] + public async Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration) + { + var config = await fleetTelemetryConfigurationService.SetFleetTelemetryConfiguration(vin, forceReconfiguration); + var configString = JsonConvert.SerializeObject(config, Formatting.Indented); + return Ok(new DtoValue(configString)); + } + [HttpGet] public async Task GetCars() { diff --git a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs new file mode 100644 index 000000000..f40d3f2b4 --- /dev/null +++ b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs @@ -0,0 +1,13 @@ +using TeslaSolarCharger.Server.Enums; + +namespace TeslaSolarCharger.Server.Dtos.FleetTelemetry; + +public class DtoFleetTelemetryConfigurationResult +{ + public bool Success { get; set; } + public bool ConfigurationSent { get; set; } + public string? ReconfigurationReason { get; set; } + public string? ErrorMessage { get; set; } + + public TeslaFleetTelemetryConfigurationErrorType? ConfigurationErrorType { get; set; } = new(); +} diff --git a/TeslaSolarCharger/Server/Enums/TeslaFleetTelemetryConfigurationErrorType.cs b/TeslaSolarCharger/Server/Enums/TeslaFleetTelemetryConfigurationErrorType.cs new file mode 100644 index 000000000..65c606f8f --- /dev/null +++ b/TeslaSolarCharger/Server/Enums/TeslaFleetTelemetryConfigurationErrorType.cs @@ -0,0 +1,9 @@ +namespace TeslaSolarCharger.Server.Enums; + +public enum TeslaFleetTelemetryConfigurationErrorType +{ + MissingKey, + UnsupportedFirmware, + UnsupportedHardware, + MaxConfigs, +} diff --git a/TeslaSolarCharger/Server/Services/BackendApiService.cs b/TeslaSolarCharger/Server/Services/BackendApiService.cs index 77b954d21..235bc23a6 100644 --- a/TeslaSolarCharger/Server/Services/BackendApiService.cs +++ b/TeslaSolarCharger/Server/Services/BackendApiService.cs @@ -373,7 +373,7 @@ public async Task IsFleetApiLicensed(string vin, bool useCache) { var problemDetails = await response.Content.ReadFromJsonAsync(); var message = problemDetails != null - ? $"Cloud Error: {problemDetails.Detail}" + ? $"Cloud Error: Status Code: {response.StatusCode}, ProblemDetails: {problemDetails.Detail}" : "An error occurred while retrieving data from the backend server."; return new Dtos.Result(default, message, problemDetails); diff --git a/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs index 5a9e8f840..d9cfde5f4 100644 --- a/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs @@ -5,4 +5,5 @@ namespace TeslaSolarCharger.Server.Services.Contracts; public interface IFleetTelemetryConfigurationService { Task GetFleetTelemetryConfiguration(string vin); + Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration); } diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs index fe4813a8a..f6678c3ba 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using TeslaSolarCharger.Model.Contracts; using TeslaSolarCharger.Server.Dtos.FleetTelemetry; +using TeslaSolarCharger.Server.Enums; using TeslaSolarCharger.Server.Services.Contracts; using TeslaSolarCharger.Shared.Resources.Contracts; @@ -37,4 +38,65 @@ public async Task GetFleetTelemetryConfigurat } return result.Data; } + + public async Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration) + { + logger.LogTrace("{method}({vin}, {forceReconfiguration})", nameof(SetFleetTelemetryConfiguration), vin, forceReconfiguration); + var carSettings = teslaSolarChargerContext.Cars + .Where(c => c.Vin == vin) + .Select(c => new + { + c.IncludeTrackingRelevantFields, + c.IsFleetTelemetryHardwareIncompatible, + }) + .FirstOrDefault(); + if (carSettings == default) + { + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = "Car not found in local TSC database", + }; + } + + if (carSettings.IsFleetTelemetryHardwareIncompatible) + { + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = "Car hardware is not compatible with fleet telemetry", + ConfigurationErrorType = TeslaFleetTelemetryConfigurationErrorType.UnsupportedHardware, + }; + } + + var token = await teslaSolarChargerContext.BackendTokens.SingleAsync(); + //Fleet API Proxy is required to set fleet Telemetry configuration + var fleetApiProxyRequired = true; + var decryptionKey = await tscConfigurationService.GetConfigurationValueByKey(constants.TeslaTokenEncryptionKeyKey); + if (decryptionKey == default) + { + logger.LogError("Decryption key not found do not send command"); + throw new InvalidOperationException("Decryption key not found do not send command"); + } + var result = await backendApiService.SendRequestToBackend(HttpMethod.Post, token.AccessToken, + $"FleetTelemetryConfiguration/SetFleetTelemetryConfiguration?encryptionKey={Uri.EscapeDataString(decryptionKey)}&vin={vin}&carRequiresProxy={fleetApiProxyRequired}&includeTrackingRelevantFields={carSettings.IncludeTrackingRelevantFields}&forceReconfiguration={forceReconfiguration}", null); + if (result.HasError) + { + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = result.ErrorMessage, + }; + } + var cloudData = result.Data; + if (cloudData == default) + { + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = "No data returned from backend", + }; + } + return cloudData; + } } From 76b502b3be35ba52654d294641fc9d6940d3e9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 17:24:18 +0100 Subject: [PATCH 24/27] feat(FleetTelemetryConfigurationService): handle configuration errors --- .../FleetTelemetryConfigurationService.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs index f6678c3ba..c5152b3b7 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs @@ -3,6 +3,7 @@ using TeslaSolarCharger.Server.Dtos.FleetTelemetry; using TeslaSolarCharger.Server.Enums; using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Enums; using TeslaSolarCharger.Shared.Resources.Contracts; namespace TeslaSolarCharger.Server.Services; @@ -97,6 +98,39 @@ public async Task SetFleetTelemetryConfigu ErrorMessage = "No data returned from backend", }; } + + if (cloudData.ConfigurationErrorType != default) + { + logger.LogError("Error setting fleet telemetry configuration: {errorType}", cloudData.ConfigurationErrorType); + var car = await teslaSolarChargerContext.Cars.FirstAsync(c => c.Vin == vin); + switch (cloudData.ConfigurationErrorType) + { + case TeslaFleetTelemetryConfigurationErrorType.MissingKey: + car.TeslaFleetApiState = TeslaCarFleetApiState.NotWorking; + break; + case TeslaFleetTelemetryConfigurationErrorType.UnsupportedFirmware: + logger.LogWarning("Disable Fleet Telemetry for car {vin} as firmware is not supported", vin); + car.UseFleetTelemetry = false; + car.IncludeTrackingRelevantFields = false; + break; + case TeslaFleetTelemetryConfigurationErrorType.UnsupportedHardware: + logger.LogWarning("Disable Fleet Telemetry for car {vin} as hardware is not supported", vin); + car.IsFleetTelemetryHardwareIncompatible = true; + car.UseFleetTelemetry = false; + car.IncludeTrackingRelevantFields = false; + break; + case TeslaFleetTelemetryConfigurationErrorType.MaxConfigs: + logger.LogWarning("Disable Fleet Telemetry for car {vin} as max configurations reached", vin); + car.UseFleetTelemetry = false; + car.IncludeTrackingRelevantFields = false; + break; + case null: + break; + default: + throw new ArgumentOutOfRangeException(); + } + await teslaSolarChargerContext.SaveChangesAsync(); + } return cloudData; } } From 603c1b8f4b0fa1387c3219ff090cb0c6c467c51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 17:47:59 +0100 Subject: [PATCH 25/27] feat(SupportRazor): can display error messages in fleet Telemetry configuration --- TeslaSolarCharger/Client/Pages/Support.razor | 54 +++++++++++++++++-- .../Server/Controllers/DebugController.cs | 2 +- .../DtoFleetTelemetryConfigurationResult.cs | 2 +- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor index 3e907cce2..18fb0a6a0 100644 --- a/TeslaSolarCharger/Client/Pages/Support.razor +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -1,4 +1,5 @@ @page "/support" +@using MudBlazor.Utilities @using TeslaSolarCharger.Client.Helper.Contracts @using TeslaSolarCharger.Shared.Dtos @using TeslaSolarCharger.Shared.Dtos.Support @@ -34,9 +35,10 @@ else
Is Available in Tesla account: @car.Value.IsAvailableInTeslaAccount
Should be managed: @car.Value.ShouldBeManaged
-

Fleet Telemetry Config

- @if (car.Value.Vin != default && _fleetTelemetryConfigs.TryGetValue(car.Value.Vin, out var config)) + + @if (car.Value.Vin != default && _fleetTelemetryGetConfigs.TryGetValue(car.Value.Vin, out var config)) { +

Fleet Telemetry Config

@config
} @@ -45,6 +47,23 @@ else IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)" DisabledToolTipText="@(car.Value.IsAvailableInTeslaAccount ? null : "Can not check config as car is not part of Tesla account")" OnButtonClicked="@(() => GetFleetTelemetryConfig(car.Value.Vin))"> + + @if (car.Value.Vin != default && _fleetTelemetrySetResults.TryGetValue(car.Value.Vin, out var result)) + { +

Fleet Telemetry SetResult

+
@result
+ } + + + } @@ -53,7 +72,9 @@ else @code { - private readonly Dictionary _fleetTelemetryConfigs = new(); + private readonly Dictionary _fleetTelemetryGetConfigs = new(); + + private readonly Dictionary _fleetTelemetrySetResults = new(); private Dictionary? _debugCars; @@ -79,7 +100,30 @@ else } _isFleetTelemetryLoading = true; - var result = await HttpClientHelper.SendGetRequestAsync>($"api/Config/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}"); + var result = await HttpClientHelper.SendGetRequestAsync>($"api/Debug/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}"); + string stringToDisplay; + if (result.HasError) + { + stringToDisplay = result.ErrorMessage ?? "No error message"; + } + else + { + stringToDisplay = result.Data?.Value ?? "No data"; + } + _fleetTelemetryGetConfigs[vin] = stringToDisplay; + _isFleetTelemetryLoading = false; + } + + private async Task SetFleetTelemetryConfig(string? vin, bool forceReconfiguration) + { + if (vin == default) + { + Snackbar.Add("VIN is unknown", Severity.Error); + return; + } + + _isFleetTelemetryLoading = true; + var result = await HttpClientHelper.SendPostRequestAsync>($"api/Debug/SetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}&forceReconfiguration={forceReconfiguration}", null); string stringToDisplay; if (result.HasError) { @@ -89,7 +133,7 @@ else { stringToDisplay = result.Data?.Value ?? "No data"; } - _fleetTelemetryConfigs[vin] = stringToDisplay; + _fleetTelemetrySetResults[vin] = stringToDisplay; _isFleetTelemetryLoading = false; } } diff --git a/TeslaSolarCharger/Server/Controllers/DebugController.cs b/TeslaSolarCharger/Server/Controllers/DebugController.cs index c9f701d79..8c4268c00 100644 --- a/TeslaSolarCharger/Server/Controllers/DebugController.cs +++ b/TeslaSolarCharger/Server/Controllers/DebugController.cs @@ -56,7 +56,7 @@ public async Task GetFleetTelemetryConfiguration(string vin) return Ok(new DtoValue(configString)); } - [HttpGet] + [HttpPost] public async Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration) { var config = await fleetTelemetryConfigurationService.SetFleetTelemetryConfiguration(vin, forceReconfiguration); diff --git a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs index f40d3f2b4..6e94b8ccc 100644 --- a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs +++ b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs @@ -9,5 +9,5 @@ public class DtoFleetTelemetryConfigurationResult public string? ReconfigurationReason { get; set; } public string? ErrorMessage { get; set; } - public TeslaFleetTelemetryConfigurationErrorType? ConfigurationErrorType { get; set; } = new(); + public TeslaFleetTelemetryConfigurationErrorType? ConfigurationErrorType { get; set; } } From 701844c4573eb446ff299f275991e807d5e73741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 17:56:50 +0100 Subject: [PATCH 26/27] feat(SupportRazor): improve page styling --- TeslaSolarCharger/Client/Pages/Support.razor | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/TeslaSolarCharger/Client/Pages/Support.razor b/TeslaSolarCharger/Client/Pages/Support.razor index 18fb0a6a0..5f8cdfe92 100644 --- a/TeslaSolarCharger/Client/Pages/Support.razor +++ b/TeslaSolarCharger/Client/Pages/Support.razor @@ -1,11 +1,11 @@ @page "/support" -@using MudBlazor.Utilities @using TeslaSolarCharger.Client.Helper.Contracts @using TeslaSolarCharger.Shared.Dtos @using TeslaSolarCharger.Shared.Dtos.Support @inject IHttpClientHelper HttpClientHelper @inject ISnackbar Snackbar +@inject NavigationManager NavigationManager

Support

@@ -15,10 +15,13 @@
Never share logs publicly
Logs might contain sensitive information like your vehicle's location. Do not share logs publicly. +

General

-Download Logs + -

Car Debug Details

+

Car Debug Details

@if (_debugCars == default) { @@ -38,7 +41,7 @@ else @if (car.Value.Vin != default && _fleetTelemetryGetConfigs.TryGetValue(car.Value.Vin, out var config)) { -

Fleet Telemetry Config

+

Fleet Telemetry Config

@config
} @@ -50,7 +53,7 @@ else @if (car.Value.Vin != default && _fleetTelemetrySetResults.TryGetValue(car.Value.Vin, out var result)) { -

Fleet Telemetry SetResult

+

Fleet Telemetry SetResult

@result
} From 59b262f50c924aecfce8297aa9301731bd4c6a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 9 Feb 2025 21:36:32 +0100 Subject: [PATCH 27/27] feat(FleetTelemetryConnection): do not call fleet telemetry connect including tesla token encryption key --- .../DtoFleetTelemetryConfigurationResult.cs | 1 + .../Server/Scheduling/JobManager.cs | 5 +- .../Jobs/FleetTelemetryReconnectionJob.cs | 4 +- .../IFleetTelemetryConfigurationService.cs | 1 + .../FleetTelemetryConfigurationService.cs | 63 ++++++++++++++++++- .../FleetTelemetryWebSocketService.cs | 15 +---- TeslaSolarCharger/Server/appsettings.json | 4 +- .../Shared/Resources/Constants.cs | 1 + .../Shared/Resources/Contracts/IConstants.cs | 1 + 9 files changed, 78 insertions(+), 17 deletions(-) diff --git a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs index 6e94b8ccc..4fe468141 100644 --- a/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs +++ b/TeslaSolarCharger/Server/Dtos/FleetTelemetry/DtoFleetTelemetryConfigurationResult.cs @@ -8,6 +8,7 @@ public class DtoFleetTelemetryConfigurationResult public bool ConfigurationSent { get; set; } public string? ReconfigurationReason { get; set; } public string? ErrorMessage { get; set; } + public long? ValidUntil { get; set; } public TeslaFleetTelemetryConfigurationErrorType? ConfigurationErrorType { get; set; } } diff --git a/TeslaSolarCharger/Server/Scheduling/JobManager.cs b/TeslaSolarCharger/Server/Scheduling/JobManager.cs index 4e2d229d8..aaccf8976 100644 --- a/TeslaSolarCharger/Server/Scheduling/JobManager.cs +++ b/TeslaSolarCharger/Server/Scheduling/JobManager.cs @@ -101,6 +101,8 @@ public async Task StartJobs() .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(59)).Build(); var fleetApiTokenRefreshTrigger = TriggerBuilder.Create().WithIdentity("fleetApiTokenRefreshTrigger") + //start 5 seconds later, so backend token is already refreshed + .StartAt(currentDate.AddSeconds(5)) .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(58)).Build(); var vehicleDataRefreshTrigger = TriggerBuilder.Create().WithIdentity("vehicleDataRefreshTrigger") @@ -118,12 +120,13 @@ public async Task StartJobs() var errorDetectionTrigger = TriggerBuilder.Create() .WithIdentity("errorDetectionTrigger") - .StartAt(latestTriggerStartTime.Add(TimeSpan.FromSeconds(5))) + .StartAt(latestTriggerStartTime.Add(TimeSpan.FromSeconds(12))) .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(62)).Build(); var bleApiVersionDetectionTrigger = TriggerBuilder.Create().WithIdentity("bleApiVersionDetectionTrigger") .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(61)).Build(); var fleetTelemetryReconnectionTrigger = TriggerBuilder.Create().WithIdentity("fleetTelemetryReconnectionTrigger") + .StartAt(currentDate.AddSeconds(10)) .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(61)).Build(); var random = new Random(); diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs index 46a899251..950701a88 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs @@ -5,11 +5,13 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; public class FleetTelemetryReconnectionJob( ILogger logger, - IFleetTelemetryWebSocketService service) : IJob + IFleetTelemetryWebSocketService service, + IFleetTelemetryConfigurationService fleetTelemetryConfigurationService) : IJob { public async Task Execute(IJobExecutionContext context) { logger.LogTrace("{method}({context})", nameof(Execute), context); + await fleetTelemetryConfigurationService.ReconfigureAllCarsIfRequired(); await service.ReconnectWebSocketsForEnabledCars(); } } diff --git a/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs index d9cfde5f4..e4515206a 100644 --- a/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/Contracts/IFleetTelemetryConfigurationService.cs @@ -6,4 +6,5 @@ public interface IFleetTelemetryConfigurationService { Task GetFleetTelemetryConfiguration(string vin); Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration); + Task ReconfigureAllCarsIfRequired(); } diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs index c5152b3b7..9257abe3d 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs @@ -1,8 +1,10 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using TeslaSolarCharger.Model.Contracts; using TeslaSolarCharger.Server.Dtos.FleetTelemetry; using TeslaSolarCharger.Server.Enums; using TeslaSolarCharger.Server.Services.Contracts; +using TeslaSolarCharger.Shared.Contracts; using TeslaSolarCharger.Shared.Enums; using TeslaSolarCharger.Shared.Resources.Contracts; @@ -13,7 +15,9 @@ public class FleetTelemetryConfigurationService(ILogger GetFleetTelemetryConfiguration(string vin) { @@ -43,6 +47,15 @@ public async Task GetFleetTelemetryConfigurat public async Task SetFleetTelemetryConfiguration(string vin, bool forceReconfiguration) { logger.LogTrace("{method}({vin}, {forceReconfiguration})", nameof(SetFleetTelemetryConfiguration), vin, forceReconfiguration); + if (!await backendApiService.IsBaseAppLicensed(true)) + { + logger.LogWarning("Base App is not licensed, do not connect to Fleet Telemetry"); + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = "Can not configure Fleet Telemetry as TSC is not licensed", + }; + } var carSettings = teslaSolarChargerContext.Cars .Where(c => c.Vin == vin) .Select(c => new @@ -69,6 +82,16 @@ public async Task SetFleetTelemetryConfigu ConfigurationErrorType = TeslaFleetTelemetryConfigurationErrorType.UnsupportedHardware, }; } + if (carSettings.IncludeTrackingRelevantFields && (!await backendApiService.IsFleetApiLicensed(vin, true))) + { + logger.LogWarning("Car {vin} is not licensed for Fleet API, do not connect as IncludeTrackingRelevant fields is enabled", vin); + return new DtoFleetTelemetryConfigurationResult + { + Success = false, + ErrorMessage = "Fleet API license required for car {vin} as Include Tracking Relevant Fields is enabled", + }; + } + var token = await teslaSolarChargerContext.BackendTokens.SingleAsync(); //Fleet API Proxy is required to set fleet Telemetry configuration @@ -133,4 +156,42 @@ public async Task SetFleetTelemetryConfigu } return cloudData; } + + public async Task ReconfigureAllCarsIfRequired() + { + logger.LogTrace("{method}", nameof(ReconfigureAllCarsIfRequired)); + var cars = await teslaSolarChargerContext.Cars + .Where(c => c.UseFleetTelemetry + && (c.ShouldBeManaged == true) + && (c.TeslaFleetApiState != TeslaCarFleetApiState.NotWorking) + && (c.TeslaFleetApiState != TeslaCarFleetApiState.OpenedLinkButNotTested) + && (c.TeslaFleetApiState != TeslaCarFleetApiState.NotConfigured) + && (c.IsFleetTelemetryHardwareIncompatible == false)) + .Select(c => new { c.Vin, IncludeTrackingRelevantFields = c.IncludeTrackingRelevantFields, }) + .ToListAsync(); + var currentDate = dateTimeProvider.DateTimeOffSetUtcNow(); + foreach (var car in cars) + { + if (car.Vin == default) + { + continue; + } + var reconfigurationRequired = + !memoryCache.TryGetValue(constants.FleetTelemetryConfigurationExpiryKey + car.Vin, out DateTimeOffset expiryTime); + if(!reconfigurationRequired) + { + reconfigurationRequired = expiryTime < currentDate; + } + if (!reconfigurationRequired) + { + logger.LogDebug("Fleet Telemetry reconfiguration for car {vin} not required as expires in the future", car.Vin); + continue; + } + var result = await SetFleetTelemetryConfiguration(car.Vin, false); + if (result.Success && (result.ValidUntil != default)) + { + memoryCache.Set(constants.FleetTelemetryConfigurationExpiryKey + car.Vin, DateTimeOffset.FromUnixTimeSeconds(result.ValidUntil.Value)); + } + } + } } diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs index 82a7f0887..e3379d109 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryWebSocketService.cs @@ -104,7 +104,7 @@ await existingClient.WebSocketClient.SendAsync(segment, WebSocketMessageType.Tex Clients.Remove(existingClient); } - ConnectToFleetTelemetryApi(car.Vin, car.IncludeTrackingRelevantFields); + ConnectToFleetTelemetryApi(car.Vin); } } @@ -126,24 +126,15 @@ await client.WebSocketClient } } - private async Task ConnectToFleetTelemetryApi(string vin, bool includeTrackingRelevantFields) + private async Task ConnectToFleetTelemetryApi(string vin) { logger.LogTrace("{method}({carId})", nameof(ConnectToFleetTelemetryApi), vin); var scope = serviceProvider.CreateScope(); var dateTimeProvider = scope.ServiceProvider.GetRequiredService(); var configurationWrapper = scope.ServiceProvider.GetRequiredService(); var context = scope.ServiceProvider.GetRequiredService(); - var tscConfigurationService = scope.ServiceProvider.GetRequiredService(); - var constants = scope.ServiceProvider.GetRequiredService(); var currentDate = dateTimeProvider.UtcNow(); - var decryptionKey = await tscConfigurationService.GetConfigurationValueByKey(constants.TeslaTokenEncryptionKeyKey); - if (decryptionKey == default) - { - logger.LogError("Decryption key not found do not send command"); - throw new InvalidOperationException("No Decryption key found."); - } - var url = configurationWrapper.FleetTelemetryApiUrl() + - $"vin={vin}&forceReconfiguration=false&includeTrackingRelevantFields={includeTrackingRelevantFields}&encryptionKey={Uri.EscapeDataString(decryptionKey)}"; + var url = configurationWrapper.FleetTelemetryApiUrl() + $"vin={vin}"; var authToken = await context.BackendTokens.AsNoTracking().SingleOrDefaultAsync(); if(authToken == default) { diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json index d04aa8832..5ff48ea27 100644 --- a/TeslaSolarCharger/Server/appsettings.json +++ b/TeslaSolarCharger/Server/appsettings.json @@ -37,8 +37,8 @@ "CarRefreshAfterCommandSeconds": 11, "BleUsageStopAfterErrorSeconds": 300, "FleetApiRefreshIntervalSeconds": 500, - "FleetTelemetryApiUrl": "wss://api.fleet-telemetry.solar4car.com/ws?", - "BetaFleetTelemetryApiUrl": "wss://beta.api.fleet-telemetry.solar4car.com/ws?", + "FleetTelemetryApiUrl": "wss://api.fleet-telemetry.solar4car.com/notokenws?", + "BetaFleetTelemetryApiUrl": "wss://beta.api.fleet-telemetry.solar4car.com/notokenws?", "BackendPasswordDefaultLength": 25, "GridPriceProvider": { "EnergyProvider": "FixedPrice", diff --git a/TeslaSolarCharger/Shared/Resources/Constants.cs b/TeslaSolarCharger/Shared/Resources/Constants.cs index 924a25670..6b8087587 100644 --- a/TeslaSolarCharger/Shared/Resources/Constants.cs +++ b/TeslaSolarCharger/Shared/Resources/Constants.cs @@ -44,6 +44,7 @@ public class Constants : IConstants public string BackendTokenStateKey => "BackendTokenState"; public string IsBaseAppLicensedKey => "IsBaseAppLicensed"; public string IsFleetApiLicensedKey => "IsFleetApiLicensed_"; + public string FleetTelemetryConfigurationExpiryKey => "FleetTelemetryConfigurationExpiry_"; public string GridPoleIcon => "power-pole"; } diff --git a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs index 251901a17..7878ed5d9 100644 --- a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs +++ b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs @@ -44,4 +44,5 @@ public interface IConstants string BackendTokenStateKey { get; } string IsBaseAppLicensedKey { get; } string IsFleetApiLicensedKey { get; } + string FleetTelemetryConfigurationExpiryKey { get; } }