feat: rename and add api calls (energy_sites)
- rename api/cli energy_sites to products (to match the api) - add energy_sites site_status - add energy_sites live_status - add energy_sites site_info
This commit is contained in:
parent
cc01e30c63
commit
a64a04e3d7
|
@ -1,5 +1,6 @@
|
|||
use std::env;
|
||||
use teslatte::auth::AccessToken;
|
||||
use teslatte::products::Product;
|
||||
use teslatte::Api;
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -16,12 +17,46 @@ async fn main() {
|
|||
};
|
||||
|
||||
let vehicles = api.vehicles().await.unwrap();
|
||||
dbg!(&vehicles);
|
||||
dbg!(&*vehicles);
|
||||
|
||||
if !vehicles.is_empty() {
|
||||
let vehicle_data = api.vehicle_data(&vehicles[0].id).await.unwrap();
|
||||
dbg!(vehicle_data);
|
||||
dbg!(&*vehicle_data);
|
||||
} else {
|
||||
println!("No vehicles found!");
|
||||
}
|
||||
|
||||
let products = api.products().await.unwrap();
|
||||
dbg!(&*products);
|
||||
|
||||
if !products.is_empty() {
|
||||
for product in &*products {
|
||||
match product {
|
||||
Product::Vehicle(v) => {
|
||||
dbg!(v);
|
||||
}
|
||||
|
||||
Product::Solar(e) => {
|
||||
let site_info = api.energy_sites_site_info(&e.energy_site_id).await.unwrap();
|
||||
dbg!(&*site_info);
|
||||
|
||||
let live_info = api
|
||||
.energy_sites_live_status(&e.energy_site_id)
|
||||
.await
|
||||
.unwrap();
|
||||
dbg!(&*live_info);
|
||||
}
|
||||
|
||||
Product::Powerwall(p) => {
|
||||
let live_info = api
|
||||
.energy_sites_live_status(&p.energy_site_id)
|
||||
.await
|
||||
.unwrap();
|
||||
dbg!(&*live_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("No products found!");
|
||||
}
|
||||
}
|
||||
|
|
2
justfile
2
justfile
|
@ -9,7 +9,7 @@ no_token_test:
|
|||
token_tests:
|
||||
cargo run -- api vehicles
|
||||
cargo run --no-default-features --features cli -- api vehicles
|
||||
cargo run -- api energy-sites
|
||||
cargo run -- api products
|
||||
|
||||
publish version:
|
||||
git diff-index --quiet HEAD
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
use crate::energy::EnergySiteId;
|
||||
use crate::Values;
|
||||
use crate::{get_args, join_query_pairs, rfc3339, Api};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::Deserialize;
|
||||
use strum::{Display, EnumString, IntoStaticStr};
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl Api {
|
||||
get_args!(energy_sites_calendar_history, CalendarHistory, "/energy_sites/{}/calendar_history", CalendarHistoryValues);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Display, EnumString, IntoStaticStr)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HistoryKind {
|
||||
Power,
|
||||
Energy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Display, EnumString, IntoStaticStr)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HistoryPeriod {
|
||||
Day,
|
||||
Month,
|
||||
Year,
|
||||
Lifetime,
|
||||
}
|
||||
|
||||
pub struct CalendarHistoryValues {
|
||||
// Modify URL:
|
||||
pub site_id: EnergySiteId,
|
||||
|
||||
// Query params:
|
||||
pub period: HistoryPeriod,
|
||||
pub kind: HistoryKind,
|
||||
pub start_date: Option<DateTime<FixedOffset>>,
|
||||
pub end_date: Option<DateTime<FixedOffset>>,
|
||||
}
|
||||
|
||||
impl Values for CalendarHistoryValues {
|
||||
fn format(&self, url: &str) -> String {
|
||||
let url = url.replace("{}", &format!("{}", self.site_id.0));
|
||||
let mut pairs: Vec<(&str, String)> = vec![
|
||||
("period", self.period.to_string()),
|
||||
("kind", self.kind.to_string()),
|
||||
];
|
||||
if let Some(start_date) = self.start_date {
|
||||
let start_date = rfc3339(&start_date);
|
||||
pairs.push(("start_date", start_date));
|
||||
}
|
||||
if let Some(end_date) = self.end_date {
|
||||
let end_date = rfc3339(&end_date);
|
||||
pairs.push(("end_date", end_date));
|
||||
}
|
||||
format!("{}?{}", url, join_query_pairs(&pairs))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CalendarHistory {
|
||||
pub serial_number: String,
|
||||
/// Only appears in energy kind.
|
||||
pub period: Option<String>,
|
||||
pub installation_time_zone: String,
|
||||
/// Optional because if there are no `Series` fields, this field is omitted.
|
||||
pub time_series: Option<Vec<Series>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Series {
|
||||
Power(PowerSeries),
|
||||
Energy(EnergySeries),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct PowerSeries {
|
||||
pub timestamp: DateTime<FixedOffset>,
|
||||
pub solar_power: f64,
|
||||
pub battery_power: f64,
|
||||
pub grid_power: f64,
|
||||
pub grid_services_power: f64,
|
||||
pub generator_power: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct EnergySeries {
|
||||
pub timestamp: DateTime<FixedOffset>,
|
||||
pub solar_energy_exported: f64,
|
||||
pub generator_energy_exported: f64,
|
||||
pub grid_energy_imported: f64,
|
||||
pub grid_services_energy_imported: f64,
|
||||
pub grid_services_energy_exported: f64,
|
||||
pub grid_energy_exported_from_solar: f64,
|
||||
pub grid_energy_exported_from_generator: f64,
|
||||
pub grid_energy_exported_from_battery: f64,
|
||||
pub battery_energy_exported: f64,
|
||||
pub battery_energy_imported_from_grid: f64,
|
||||
pub battery_energy_imported_from_solar: f64,
|
||||
pub battery_energy_imported_from_generator: f64,
|
||||
pub consumer_energy_imported_from_grid: f64,
|
||||
pub consumer_energy_imported_from_solar: f64,
|
||||
pub consumer_energy_imported_from_battery: f64,
|
||||
pub consumer_energy_imported_from_generator: f64,
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::calendar_history::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
|
||||
use crate::cli::print_json;
|
||||
use crate::energy::EnergySiteId;
|
||||
use crate::energy_sites::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
|
||||
use crate::products::EnergySiteId;
|
||||
use crate::Api;
|
||||
use chrono::DateTime;
|
||||
use clap::{Args, Subcommand};
|
||||
|
@ -8,6 +8,9 @@ use miette::{IntoDiagnostic, WrapErr};
|
|||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum EnergySiteCommand {
|
||||
SiteStatus,
|
||||
LiveStatus,
|
||||
SiteInfo,
|
||||
CalendarHistory(CalendarHistoryArgs),
|
||||
}
|
||||
|
||||
|
@ -22,6 +25,15 @@ pub struct EnergySiteArgs {
|
|||
impl EnergySiteArgs {
|
||||
pub async fn run(&self, api: &Api) -> miette::Result<()> {
|
||||
match &self.command {
|
||||
EnergySiteCommand::SiteStatus => {
|
||||
print_json(api.energy_sites_site_status(&self.id).await);
|
||||
}
|
||||
EnergySiteCommand::LiveStatus => {
|
||||
print_json(api.energy_sites_live_status(&self.id).await);
|
||||
}
|
||||
EnergySiteCommand::SiteInfo => {
|
||||
print_json(api.energy_sites_site_info(&self.id).await);
|
||||
}
|
||||
EnergySiteCommand::CalendarHistory(args) => {
|
||||
let start_date = args
|
||||
.start
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::calendar_history::{HistoryKind, HistoryPeriod};
|
||||
use crate::cli::print_json_data;
|
||||
use crate::energy_sites::{HistoryKind, HistoryPeriod};
|
||||
use crate::powerwall::{PowerwallEnergyHistoryValues, PowerwallId};
|
||||
use crate::Api;
|
||||
use clap::{Args, Subcommand};
|
||||
|
|
239
src/energy_sites.rs
Normal file
239
src/energy_sites.rs
Normal file
|
@ -0,0 +1,239 @@
|
|||
use crate::products::EnergySiteId;
|
||||
use crate::{get_arg, get_args, join_query_pairs, rfc3339, Api, Values};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::Deserialize;
|
||||
use strum::{Display, EnumString, IntoStaticStr};
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl Api {
|
||||
get_arg!(energy_sites_site_status, SiteStatus, "/energy_sites/{}/site_status", EnergySiteId);
|
||||
get_arg!(energy_sites_live_status, LiveStatus, "/energy_sites/{}/live_status", EnergySiteId);
|
||||
get_arg!(energy_sites_site_info, SiteInfo, "/energy_sites/{}/site_info", EnergySiteId);
|
||||
get_args!(energy_sites_calendar_history, CalendarHistory, "/energy_sites/{}/calendar_history", CalendarHistoryValues);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SiteStatus {
|
||||
pub backup_capable: bool,
|
||||
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,
|
||||
pub powerwall_tesla_electric_interested_in: Option<()>, // TODO: Unknown type. Was null.
|
||||
pub resource_type: String, // battery
|
||||
pub site_name: String,
|
||||
pub storm_mode_enabled: bool,
|
||||
pub sync_grid_alert_enabled: bool,
|
||||
pub total_pack_energy: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
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,
|
||||
pub grid_services_power: i64,
|
||||
pub grid_status: String,
|
||||
pub island_status: String,
|
||||
pub load_power: i64,
|
||||
pub percentage_charged: f64,
|
||||
pub solar_power: i64,
|
||||
pub storm_mode_active: bool,
|
||||
pub timestamp: String,
|
||||
pub total_pack_energy: i64,
|
||||
pub wall_connectors: Vec<()>, // TODO: gak: This is empty so I don't know what it looks like.
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UserSettings {
|
||||
pub breaker_alert_enabled: bool,
|
||||
pub powerwall_onboarding_settings_set: bool,
|
||||
pub powerwall_tesla_electric_interested_in: bool,
|
||||
pub storm_mode_enabled: bool,
|
||||
pub sync_grid_alert_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Schedule {
|
||||
pub end_seconds: i64,
|
||||
pub start_seconds: i64,
|
||||
pub target: String,
|
||||
pub week_days: Vec<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct TouSettings {
|
||||
pub optimization_strategy: String,
|
||||
pub schedule: Vec<Schedule>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Geolocation {
|
||||
pub latitude: f64,
|
||||
pub longitude: f64,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Components {
|
||||
pub backup: bool,
|
||||
pub backup_time_remaining_enabled: bool,
|
||||
pub battery: bool,
|
||||
pub battery_solar_offset_view_enabled: bool,
|
||||
pub battery_type: String,
|
||||
pub car_charging_data_supported: bool,
|
||||
pub configurable: bool,
|
||||
pub edit_setting_energy_exports: bool,
|
||||
pub edit_setting_grid_charging: bool,
|
||||
pub edit_setting_permission_to_export: bool,
|
||||
pub energy_service_self_scheduling_enabled: bool,
|
||||
pub energy_value_header: String,
|
||||
pub energy_value_subheader: String,
|
||||
pub flex_energy_request_capable: bool,
|
||||
pub gateway: String,
|
||||
pub grid: bool,
|
||||
pub grid_services_enabled: bool,
|
||||
pub load_meter: bool,
|
||||
pub off_grid_vehicle_charging_reserve_supported: bool,
|
||||
pub set_islanding_mode_enabled: bool,
|
||||
pub show_grid_import_battery_source_cards: bool,
|
||||
pub solar: bool,
|
||||
pub solar_type: String,
|
||||
pub solar_value_enabled: bool,
|
||||
pub storm_mode_capable: bool,
|
||||
pub tou_capable: bool,
|
||||
pub vehicle_charging_performance_view_enabled: bool,
|
||||
pub vehicle_charging_solar_offset_view_enabled: bool,
|
||||
pub wifi_commissioning_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Address {
|
||||
pub address_line1: String,
|
||||
pub city: String,
|
||||
pub country: String,
|
||||
pub state: String,
|
||||
pub zip: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct SiteInfo {
|
||||
pub address: Address,
|
||||
pub backup_reserve_percent: i64,
|
||||
pub battery_count: i64,
|
||||
pub components: Components,
|
||||
pub default_real_mode: String,
|
||||
pub geolocation: Geolocation,
|
||||
pub id: String,
|
||||
pub installation_date: String,
|
||||
pub installation_time_zone: String,
|
||||
pub max_site_meter_power_ac: i64,
|
||||
pub min_site_meter_power_ac: i64,
|
||||
pub nameplate_energy: i64,
|
||||
pub nameplate_power: i64,
|
||||
pub site_name: String,
|
||||
pub tou_settings: TouSettings,
|
||||
pub user_settings: UserSettings,
|
||||
pub version: String,
|
||||
pub vpp_backup_reserve_percent: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Display, EnumString, IntoStaticStr)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HistoryKind {
|
||||
Power,
|
||||
Energy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Display, EnumString, IntoStaticStr)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HistoryPeriod {
|
||||
Day,
|
||||
Month,
|
||||
Year,
|
||||
Lifetime,
|
||||
}
|
||||
|
||||
pub struct CalendarHistoryValues {
|
||||
// Modify URL:
|
||||
pub site_id: EnergySiteId,
|
||||
|
||||
// Query params:
|
||||
pub period: HistoryPeriod,
|
||||
pub kind: HistoryKind,
|
||||
pub start_date: Option<DateTime<FixedOffset>>,
|
||||
pub end_date: Option<DateTime<FixedOffset>>,
|
||||
}
|
||||
|
||||
impl Values for CalendarHistoryValues {
|
||||
fn format(&self, url: &str) -> String {
|
||||
let url = url.replace("{}", &format!("{}", self.site_id.0));
|
||||
let mut pairs: Vec<(&str, String)> = vec![
|
||||
("period", self.period.to_string()),
|
||||
("kind", self.kind.to_string()),
|
||||
];
|
||||
if let Some(start_date) = self.start_date {
|
||||
let start_date = rfc3339(&start_date);
|
||||
pairs.push(("start_date", start_date));
|
||||
}
|
||||
if let Some(end_date) = self.end_date {
|
||||
let end_date = rfc3339(&end_date);
|
||||
pairs.push(("end_date", end_date));
|
||||
}
|
||||
format!("{}?{}", url, join_query_pairs(&pairs))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CalendarHistory {
|
||||
pub serial_number: String,
|
||||
/// Only appears in energy kind.
|
||||
pub period: Option<String>,
|
||||
pub installation_time_zone: String,
|
||||
/// Optional because if there are no `Series` fields, this field is omitted.
|
||||
pub time_series: Option<Vec<Series>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Series {
|
||||
Power(PowerSeries),
|
||||
Energy(EnergySeries),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct PowerSeries {
|
||||
pub timestamp: DateTime<FixedOffset>,
|
||||
pub solar_power: f64,
|
||||
pub battery_power: f64,
|
||||
pub grid_power: f64,
|
||||
pub grid_services_power: f64,
|
||||
pub generator_power: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct EnergySeries {
|
||||
pub timestamp: DateTime<FixedOffset>,
|
||||
pub solar_energy_exported: f64,
|
||||
pub generator_energy_exported: f64,
|
||||
pub grid_energy_imported: f64,
|
||||
pub grid_services_energy_imported: f64,
|
||||
pub grid_services_energy_exported: f64,
|
||||
pub grid_energy_exported_from_solar: f64,
|
||||
pub grid_energy_exported_from_generator: f64,
|
||||
pub grid_energy_exported_from_battery: f64,
|
||||
pub battery_energy_exported: f64,
|
||||
pub battery_energy_imported_from_grid: f64,
|
||||
pub battery_energy_imported_from_solar: f64,
|
||||
pub battery_energy_imported_from_generator: f64,
|
||||
pub consumer_energy_imported_from_grid: f64,
|
||||
pub consumer_energy_imported_from_solar: f64,
|
||||
pub consumer_energy_imported_from_battery: f64,
|
||||
pub consumer_energy_imported_from_generator: f64,
|
||||
}
|
|
@ -8,10 +8,10 @@ use std::fmt::{Debug, Display};
|
|||
use tracing::debug;
|
||||
|
||||
pub mod auth;
|
||||
pub mod calendar_history;
|
||||
pub mod energy;
|
||||
pub mod energy_sites;
|
||||
pub mod error;
|
||||
pub mod powerwall;
|
||||
pub mod products;
|
||||
pub mod vehicles;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
|
|
|
@ -59,7 +59,7 @@ enum ApiCommand {
|
|||
Vehicle(VehicleArgs),
|
||||
|
||||
/// List of energy sites.
|
||||
EnergySites,
|
||||
Products,
|
||||
|
||||
/// Specific energy site.
|
||||
EnergySite(EnergySiteArgs),
|
||||
|
@ -111,8 +111,8 @@ async fn main() -> miette::Result<()> {
|
|||
ApiCommand::Vehicle(v) => {
|
||||
v.run(&api).await?;
|
||||
}
|
||||
ApiCommand::EnergySites => {
|
||||
print_json(api.energy_sites().await);
|
||||
ApiCommand::Products => {
|
||||
print_json(api.products().await);
|
||||
}
|
||||
ApiCommand::EnergySite(e) => {
|
||||
e.run(&api).await?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::calendar_history::{HistoryKind, HistoryPeriod};
|
||||
use crate::energy::GatewayId;
|
||||
use crate::energy_sites::{HistoryKind, HistoryPeriod};
|
||||
use crate::products::GatewayId;
|
||||
use crate::{get_arg, get_args, join_query_pairs, rfc3339, Api, Values};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use derive_more::{Display, FromStr};
|
||||
|
|
|
@ -2,15 +2,16 @@ use crate::error::TeslatteError;
|
|||
use crate::powerwall::PowerwallId;
|
||||
use crate::vehicles::VehicleData;
|
||||
use crate::{get, Api};
|
||||
use derive_more::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl Api {
|
||||
get!(energy_sites, Vec<EnergySite>, "/products");
|
||||
get!(products, Vec<Product>, "/products");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Display)]
|
||||
pub struct EnergySiteId(pub u64);
|
||||
|
||||
impl FromStr for EnergySiteId {
|
||||
|
@ -28,7 +29,7 @@ pub struct GatewayId(String);
|
|||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum EnergySite {
|
||||
pub enum Product {
|
||||
Vehicle(Box<VehicleData>),
|
||||
Solar(Box<SolarData>),
|
||||
Powerwall(Box<PowerwallData>),
|
||||
|
@ -83,7 +84,7 @@ pub struct Components {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::calendar_history::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
|
||||
use crate::energy_sites::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
|
||||
use crate::Values;
|
||||
use chrono::DateTime;
|
||||
|
||||
|
@ -117,7 +118,7 @@ mod tests {
|
|||
}
|
||||
"#;
|
||||
|
||||
if let EnergySite::Powerwall(data) = serde_json::from_str(json).unwrap() {
|
||||
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);
|
||||
|
@ -208,8 +209,8 @@ mod tests {
|
|||
"command_signing": "allowed"
|
||||
}
|
||||
"#;
|
||||
let energy_site: EnergySite = serde_json::from_str(json).unwrap();
|
||||
if let EnergySite::Vehicle(v) = energy_site {
|
||||
let energy_site: Product = serde_json::from_str(json).unwrap();
|
||||
if let Product::Vehicle(v) = energy_site {
|
||||
assert_eq!(v.id.0, 1111193485934);
|
||||
assert_eq!(v.user_id, 2222291283912);
|
||||
assert_eq!(v.vehicle_id.0, 333331238921);
|
Loading…
Reference in a new issue