All checks were successful
Build, Push and Run Container / build (push) Successful in 27s
Specifies that the logger and http client factory should be injected via dependency injection instead of directly from the request. This allows for better testability and separation of concerns.
390 lines
21 KiB
C#
390 lines
21 KiB
C#
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
using Microsoft.IdentityModel.Protocols;
|
|
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
|
using ProofOfConcept.Models;
|
|
using ProofOfConcept.Services;
|
|
using ProofOfConcept.Utilities;
|
|
|
|
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
|
|
|
|
var builder = WebApplication.CreateSlimBuilder(args);
|
|
|
|
// Load static web assets manifest (referenced libs + your wwwroot)
|
|
builder.WebHost.UseStaticWebAssets();
|
|
|
|
// builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); });
|
|
|
|
// Add services
|
|
builder.Services.AddOpenApi();
|
|
builder.Services.AddMediator();
|
|
builder.Services.AddMemoryCache();
|
|
builder.Services.AddHybridCache();
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddHealthChecks()
|
|
.AddAsyncCheck("", cancellationToken => Task.FromResult(HealthCheckResult.Healthy()), ["ready"]); //TODO: Check tag
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddHttpClient().AddHttpClient("InsecureClient")
|
|
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
|
{
|
|
ServerCertificateCustomValidationCallback =
|
|
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
|
});
|
|
|
|
builder.Services
|
|
.AddAuthentication(o =>
|
|
{
|
|
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
|
})
|
|
.AddCookie()
|
|
.AddOpenIdConnect(o =>
|
|
{
|
|
// Point directly at the third-party metadata
|
|
// Metadata is wrong... it sets non-existing uris like: "jwks_uri": "https://fleet-auth.tesla.com/oauth2/v3/discovery/thirdparty/keys"
|
|
// o.MetadataAddress = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/thirdparty/.well-known/openid-configuration";
|
|
//
|
|
// // === Use Fleet-Auth third-party OIDC config ===
|
|
// o.Authority = "https://fleet-auth.tesla.com/oauth2/v3/nts";
|
|
//
|
|
// o.Configuration ??= new OpenIdConnectConfiguration();
|
|
// o.Configuration.AuthorizationEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
|
|
// o.Configuration.TokenEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
|
|
// o.Configuration.JwksUri = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/discovery/thirdparty/keys";
|
|
// o.Configuration.EndSessionEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/logout";
|
|
// o.Configuration.UserInfoEndpoint = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/userinfo";
|
|
//
|
|
// o.Configuration.TokenEndpointAuthMethodsSupported.Clear();
|
|
// o.Configuration.TokenEndpointAuthMethodsSupported.Add("client_secret_post");
|
|
//
|
|
// o.Configuration.ResponseModesSupported.Clear();
|
|
// o.Configuration.ResponseModesSupported.Add("query");
|
|
//
|
|
// o.Configuration.GrantTypesSupported.Clear();
|
|
// o.Configuration.GrantTypesSupported.Add("authorization_code");
|
|
//
|
|
// o.Configuration.SubjectTypesSupported.Clear();
|
|
// o.Configuration.SubjectTypesSupported.Add("public");
|
|
//
|
|
// o.Configuration.ScopesSupported.Clear();
|
|
// o.Configuration.ScopesSupported.Add("openid");
|
|
// o.Configuration.ScopesSupported.Add("email");
|
|
// o.Configuration.ScopesSupported.Add("profile");
|
|
// o.Configuration.ScopesSupported.Add("metadata");
|
|
//
|
|
// o.Configuration.IdTokenSigningAlgValuesSupported.Clear();
|
|
// o.Configuration.IdTokenSigningAlgValuesSupported.Add("RS256");
|
|
//
|
|
// o.Configuration.TokenEndpointAuthSigningAlgValuesSupported.Clear();
|
|
// o.Configuration.TokenEndpointAuthSigningAlgValuesSupported.Add("RS256");
|
|
//
|
|
// o.Configuration.ClaimsSupported.Clear();
|
|
// o.Configuration.ClaimsSupported.Add("iss");
|
|
// o.Configuration.ClaimsSupported.Add("iat");
|
|
// o.Configuration.ClaimsSupported.Add("exp");
|
|
// o.Configuration.ClaimsSupported.Add("nonce");
|
|
// o.Configuration.ClaimsSupported.Add("sub");
|
|
// o.Configuration.ClaimsSupported.Add("aud");
|
|
|
|
o.ConfigurationManager = new TeslaOIDCConfigurationManager("https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/thirdparty/.well-known/openid-configuration");
|
|
|
|
// Standard OIDC web app settings
|
|
o.ResponseType = "code";
|
|
o.UsePkce = true;
|
|
o.SaveTokens = true;
|
|
|
|
o.ClientId = "b2240ee4-332a-4252-91aa-bbcc24f78fdb";
|
|
o.ClientSecret = "ta-secret.YG+XSdlvr6Lv8U-x";
|
|
|
|
// Must exactly match what you registered in Tesla portal
|
|
o.CallbackPath = new PathString("/token-exchange");
|
|
|
|
// Set scopes
|
|
o.Scope.Clear();
|
|
o.Scope.Add("openid");
|
|
o.Scope.Add("offline_access");
|
|
o.Scope.Add("vehicle_device_data");
|
|
o.Scope.Add("vehicle_location");
|
|
|
|
// Optional Tesla flags
|
|
o.AdditionalAuthorizationParameters.Add("require_requested_scopes", "true");
|
|
o.AdditionalAuthorizationParameters.Add("show_keypair_step", "true");
|
|
o.AdditionalAuthorizationParameters.Add("prompt_missing_scopes", "true");
|
|
|
|
o.TokenValidationParameters.ValidateIssuer = true;
|
|
o.TokenValidationParameters.ValidIssuer = "https://fleet-auth.tesla.com/oauth2/v3/nts";
|
|
|
|
// ✅ Add the Fleet API audience to the token POST
|
|
const string FleetApiAudience = "https://fleet-api.prd.eu.vn.cloud.tesla.com"; // set your region base
|
|
o.Events = new OpenIdConnectEvents
|
|
{
|
|
OnAuthorizationCodeReceived = ctx =>
|
|
{
|
|
ctx.TokenEndpointRequest.Parameters["audience"] = FleetApiAudience;
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
|
|
// Auto-refresh keys if Tesla rotates JWKS
|
|
o.RefreshOnIssuerKeyNotFound = true;
|
|
});
|
|
|
|
// Add own services
|
|
builder.Services.AddSingleton<IMessageProcessor, MessageProcessor>();
|
|
builder.Services.AddTransient<ITeslaAuthenticatorService, TeslaAuthenticatorService>();
|
|
builder.Services.AddTransient<ZoneDeterminatorService>();
|
|
|
|
// Add hosted services
|
|
builder.Services.AddHostedService<MQTTServer>();
|
|
builder.Services.AddHostedService<MQTTClient>();
|
|
|
|
//Build app
|
|
WebApplication app = builder.Build();
|
|
|
|
ForwardedHeadersOptions forwardedHeadersOptions = new ForwardedHeadersOptions() { ForwardedHeaders = ForwardedHeaders.All };
|
|
forwardedHeadersOptions.KnownNetworks.Clear();
|
|
forwardedHeadersOptions.KnownProxies.Clear();
|
|
app.UseForwardedHeaders(forwardedHeadersOptions);
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.MapOpenApi();
|
|
app.MapGet("/GetPartnerAuthenticationToken", ([FromServices] TeslaAuthenticatorService service) => service.GetPartnerAuthenticationTokenAsync());
|
|
app.MapGet("/PartnerToken", ([FromQueryAttribute] string json, [FromServices] IMemoryCache memoryCache) =>
|
|
{
|
|
var serializerOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
|
};
|
|
|
|
Token? token = JsonSerializer.Deserialize<Token>(json, serializerOptions);
|
|
if (token is not null)
|
|
memoryCache.Set(Keys.TeslaPartnerToken, token, token.Expires.Subtract(TimeSpan.FromSeconds(5)));
|
|
|
|
return JsonSerializer.Serialize(token, new JsonSerializerOptions() { WriteIndented = true });
|
|
});
|
|
app.MapGet("/CheckRegisteredApplication", ([FromServices] ITeslaAuthenticatorService service) => service.CheckApplicationRegistrationAsync());
|
|
app.MapGet("/RegisterApplication", ([FromServices] ITeslaAuthenticatorService service) => service.RegisterApplicationAsync());
|
|
app.MapGet("/Authorize", async ([FromQuery] string redirect, [FromServices] IHttpContextAccessor contextAccessor) => await (contextAccessor.HttpContext!).ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = redirect }));
|
|
app.MapGet("/KeyPairing", () => Results.Redirect("https://tesla.com/_ak/automatic-parking.app"));
|
|
app.MapGet("/Tokens", async (IHttpContextAccessor httpContextAccessor) =>
|
|
{
|
|
var ctx = httpContextAccessor.HttpContext;
|
|
|
|
var accessToken = await ctx.GetTokenAsync("access_token");
|
|
var idToken = await ctx.GetTokenAsync("id_token");
|
|
var refreshToken = await ctx.GetTokenAsync("refresh_token");
|
|
var expiresAtRaw = await ctx.GetTokenAsync("expires_at"); // ISO 8601 string
|
|
|
|
return JsonSerializer.Serialize(new
|
|
{
|
|
AccessToken = accessToken,
|
|
IDToken = idToken,
|
|
RefreshToken = refreshToken,
|
|
ExpiresAtRaw = expiresAtRaw
|
|
});
|
|
});
|
|
app.MapGet("DebugProxy", (IHttpContextAccessor httpContextAccessor) =>
|
|
{
|
|
var ctx = httpContextAccessor.HttpContext!;
|
|
var request = ctx.Request;
|
|
|
|
Dictionary<string, string> headers = new Dictionary<string, string>();
|
|
headers.Add("Host", request.Host.Value ?? "");
|
|
headers.Add("Scheme", request.Scheme);
|
|
headers.Add("Method", request.Method);
|
|
headers.Add("Path", request.Path.Value ?? "");
|
|
headers.Add("QueryString", request.QueryString.Value ?? "");
|
|
headers.Add("RemoteIpAddress", ctx.Connection.RemoteIpAddress?.ToString() ?? "");
|
|
headers.Add("RemotePort", ctx.Connection.RemotePort.ToString());
|
|
headers.Add("LocalIpAddress", ctx.Connection.LocalIpAddress?.ToString() ?? "");
|
|
headers.Add("LocalPort", ctx.Connection.LocalPort.ToString());
|
|
headers.Add("IsHttps", request.IsHttps.ToString());
|
|
headers.Add("X-Forwarded-For", request.Headers["X-Forwarded-For"].ToString());
|
|
headers.Add("X-Forwarded-Proto", request.Headers["X-Forwarded-Proto"].ToString());
|
|
headers.Add("X-Forwarded-Host", request.Headers["X-Forwarded-Host"].ToString());
|
|
headers.Add("X-Forwarded-Port", request.Headers["X-Forwarded-Port"].ToString());
|
|
headers.Add("X-Forwarded-Prefix", request.Headers["X-Forwarded-Prefix"].ToString());
|
|
headers.Add("X-Forwarded-Server", request.Headers["X-Forwarded-Server"].ToString());
|
|
headers.Add("X-Forwarded-Path", request.Headers["X-Forwarded-Path"].ToString());
|
|
headers.Add("X-Forwarded-PathBase", request.Headers["X-Forwarded-PathBase"].ToString());
|
|
headers.Add("X-Forwarded-Query", request.Headers["X-Forwarded-Query"].ToString());
|
|
headers.Add("X-Forwarded-Query-String", request.Headers["X-Forwarded-Query-String"].ToString());
|
|
headers.Add("Connection", request.Headers["Connection"].ToString());
|
|
headers.Add("Accept", request.Headers["Accept"].ToString());
|
|
headers.Add("Accept-Encoding", request.Headers["Accept-Encoding"].ToString());
|
|
headers.Add("Accept-Language", request.Headers["Accept-Language"].ToString());
|
|
headers.Add("Cache-Control", request.Headers["Cache-Control"].ToString());
|
|
headers.Add("Content-Length", request.Headers["Content-Length"].ToString());
|
|
headers.Add("Content-Type", request.Headers["Content-Type"].ToString());
|
|
headers.Add("Cookie", request.Headers["Cookie"].ToString());
|
|
headers.Add("Pragma", request.Headers["Pragma"].ToString());
|
|
headers.Add("Referer", request.Headers["Referer"].ToString());
|
|
|
|
String json = JsonSerializer.Serialize(headers, new JsonSerializerOptions() { WriteIndented = true });
|
|
|
|
return json;
|
|
});
|
|
|
|
app.MapGet("/Diagnose", async ([FromServices] ILogger<Configurator> logger, [FromServices] IHttpContextAccessor httpContextAccessor, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
logger.LogTrace("Checking errors for car...");
|
|
|
|
HttpContext? context = httpContextAccessor.HttpContext;
|
|
|
|
if (context is null)
|
|
return Results.BadRequest();
|
|
|
|
string? access_token = await context.GetTokenAsync("access_token");
|
|
string? refresh_token = await context.GetTokenAsync("refresh_token");
|
|
logger.LogCritical("User has access_token: {access_token} and refresh_token: {refresh_token}", access_token, refresh_token);
|
|
|
|
if (String.IsNullOrEmpty(access_token))
|
|
return Results.LocalRedirect("/Authorize?redirect=Diagnose");
|
|
|
|
HttpClient client = httpClientFactory.CreateClient("InsecureClient");
|
|
client.BaseAddress = new Uri("https://tesla_command_proxy");
|
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {access_token}");
|
|
|
|
//Get cars
|
|
VehiclesEnvelope? vehiclesEnvelope = await client.GetFromJsonAsync<VehiclesEnvelope>("/api/1/vehicles");
|
|
string[] vinNumbers = vehiclesEnvelope?.Response.Select(x => x.Vin ?? "").Where(v => !String.IsNullOrWhiteSpace(v)).ToArray() ?? Array.Empty<string>();
|
|
logger.LogCritical("User has access to {count} cars: {vins}", vinNumbers.Length, String.Join(", ", vinNumbers));
|
|
|
|
if (vinNumbers.Length == 0)
|
|
return Results.Ok("No cars found");
|
|
|
|
foreach (string vinNumber in vinNumbers)
|
|
{
|
|
HttpResponseMessage responseMessage = await client.GetAsync($"/api/1/vehicles/{vinNumber}/fleet_telemetry_errors");
|
|
string response = await responseMessage.Content.ReadAsStringAsync();
|
|
logger.LogInformation("Telemetry errors for {vinNumber}: {response}", vinNumber, response);
|
|
}
|
|
|
|
return Results.Ok("Done");
|
|
});
|
|
|
|
app.MapGet("/Tesla", async ([FromServices] ILogger<Configurator> logger, [FromServices] IHttpContextAccessor httpContextAccessor, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
HttpContext? context = httpContextAccessor.HttpContext;
|
|
|
|
if (context is null)
|
|
return Results.BadRequest();
|
|
|
|
string? access_token = await context.GetTokenAsync("access_token");
|
|
string? refresh_token = await context.GetTokenAsync("refresh_token");
|
|
logger.LogCritical("User has access_token: {access_token} and refresh_token: {refresh_token}", access_token, refresh_token);
|
|
|
|
if (String.IsNullOrEmpty(access_token))
|
|
return Results.LocalRedirect("/Authorize");
|
|
|
|
HttpClient client = httpClientFactory.CreateClient("InsecureClient");
|
|
client.BaseAddress = new Uri("https://tesla_command_proxy");
|
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {access_token}");
|
|
|
|
//Get cars
|
|
VehiclesEnvelope? vehiclesEnvelope = await client.GetFromJsonAsync<VehiclesEnvelope>("/api/1/vehicles");
|
|
string[] vinNumbers = vehiclesEnvelope?.Response.Select(x => x.Vin ?? "").Where(v => !String.IsNullOrWhiteSpace(v)).ToArray() ?? Array.Empty<string>();
|
|
logger.LogCritical("User has access to {count} cars: {vins}", vinNumbers.Length, String.Join(", ", vinNumbers));
|
|
|
|
if (vinNumbers.Length == 0)
|
|
return Results.Ok("No cars found");
|
|
|
|
//Check if key pairing is required
|
|
var requestObject = new { vins = vinNumbers };
|
|
HttpResponseMessage statusResponse = await client.PostAsJsonAsync("/api/1/vehicles/fleet_status", requestObject);
|
|
string statusResponseContent = await statusResponse.Content.ReadAsStringAsync();
|
|
logger.LogTrace("Status response: {statusResponseContent}", statusResponseContent);
|
|
|
|
FleetResponse? fleetResponse = JsonSerializer.Deserialize<FleetResponse>(statusResponseContent);
|
|
|
|
if (!fleetResponse?.KeyPairedVins.Any() ?? false)
|
|
return Results.Redirect("/KeyPairing");
|
|
|
|
//Get CA from validate server file
|
|
string fileContent = await File.ReadAllTextAsync("Resources/validate_server.json");
|
|
ValidationModel? vm = JsonSerializer.Deserialize<ValidationModel>(fileContent);
|
|
|
|
TelemetryConfigRequest configRequest = new TelemetryConfigRequest()
|
|
{
|
|
Vins = new List<string>(vinNumbers),
|
|
Config = new TelemetryConfig()
|
|
{
|
|
Hostname = "tesla-telemetry.automatic-parking.app",
|
|
Port = 443,
|
|
CertificateAuthority = vm?.CA ?? "EMPTY",
|
|
Fields = new Dictionary<string, TelemetryFieldConfig>()
|
|
{
|
|
{ "Gear", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Locked", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "DriverSeatOccupied", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "GpsState", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Location", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
}
|
|
}
|
|
};
|
|
logger.LogInformation("Config request: {configRequest}", JsonSerializer.Serialize(configRequest, new JsonSerializerOptions() { WriteIndented = true }));
|
|
|
|
HttpResponseMessage response = await client.PostAsJsonAsync("/api/1/vehicles/fleet_telemetry_config", configRequest);
|
|
return Results.Ok(response.Content.ReadAsStringAsync());
|
|
});
|
|
}
|
|
|
|
app.MapGet("/RePair", async ([FromServices] ILogger logger, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
string access_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InFEc3NoM2FTV0cyT05YTTdLMzFWV0VVRW5BNCJ9.eyJpc3MiOiJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My9udHMiLCJhenAiOiJiMjI0MGVlNC0zMzJhLTQyNTItOTFhYS1iYmNjMjRmNzhmZGIiLCJzdWIiOiJkZDg3Mzc4OC00ZjliLTQyY2UtYmRkNi00YzdmMjQxOGMwN2UiLCJhdWQiOlsiaHR0cHM6Ly9mbGVldC1hcGkucHJkLm5hLnZuLmNsb3VkLnRlc2xhLmNvbSIsImh0dHBzOi8vZmxlZXQtYXBpLnByZC5ldS52bi5jbG91ZC50ZXNsYS5jb20iLCJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My91c2VyaW5mbyJdLCJzY3AiOlsib3BlbmlkIiwib2ZmbGluZV9hY2Nlc3MiLCJ2ZWhpY2xlX2RldmljZV9kYXRhIiwidmVoaWNsZV9sb2NhdGlvbiJdLCJhbXIiOlsicHdkIl0sImV4cCI6MTc1NTgwODkzNSwiaWF0IjoxNzU1NzgwMTM1LCJvdV9jb2RlIjoiRVUiLCJsb2NhbGUiOiJodS1IVSIsImFjY291bnRfdHlwZSI6InBlcnNvbiIsIm9wZW5fc291cmNlIjpmYWxzZSwiYWNjb3VudF9pZCI6IjE5YTBhZjRmLTY1ZDgtNDc2MC1hYjVmLTZjMzk3ZTViMTI4ZiIsImF1dGhfdGltZSI6MTc1NTc4MDEzNCwibm9uY2UiOiI2Mzg5MTM3NjkyNDEzMDI0MjMuTmpBNE0yWmpOalV0Wmpkak9DMDBabVF6TFdFeVlqa3RaR05rWVRKa01HSTRZMll6TnpKa09HSTNPV0V0Wmprd055MDBPREZpTFdGbE1UQXRNbVV4WlRnME1UZG1PV00xIn0.IAfZApY-P3HkRp4U2oO_T2DUFplbGfwuOfnXihcnlmiGKxKSSSuJ5aI76pcaDg9saxrhIhg17KjmEC4gL90ByDk6P7KUMp_xot0FN1Vtwy3C8_NDltebhZdM2emR5N7QHXdP4OYAQNvHwanRwBUeQthQ8pFUk9-fDzsZhwkTjrGYtvpQKZK-pn5GCLIKLib4AemsidCtfOlObjgqTd6wf_Tdb2dkbt-ACNIueTcmfXt-eFUZVRySwrvOb5pOWAUkjTUCpW074ySJjj_TDYheQHA9aTZsDJWCUNHC-51qnawiUvh-LwYWasfFhQZQisSfSusCgpGvHRVsyuLbOtd2fQ and refresh_token: EU_dee3cce77456a4d585d78e0536427a508eeed6ab14d86238b776000590acb7cd";
|
|
|
|
HttpClient client = httpClientFactory.CreateClient("InsecureClient");
|
|
client.BaseAddress = new Uri("https://tesla_command_proxy");
|
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {access_token}");
|
|
|
|
//Get fleet_status endpoint
|
|
string vin = "5YJ3E7EB7KF291652";
|
|
|
|
var resp = await client.DeleteAsync($"/api/1/vehicles/{vin}/fleet_telemetry_config");
|
|
logger.LogInformation("Fleet telemetry remove response ({ResponseStatusCode}): {ResponseContent}): ", resp.StatusCode, await resp.Content.ReadAsStringAsync());;
|
|
|
|
//Get CA from validate server file
|
|
string fileContent = await File.ReadAllTextAsync("Resources/validate_server.json");
|
|
ValidationModel? vm = JsonSerializer.Deserialize<ValidationModel>(fileContent);
|
|
|
|
TelemetryConfigRequest configRequest = new TelemetryConfigRequest()
|
|
{
|
|
Vins = new List<string>(),
|
|
Config = new TelemetryConfig()
|
|
{
|
|
Hostname = "tesla-telemetry.automatic-parking.app",
|
|
Port = 443,
|
|
CertificateAuthority = vm?.CA ?? "EMPTY",
|
|
Fields = new Dictionary<string, TelemetryFieldConfig>()
|
|
{
|
|
{ "Gear", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Locked", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "DriverSeatOccupied", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "GpsState", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
{ "Location", new TelemetryFieldConfig() { IntervalSeconds = 60 } },
|
|
}
|
|
}
|
|
};
|
|
logger.LogInformation("Config request: {configRequest}", JsonSerializer.Serialize(configRequest, new JsonSerializerOptions() { WriteIndented = true }));
|
|
|
|
HttpResponseMessage response = await client.PostAsJsonAsync("/api/1/vehicles/fleet_telemetry_config", configRequest);
|
|
|
|
return Results.Ok(response.Content.ReadAsStringAsync());
|
|
});
|
|
|
|
//Map static assets
|
|
app.MapStaticAssets();
|
|
|
|
//TODO: Build a middleware that responds with 503 if the public key is not registered at Tesla
|
|
|
|
app.MapRazorPages();
|
|
|
|
app.Run(); |