Compare commits

..

32 commits

Author SHA1 Message Date
Alex Janka ef26ce4697 charge state: complete 2024-05-24 19:42:48 +10:00
Alex Janka fd1794744b cop&hvac enums 2024-05-24 19:42:48 +10:00
Alex Janka 3add4893f9 "parked" is a stupid name 2024-05-24 19:42:48 +10:00
Alex Janka b13c40429f shift state enum 2024-05-24 19:42:48 +10:00
Alex Janka 64aa35a969 charging state as enum 2024-05-24 19:42:48 +10:00
Alex Janka a2bc59fb34 fix speed type 2024-05-24 19:42:48 +10:00
Alex Janka 9ecf21554e derive Copy on VehicleId 2024-05-24 19:42:47 +10:00
Alex Janka 677d959e6d get rid of this one warning 2024-05-24 19:42:47 +10:00
Alex Janka 78d347f38e reverse proxied api url 2024-05-24 19:42:47 +10:00
Alex Janka 3123cad1a5 my changes 2024-05-24 19:42:44 +10:00
gak 13f4be3478
chore: v0.1.15 2024-04-12 11:14:32 +10:00
gak e326d17c14
chore: changelog 2024-04-12 11:14:20 +10:00
gak 6f7fb72943
fix: another invalid total_pack_energy 2024-04-12 11:10:15 +10:00
gak 62b3343b21
chore: v0.1.14 2024-04-12 08:53:29 +10:00
gak 19ac4b485d
chore: changelog release 2024-04-12 08:52:41 +10:00
gak 1e6c1d5ff3
fix: energy_left total_pack_energy` gone. Untagged handling 2024-04-12 08:49:20 +10:00
gak fcc9e63930
Merge pull request #18 from gak/dependabot/cargo/miette-7.2.0
chore(deps): update miette requirement from 5.10.0 to 7.2.0
2024-04-12 08:49:06 +10:00
gak afd833f13f
Merge pull request #23 from gak/dependabot/cargo/reqwest-0.12.3
chore(deps): update reqwest requirement from 0.11.23 to 0.12.3
2024-04-12 08:48:49 +10:00
gak 6723c0ad02
Merge pull request #24 from gak/dependabot/cargo/colored_json-5.0.0
chore(deps): update colored_json requirement from 4.1.0 to 5.0.0
2024-04-12 08:48:40 +10:00
dependabot[bot] 8b5141376e
chore(deps): update colored_json requirement from 4.1.0 to 5.0.0
Updates the requirements on [colored_json](https://github.com/ctron/colored_json) to permit the latest version.
- [Release notes](https://github.com/ctron/colored_json/releases)
- [Commits](https://github.com/ctron/colored_json/compare/v4.1.0...v5.0.0)

---
updated-dependencies:
- dependency-name: colored_json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-08 08:42:32 +00:00
dependabot[bot] 0de5ee26b9
chore(deps): update reqwest requirement from 0.11.23 to 0.12.3
Updates the requirements on [reqwest](https://github.com/seanmonstar/reqwest) to permit the latest version.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.23...v0.12.3)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-08 08:42:13 +00:00
dependabot[bot] 906ce9cf04
chore(deps): update miette requirement from 5.10.0 to 7.2.0
Updates the requirements on [miette](https://github.com/zkat/miette) to permit the latest version.
- [Release notes](https://github.com/zkat/miette/releases)
- [Changelog](https://github.com/zkat/miette/blob/main/CHANGELOG.md)
- [Commits](https://github.com/zkat/miette/compare/miette-derive-v5.10.0...miette-derive-v7.2.0)

---
updated-dependencies:
- dependency-name: miette
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-08 08:55:59 +00:00
gak b7e2f94884
Merge pull request #17 from gak/dependabot/cargo/rustls-0.23.0
chore(deps): update rustls requirement from 0.22.2 to 0.23.0
2024-03-01 21:52:58 +11:00
dependabot[bot] 63015deb1b
chore(deps): update rustls requirement from 0.22.2 to 0.23.0
Updates the requirements on [rustls](https://github.com/rustls/rustls) to permit the latest version.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.22.2...v/0.23.0)

---
updated-dependencies:
- dependency-name: rustls
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 08:53:16 +00:00
gak 325b899504
Merge pull request #14 from gak/dependabot/cargo/strum-0.26.1
chore(deps): update strum requirement from 0.25.0 to 0.26.1
2024-01-29 21:47:37 +11:00
dependabot[bot] a16ca64151
chore(deps): update strum requirement from 0.25.0 to 0.26.1
---
updated-dependencies:
- dependency-name: strum
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-29 08:16:42 +00:00
gak 4ba1dd2c33
chore: v0.1.13 2024-01-24 10:26:40 +11:00
gak 796c09c17a
fix: remove vehicles list (#13) 2024-01-24 10:25:41 +11:00
gak c8b45d5c3e
chore: v0.1.12 2024-01-20 19:46:12 +11:00
gak 280f5751cc
chore: more deps 2024-01-20 19:42:17 +11:00
gak c2b12df27b
chore: dep upgrades 2024-01-20 19:42:13 +11:00
gak 85e3728a1d
fix: powerwall api new fields 2024-01-20 19:40:35 +11:00
12 changed files with 335 additions and 114 deletions

View file

@ -5,10 +5,46 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.15] - 2023-04-12
### Fixed
- Forgot a `total_pack_energy`. (#22)
## [0.1.14] - 2023-04-12
### Changed
- `energy_left` and `total_pack_energy` are gone from the API. (#22)
- Don't rely on serde untagged to determine the product type, as any missing
fields will give an unspecified error. Instead directly check known fields
that will probably not be removed. https://github.com/serde-rs/serde/pull/2376
## [0.1.13] - 2023-01-24
### Changed
- Vehicles list is gone from the API. Use `products` instead. (#13)
## [0.1.12] - 2023-01-20
### Changed
- PowerwallData new fields:
- powerwall_onboarding_settings_set
- storm_mode_enabled
- features
- warp_site_number
- go_off_grid_test_banner_enabled
- powerwall_tesla_electric_interested_in
- vpp_tour_enabled
- Components market_type is now Option<String>
- LiveWallConnector wall_connector_power is now f32
## [0.1.11] - 2023-11-11
### Changed
- `vehicle_data` now accepts `GetVehicleData` instead of `VehicleId`. (See [6facc27](https://github.com/gak/teslatte/commit/6facc27d8b408d35b98b4c6c0ad3e5df82328d2c))
## [0.1.10] - 2023-11-11
@ -16,17 +52,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- API changes for "api_version 67"
- VehicleData new fields:
- cached_data
- command_signing
- release_notes_supported
- ClimateState new fields:
- auto_steering_wheel_heat
- cop_activation_temperature,
- steering_wheel_heat_level
- DriveState now Optional:
- gps_as_of
- heading
- latitude
@ -37,16 +77,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- native_type
- DriveState new fields:
- active_route_traffic_minutes_delay
- GuiSettings new field:
- gui_tirepressure_units
- VehicleConfig new fields:
- cop_user_set_temp_supported
- webcam_selfie_supported
- VehicleState new fields:
- media_info: MediaInfo
- tpms_hard_warning_fl
- tpms_hard_warning_fr

View file

@ -1,6 +1,6 @@
[package]
name = "teslatte"
version = "0.1.11"
version = "0.1.15"
description = "A command line tool and Rust crate for querying the Tesla API."
edition = "2021"
rust-version = "1.75.0"
@ -20,25 +20,25 @@ path = "src/main.rs"
required-features = ["cli"]
[dependencies]
miette = { version = "5.10.0", features = ["fancy"] }
thiserror = "1.0.50"
tokio = { version = "1.33.0", features = ["full"] }
miette = { version = "7.2.0", features = ["fancy"] }
thiserror = "1.0.56"
tokio = { version = "1.35.1", features = ["full"] }
tracing = "0.1.40"
reqwest = { version = "0.11.22", features = ["rustls-tls", "cookies", "json"] }
url = "2.4.1"
serde = { version = "1.0.189", features = ["derive"] }
serde_json = "1.0.107"
rustls = "0.22.1"
reqwest = { version = "0.12.3", features = ["rustls-tls", "cookies", "json"] }
url = "2.5.0"
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
rustls = "0.23.0"
rand = "0.8.5"
chrono = { version = "0.4.31", features = ["serde"] }
strum = { version = "0.25.0", features = ["derive"] }
strum = { version = "0.26.1", features = ["derive"] }
urlencoding = "2.1.3"
derive_more = "0.99.17"
pkce = "0.2.0"
clap = { version = "4.4.6", features = ["derive", "env"], optional = true }
tracing-subscriber = { version = "0.3.17", optional = true }
colored_json = { version = "4.0.0", optional = true }
clap = { version = "4.4.18", features = ["derive", "env"], optional = true }
tracing-subscriber = { version = "0.3.18", optional = true }
colored_json = { version = "5.0.0", optional = true }
[dev-dependencies]
test-log = { version = "0.2.13", default-features = false, features = ["trace"] }
test-log = { version = "0.2.14", default-features = false, features = ["trace"] }

View file

@ -1,8 +1,7 @@
use std::env;
use teslatte::auth::AccessToken;
use teslatte::products::Product;
use teslatte::vehicles::GetVehicleData;
use teslatte::{OwnerApi, VehicleApi};
use teslatte::OwnerApi;
#[tokio::main]
async fn main() {
@ -17,19 +16,8 @@ async fn main() {
}
};
let vehicles = api.vehicles().await.unwrap();
dbg!(&*vehicles);
if !vehicles.is_empty() {
let get_vehicle_data = GetVehicleData::new(vehicles[0].id.clone());
let vehicle_data = api.vehicle_data(&get_vehicle_data).await.unwrap();
dbg!(&vehicle_data);
} else {
println!("No vehicles found!");
}
let products = api.products().await.unwrap();
dbg!(&*products);
dbg!(&products);
if !products.is_empty() {
for product in &*products {

View file

@ -7,9 +7,8 @@ no_token_test:
# Require an access token from "cli.json". Use `just auth` to generate.
token_tests:
cargo run -- api vehicles
cargo run --no-default-features --features cli -- api vehicles
cargo run -- api products
cargo run --no-default-features --features cli -- api products
publish version:
git diff-index --quiet HEAD
@ -30,3 +29,7 @@ auth:
audit:
cargo audit
update:
cargo update && cargo upgrade
cd tesla_api_coverage && cargo update && cargo upgrade

View file

@ -18,7 +18,6 @@ pub struct SiteStatus {
pub battery_power: i64,
pub battery_type: String,
pub breaker_alert_enabled: bool,
pub energy_left: f64,
pub gateway_id: String,
pub percentage_charged: f64,
pub powerwall_onboarding_settings_set: bool,
@ -29,14 +28,12 @@ pub struct SiteStatus {
pub site_name: String,
pub storm_mode_enabled: bool,
pub sync_grid_alert_enabled: bool,
pub total_pack_energy: i64,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub struct LiveStatus {
pub backup_capable: bool,
pub battery_power: i64,
pub energy_left: f64,
pub generator_power: i64,
pub grid_power: i64,
pub grid_services_active: bool,
@ -48,7 +45,6 @@ pub struct LiveStatus {
pub solar_power: i64,
pub storm_mode_active: bool,
pub timestamp: String,
pub total_pack_energy: i64,
pub wall_connectors: Vec<LiveWallConnector>,
}
@ -69,7 +65,7 @@ pub struct LiveWallConnector {
/// 2: not plugged in
/// 4: plugged in (not charging)
pub wall_connector_state: i64,
pub wall_connector_power: i64,
pub wall_connector_power: f32,
pub wall_connector_fault_state: i64,
}
@ -311,7 +307,7 @@ mod tests {
vin: Some("1234".to_string()),
din: "5432".to_string(),
wall_connector_state: 4,
wall_connector_power: 0,
wall_connector_power: 0.,
wall_connector_fault_state: 2,
}
);
@ -336,7 +332,7 @@ mod tests {
vin: None,
din: "1234".to_string(),
wall_connector_state: 2,
wall_connector_power: 0,
wall_connector_power: 0.,
wall_connector_fault_state: 2,
}
);

View file

@ -32,7 +32,6 @@ const API_URL: &str = if cfg!(debug_assertions) {
};
pub trait VehicleApi {
async fn vehicles(&self) -> Result<Vec<Vehicle>, TeslatteError>;
async fn vehicle_data(
&self,
get_vehicle_data: &GetVehicleData,
@ -157,8 +156,6 @@ pub trait FleetVehicleApi {
async fn remote_start_drive(&self, vin: &str) -> Result<PostResponse, TeslatteError>;
}
trait EnergySitesApi {}
trait ApiValues {
fn format(&self, url: &str) -> String;
}
@ -502,6 +499,7 @@ struct ResponseError {
struct Empty {}
/// GET /api/1/[url]
#[allow(unused_macros)]
macro_rules! get {
($name:ident, $return_type:ty, $url:expr) => {
async fn $name(&self) -> Result<$return_type, crate::error::TeslatteError> {
@ -512,6 +510,7 @@ macro_rules! get {
}
};
}
#[allow(unused_imports)]
pub(crate) use get;
/// Same as get, but public.

View file

@ -4,7 +4,7 @@ use teslatte::auth::{AccessToken, RefreshToken};
use teslatte::cli::energy::EnergySiteArgs;
use teslatte::cli::powerwall::PowerwallArgs;
use teslatte::cli::vehicle::VehicleArgs;
use teslatte::{OwnerApi, PrintResponses, VehicleApi};
use teslatte::{OwnerApi, PrintResponses};
/// Teslatte
///
@ -106,7 +106,7 @@ async fn main() -> miette::Result<()> {
api.print_responses = PrintResponses::Pretty;
match api_args.command {
ApiCommand::Vehicles => {
api.vehicles().await?;
panic!("Tesla API has changed. This command is no longer works with the Owners API.");
}
ApiCommand::Vehicle(v) => {
v.run(&api).await?;

View file

@ -4,7 +4,8 @@ use crate::powerwall::PowerwallId;
use crate::vehicles::VehicleData;
use crate::{pub_get, FleetApi, OwnerApi};
use derive_more::Display;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::str::FromStr;
#[rustfmt::skip]
@ -33,14 +34,44 @@ impl FromStr for EnergySiteId {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GatewayId(String);
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
#[derive(Debug, Clone)]
pub enum Product {
Vehicle(Box<VehicleData>),
Solar(Box<SolarData>),
Powerwall(Box<PowerwallData>),
}
fn deserialize_product<'de, D>(deserializer: D) -> Result<Product, D::Error>
where
D: Deserializer<'de>,
{
let v = Value::deserialize(deserializer)?;
if v.get("vehicle_id").is_some() {
let vehicle_data = VehicleData::deserialize(v).map_err(serde::de::Error::custom)?;
Ok(Product::Vehicle(Box::new(vehicle_data)))
} else if v.get("solar_type").is_some() {
let solar_data = SolarData::deserialize(v).map_err(serde::de::Error::custom)?;
Ok(Product::Solar(Box::new(solar_data)))
} else if v.get("battery_type").is_some() {
let powerwall_data = PowerwallData::deserialize(v).map_err(serde::de::Error::custom)?;
Ok(Product::Powerwall(Box::new(powerwall_data)))
} else {
Err(serde::de::Error::custom(
"No valid key found to determine the product type",
))
}
}
impl<'de> Deserialize<'de> for Product {
fn deserialize<D>(deserializer: D) -> Result<Product, D::Error>
where
D: Deserializer<'de>,
{
deserialize_product(deserializer)
}
}
/// This is assumed from https://tesla-api.timdorr.com/api-basics/products
#[derive(Debug, Clone, Deserialize)]
pub struct SolarData {
@ -66,14 +97,25 @@ pub struct PowerwallData {
pub id: PowerwallId,
pub gateway_id: GatewayId,
pub asset_site_id: String,
pub energy_left: f64,
pub total_pack_energy: i64,
pub percentage_charged: f64,
pub backup_capable: bool,
pub battery_power: i64,
pub sync_grid_alert_enabled: bool,
pub breaker_alert_enabled: bool,
pub components: Components,
// New fields as of 2024-01-20
pub powerwall_onboarding_settings_set: bool,
pub storm_mode_enabled: bool,
pub features: PowerwallFeatures,
pub warp_site_number: String,
pub go_off_grid_test_banner_enabled: Option<bool>,
pub powerwall_tesla_electric_interested_in: Option<bool>,
pub vpp_tour_enabled: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PowerwallFeatures {
pub rate_plan_manager_no_pricing_constraint: bool,
}
#[derive(Debug, Clone, Deserialize)]
@ -84,7 +126,7 @@ pub struct Components {
pub solar_type: Option<String>,
pub grid: bool,
pub load_meter: bool,
pub market_type: String,
pub market_type: Option<String>,
#[serde(default)]
pub wall_connectors: Vec<WallConnector>,
}
@ -93,60 +135,9 @@ pub struct Components {
mod tests {
use super::*;
use crate::energy_sites::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
use crate::ApiValues;
use crate::{ApiValues, PrintResponses, RequestData};
use chrono::DateTime;
#[test]
fn energy_match_powerwall() {
let json = r#"
{
"energy_site_id": 1032748243,
"resource_type": "battery",
"site_name": "1 Railway Pde",
"id": "ABC2010-1234",
"gateway_id": "3287423824-QWE",
"asset_site_id": "123ecd-123ecd-12345-12345",
"energy_left": 4394.000000000001,
"total_pack_energy": 13494,
"percentage_charged": 32.562620423892106,
"battery_type": "ac_powerwall",
"backup_capable": true,
"battery_power": -280,
"sync_grid_alert_enabled": true,
"breaker_alert_enabled": false,
"components": {
"battery": true,
"battery_type": "ac_powerwall",
"solar": true,
"solar_type": "pv_panel",
"grid": true,
"load_meter": true,
"market_type": "residential"
}
}
"#;
if let Product::Powerwall(data) = serde_json::from_str(json).unwrap() {
assert_eq!(data.battery_type, "ac_powerwall");
assert!(data.backup_capable);
assert_eq!(data.battery_power, -280);
assert!(data.sync_grid_alert_enabled);
assert!(!data.breaker_alert_enabled);
assert!(data.components.battery);
assert_eq!(
data.components.battery_type,
Some("ac_powerwall".to_string())
);
assert!(data.components.solar);
assert_eq!(data.components.solar_type, Some("pv_panel".to_string()));
assert!(data.components.grid);
assert!(data.components.load_meter);
assert_eq!(data.components.market_type, "residential");
} else {
panic!("Expected PowerwallData");
}
}
#[test]
fn energy_match_vehicle() {
let json = r#"
@ -274,4 +265,20 @@ mod tests {
"https://base.com/e/123/history?period=month&kind=energy&start_date=2020-01-01T00:00:00Z&end_date=2020-01-31T23:59:59Z"
);
}
#[test]
fn json_products_gak_2024_01_20() {
let s = include_str!("../testdata/products_gak_2024_01_20.json");
let request_data = RequestData::Get { url: "" };
OwnerApi::parse_json::<Vec<Product>>(&request_data, s.to_string(), PrintResponses::Pretty)
.unwrap();
}
#[test]
fn json_products_gak_2024_04_12() {
let s = include_str!("../testdata/products_gak_2024_04_12.json");
let request_data = RequestData::Get { url: "" };
OwnerApi::parse_json::<Vec<Product>>(&request_data, s.to_string(), PrintResponses::Pretty)
.unwrap();
}
}

View file

@ -12,7 +12,6 @@ use strum::{Display, EnumString};
#[rustfmt::skip]
impl VehicleApi for OwnerApi {
get!(vehicles, Vec<Vehicle>, "/vehicles");
get_args!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", GetVehicleData);
post_arg_empty!(wake_up, "/vehicles/{}/command/wake_up", VehicleId);
@ -547,9 +546,6 @@ pub struct GranularAccess {
}
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
pub struct Vehicles(Vec<Vehicle>);
#[derive(Debug, Deserialize)]
pub struct Vehicle {
pub id: VehicleId,

View file

@ -4,18 +4,18 @@ version = "0.1.0"
edition = "2021"
[dependencies]
reqwest = "0.11.22"
tokio = { version = "1.33.0", features = ["full"] }
clap = { version = "4.4.6", features = ["derive"] }
scraper = "0.17.1"
reqwest = "0.11.23"
tokio = { version = "1.35.1", features = ["full"] }
clap = { version = "4.4.18", features = ["derive"] }
scraper = "0.18.1"
strum = { version = "0.25.0", features = ["derive"] }
tracing-subscriber = "0.3.17"
tracing-subscriber = "0.3.18"
tracing = "0.1.40"
log = "0.4.20"
nom = "7.1.3"
anyhow = "1.0.75"
serde = { version = "1.0.189", features = ["derive"] }
serde_json = "1.0.107"
anyhow = "1.0.79"
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
heck = "0.5.0-rc.1"
glob = "0.3.1"
syn = { version = "2.0.38" , features = ["full", "extra-traits"] }
syn = { version = "2.0.48" , features = ["full", "extra-traits"] }

118
testdata/products_gak_2024_01_20.json vendored Normal file
View file

@ -0,0 +1,118 @@
{
"count": 2,
"response": [
{
"access_type": "OWNER",
"api_version": 71,
"backseat_token": null,
"backseat_token_updated_at": null,
"ble_autopair_enrolled": false,
"cached_data": "",
"calendar_enabled": true,
"color": null,
"command_signing": "required",
"display_name": "Kool Beans",
"granular_access": {
"hide_private": false
},
"id": 1,
"id_s": "1",
"in_service": false,
"option_codes": null,
"release_notes_supported": true,
"state": "online",
"tokens": [
],
"user_id": 2,
"vehicle_config": {
"aux_park_lamps": "Eu",
"badge_version": 0,
"can_accept_navigation_requests": true,
"can_actuate_trunks": true,
"car_special_type": "base",
"car_type": "model3",
"charge_port_type": "CCS",
"cop_user_set_temp_supported": false,
"dashcam_clip_save_supported": true,
"default_charge_to_max": false,
"driver_assist": "TeslaAP3",
"ece_restrictions": false,
"efficiency_package": "M32021",
"eu_vehicle": true,
"exterior_color": "MidnightSilver",
"exterior_trim": "Black",
"exterior_trim_override": "",
"has_air_suspension": false,
"has_ludicrous_mode": false,
"has_seat_cooling": false,
"headlamp_type": "Global",
"interior_trim_type": "Black2",
"key_version": 2,
"motorized_charge_port": true,
"paint_color_override": "24,24,27,0.9,0.3",
"performance_package": "Base",
"plg": true,
"pws": false,
"rear_drive_unit": "PM216MOSFET",
"rear_seat_heaters": 1,
"rear_seat_type": 0,
"rhd": true,
"roof_color": "RoofColorGlass",
"seat_type": null,
"spoiler_type": "None",
"sun_roof_installed": null,
"supports_qr_pairing": false,
"third_row_seats": "None",
"timestamp": 1705717676876,
"trim_badging": "74d",
"use_range_badging": true,
"utc_offset": 36000,
"webcam_selfie_supported": true,
"webcam_supported": true,
"wheel_type": "StilettoRefresh19"
},
"vehicle_id": 1,
"vin": "LR"
},
{
"asset_site_id": "",
"backup_capable": true,
"battery_power": 3520,
"battery_type": "ac_powerwall",
"breaker_alert_enabled": true,
"components": {
"battery": true,
"battery_type": "ac_powerwall",
"grid": true,
"load_meter": true,
"solar": true,
"solar_type": "pv_panel",
"wall_connectors": [
{
"device_id": "",
"din": "",
"is_active": true
}
]
},
"energy_left": 13136.26315789474,
"energy_site_id": 247079748146,
"features": {
"rate_plan_manager_no_pricing_constraint": true
},
"gateway_id": "a",
"go_off_grid_test_banner_enabled": null,
"id": "1234",
"percentage_charged": 31.52375310862408,
"powerwall_onboarding_settings_set": true,
"powerwall_tesla_electric_interested_in": null,
"resource_type": "battery",
"site_name": "",
"storm_mode_enabled": true,
"sync_grid_alert_enabled": true,
"total_pack_energy": 41671,
"vpp_tour_enabled": null,
"warp_site_number": "STE00"
}
]
}

70
testdata/products_gak_2024_04_12.json vendored Normal file
View file

@ -0,0 +1,70 @@
{
"count": 2,
"response": [
{
"access_type": "OWNER",
"api_version": 73,
"backseat_token": null,
"backseat_token_updated_at": null,
"ble_autopair_enrolled": false,
"cached_data": "",
"calendar_enabled": true,
"color": null,
"command_signing": "required",
"display_name": "Kool Beans",
"granular_access": {
"hide_private": false
},
"id": 123,
"id_s": "123",
"in_service": false,
"option_codes": null,
"release_notes_supported": true,
"state": "online",
"tokens": [],
"user_id": 123,
"vehicle_id": 123,
"vin": "LRW"
},
{
"asset_site_id": "qwfp",
"backup_capable": true,
"battery_power": -1590,
"battery_type": "ac_powerwall",
"breaker_alert_enabled": true,
"components": {
"battery": true,
"battery_type": "ac_powerwall",
"grid": true,
"load_meter": true,
"market_type": "residential",
"solar": true,
"solar_type": "pv_panel",
"wall_connectors": [
{
"device_id": "qwfp",
"din": "qwfp",
"is_active": true,
"part_number": "1529455-02-D"
}
]
},
"energy_site_id": 1234,
"features": {
"rate_plan_manager_no_pricing_constraint": true
},
"gateway_id": "qwfp",
"go_off_grid_test_banner_enabled": null,
"id": "qwfp",
"percentage_charged": 48.872760297620715,
"powerwall_onboarding_settings_set": true,
"powerwall_tesla_electric_interested_in": null,
"resource_type": "battery",
"site_name": "",
"storm_mode_enabled": true,
"sync_grid_alert_enabled": true,
"vpp_tour_enabled": null,
"warp_site_number": ""
}
]
}