From 1e6c1d5ff3470ce497243bbaec37c475163e61b2 Mon Sep 17 00:00:00 2001 From: gak Date: Fri, 12 Apr 2024 08:44:16 +1000 Subject: [PATCH] fix: energy_left total_pack_energy` gone. Untagged handling --- CHANGELOG.md | 21 +++++++- src/energy_sites.rs | 3 -- src/lib.rs | 2 - src/products.rs | 47 ++++++++++++++++-- src/vehicles.rs | 3 -- testdata/products_gak_2024_04_12.json | 70 +++++++++++++++++++++++++++ 6 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 testdata/products_gak_2024_04_12.json diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd0757..6677d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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). +## [Unreleased] + +### 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 @@ -23,13 +32,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - go_off_grid_test_banner_enabled - powerwall_tesla_electric_interested_in - vpp_tour_enabled -- Components market_type is now Option +- Components market_type is now Option - 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 @@ -37,17 +46,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 @@ -58,16 +71,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 diff --git a/src/energy_sites.rs b/src/energy_sites.rs index 707011a..aef5510 100644 --- a/src/energy_sites.rs +++ b/src/energy_sites.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index 03d4740..5aacb78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,8 +97,6 @@ pub trait VehicleApi { ) -> Result; } -trait EnergySitesApi {} - trait ApiValues { fn format(&self, url: &str) -> String; } diff --git a/src/products.rs b/src/products.rs index ac6d954..53e4545 100644 --- a/src/products.rs +++ b/src/products.rs @@ -4,7 +4,8 @@ use crate::powerwall::PowerwallId; use crate::vehicles::VehicleData; use crate::{pub_get, OwnerApi}; use derive_more::Display; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use std::str::FromStr; #[rustfmt::skip] @@ -28,14 +29,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), Solar(Box), Powerwall(Box), } +fn deserialize_product<'de, D>(deserializer: D) -> Result +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(deserializer: D) -> Result + 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 { @@ -61,8 +92,6 @@ 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, @@ -239,4 +268,12 @@ mod tests { OwnerApi::parse_json::>(&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::>(&request_data, s.to_string(), PrintResponses::Pretty) + .unwrap(); + } } diff --git a/src/vehicles.rs b/src/vehicles.rs index 75a96c3..bbd93f6 100644 --- a/src/vehicles.rs +++ b/src/vehicles.rs @@ -457,9 +457,6 @@ pub struct GranularAccess { pub hide_private: bool, } -#[derive(Debug, Deserialize)] -pub struct Vehicles(Vec); - #[derive(Debug, Deserialize)] pub struct Vehicle { pub id: VehicleId, diff --git a/testdata/products_gak_2024_04_12.json b/testdata/products_gak_2024_04_12.json new file mode 100644 index 0000000..1799237 --- /dev/null +++ b/testdata/products_gak_2024_04_12.json @@ -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": "" + } + ] +}