All checks were successful
Build, Push and Run Container / build (push) Successful in 24s
422 lines
24 KiB
C#
422 lines
24 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<Configurator> logger, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
string access_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InFEc3NoM2FTV0cyT05YTTdLMzFWV0VVRW5BNCJ9.eyJpc3MiOiJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My9udHMiLCJhenAiOiJiMjI0MGVlNC0zMzJhLTQyNTItOTFhYS1iYmNjMjRmNzhmZGIiLCJzdWIiOiJkZDg3Mzc4OC00ZjliLTQyY2UtYmRkNi00YzdmMjQxOGMwN2UiLCJhdWQiOlsiaHR0cHM6Ly9mbGVldC1hcGkucHJkLm5hLnZuLmNsb3VkLnRlc2xhLmNvbSIsImh0dHBzOi8vZmxlZXQtYXBpLnByZC5ldS52bi5jbG91ZC50ZXNsYS5jb20iLCJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My91c2VyaW5mbyJdLCJzY3AiOlsib3BlbmlkIiwib2ZmbGluZV9hY2Nlc3MiLCJ2ZWhpY2xlX2RldmljZV9kYXRhIiwidmVoaWNsZV9sb2NhdGlvbiJdLCJhbXIiOlsicHdkIl0sImV4cCI6MTc1NTgwODkzNSwiaWF0IjoxNzU1NzgwMTM1LCJvdV9jb2RlIjoiRVUiLCJsb2NhbGUiOiJodS1IVSIsImFjY291bnRfdHlwZSI6InBlcnNvbiIsIm9wZW5fc291cmNlIjpmYWxzZSwiYWNjb3VudF9pZCI6IjE5YTBhZjRmLTY1ZDgtNDc2MC1hYjVmLTZjMzk3ZTViMTI4ZiIsImF1dGhfdGltZSI6MTc1NTc4MDEzNCwibm9uY2UiOiI2Mzg5MTM3NjkyNDEzMDI0MjMuTmpBNE0yWmpOalV0Wmpkak9DMDBabVF6TFdFeVlqa3RaR05rWVRKa01HSTRZMll6TnpKa09HSTNPV0V0Wmprd055MDBPREZpTFdGbE1UQXRNbVV4WlRnME1UZG1PV00xIn0.IAfZApY-P3HkRp4U2oO_T2DUFplbGfwuOfnXihcnlmiGKxKSSSuJ5aI76pcaDg9saxrhIhg17KjmEC4gL90ByDk6P7KUMp_xot0FN1Vtwy3C8_NDltebhZdM2emR5N7QHXdP4OYAQNvHwanRwBUeQthQ8pFUk9-fDzsZhwkTjrGYtvpQKZK-pn5GCLIKLib4AemsidCtfOlObjgqTd6wf_Tdb2dkbt-ACNIueTcmfXt-eFUZVRySwrvOb5pOWAUkjTUCpW074ySJjj_TDYheQHA9aTZsDJWCUNHC-51qnawiUvh-LwYWasfFhQZQisSfSusCgpGvHRVsyuLbOtd2fQ";
|
|
|
|
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>() { vin },
|
|
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("/Status", async ([FromServices] ILogger<Configurator> logger, [FromServices] IHttpClientFactory httpClientFactory) =>
|
|
{
|
|
string access_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InFEc3NoM2FTV0cyT05YTTdLMzFWV0VVRW5BNCJ9.eyJpc3MiOiJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My9udHMiLCJhenAiOiJiMjI0MGVlNC0zMzJhLTQyNTItOTFhYS1iYmNjMjRmNzhmZGIiLCJzdWIiOiJkZDg3Mzc4OC00ZjliLTQyY2UtYmRkNi00YzdmMjQxOGMwN2UiLCJhdWQiOlsiaHR0cHM6Ly9mbGVldC1hcGkucHJkLm5hLnZuLmNsb3VkLnRlc2xhLmNvbSIsImh0dHBzOi8vZmxlZXQtYXBpLnByZC5ldS52bi5jbG91ZC50ZXNsYS5jb20iLCJodHRwczovL2ZsZWV0LWF1dGgudGVzbGEuY29tL29hdXRoMi92My91c2VyaW5mbyJdLCJzY3AiOlsib3BlbmlkIiwib2ZmbGluZV9hY2Nlc3MiLCJ2ZWhpY2xlX2RldmljZV9kYXRhIiwidmVoaWNsZV9sb2NhdGlvbiJdLCJhbXIiOlsicHdkIl0sImV4cCI6MTc1NTgwODkzNSwiaWF0IjoxNzU1NzgwMTM1LCJvdV9jb2RlIjoiRVUiLCJsb2NhbGUiOiJodS1IVSIsImFjY291bnRfdHlwZSI6InBlcnNvbiIsIm9wZW5fc291cmNlIjpmYWxzZSwiYWNjb3VudF9pZCI6IjE5YTBhZjRmLTY1ZDgtNDc2MC1hYjVmLTZjMzk3ZTViMTI4ZiIsImF1dGhfdGltZSI6MTc1NTc4MDEzNCwibm9uY2UiOiI2Mzg5MTM3NjkyNDEzMDI0MjMuTmpBNE0yWmpOalV0Wmpkak9DMDBabVF6TFdFeVlqa3RaR05rWVRKa01HSTRZMll6TnpKa09HSTNPV0V0Wmprd055MDBPREZpTFdGbE1UQXRNbVV4WlRnME1UZG1PV00xIn0.IAfZApY-P3HkRp4U2oO_T2DUFplbGfwuOfnXihcnlmiGKxKSSSuJ5aI76pcaDg9saxrhIhg17KjmEC4gL90ByDk6P7KUMp_xot0FN1Vtwy3C8_NDltebhZdM2emR5N7QHXdP4OYAQNvHwanRwBUeQthQ8pFUk9-fDzsZhwkTjrGYtvpQKZK-pn5GCLIKLib4AemsidCtfOlObjgqTd6wf_Tdb2dkbt-ACNIueTcmfXt-eFUZVRySwrvOb5pOWAUkjTUCpW074ySJjj_TDYheQHA9aTZsDJWCUNHC-51qnawiUvh-LwYWasfFhQZQisSfSusCgpGvHRVsyuLbOtd2fQ";
|
|
|
|
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 requestObject = new { vins = new string[] { vin } };
|
|
HttpResponseMessage statusResponse = await client.PostAsJsonAsync("/api/1/vehicles/fleet_status", requestObject);
|
|
string statusResponseContent = await statusResponse.Content.ReadAsStringAsync();
|
|
logger.LogTrace("Fleet status response: {statusResponseContent}", statusResponseContent);
|
|
|
|
HttpResponseMessage rspmsg = await client.GetAsync($"GET /api/1/vehicles/{vin}/fleet_telemetry_config");
|
|
string rsp = await rspmsg.Content.ReadAsStringAsync();
|
|
logger.LogInformation("Telemetry config: {response}", rsp);
|
|
|
|
HttpResponseMessage responseMessage = await client.GetAsync($"/api/1/vehicles/{vin}/fleet_telemetry_errors");
|
|
string response = await responseMessage.Content.ReadAsStringAsync();
|
|
logger.LogInformation("Telemetry errors: {response}", response);
|
|
|
|
return Results.Ok(JsonSerializer.Serialize(new
|
|
{
|
|
FleetStatus = statusResponseContent,
|
|
TelemetryConfig = rsp,
|
|
TelemetryErrors = response
|
|
}));
|
|
});
|
|
|
|
//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(); |