wip: Refactor vehicle api. cli.
This commit is contained in:
parent
9cca1ca5bd
commit
6214bf1b5f
|
@ -1,7 +1,7 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::env;
|
use std::env;
|
||||||
use teslatte::auth::{AccessToken, Authentication};
|
use teslatte::auth::{AccessToken, Authentication};
|
||||||
use teslatte::vehicle_state::SetChargeLimit;
|
use teslatte::vehicles::SetChargeLimit;
|
||||||
use teslatte::Api;
|
use teslatte::Api;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use teslatte::auth::{AccessToken, Authentication, RefreshToken};
|
use teslatte::auth::{AccessToken, Authentication, RefreshToken};
|
||||||
use teslatte::vehicle_state::SetChargeLimit;
|
use teslatte::vehicles::{SetChargeLimit, SetChargingAmps};
|
||||||
use teslatte::{Api, Id};
|
use teslatte::{Api, Id};
|
||||||
|
|
||||||
const TESLA_ACCESS_TOKEN: &str = "TESLA_ACCESS_TOKEN";
|
const TESLA_ACCESS_TOKEN: &str = "TESLA_ACCESS_TOKEN";
|
||||||
|
@ -55,14 +55,37 @@ enum ApiCommand {
|
||||||
/// Get a list of vehicles.
|
/// Get a list of vehicles.
|
||||||
Vehicles,
|
Vehicles,
|
||||||
|
|
||||||
|
/// Specific Vehicle.
|
||||||
|
Vehicle(Vehicle),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
struct Vehicle {
|
||||||
|
pub id: Id,
|
||||||
|
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub command: VehicleCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
enum VehicleCommand {
|
||||||
/// Get vehicle data.
|
/// Get vehicle data.
|
||||||
VehicleData { id: Id },
|
Data,
|
||||||
|
|
||||||
/// Get charge state.
|
/// Get charge state.
|
||||||
ChargeState { id: Id },
|
ChargeState,
|
||||||
|
|
||||||
/// Set charge limit.
|
/// Set charge limit.
|
||||||
SetChargeLimit { id: Id, percent: u8 },
|
SetChargeLimit { percent: u8 },
|
||||||
|
|
||||||
|
/// Set charge amps.
|
||||||
|
SetChargingAmps { charging_amps: i64 },
|
||||||
|
|
||||||
|
/// Start charging.
|
||||||
|
ChargeStart,
|
||||||
|
|
||||||
|
/// Stop charging.
|
||||||
|
ChargeStop,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -89,33 +112,51 @@ async fn main() {
|
||||||
updated_tokens(save, response.access_token, refresh_token);
|
updated_tokens(save, response.access_token, refresh_token);
|
||||||
}
|
}
|
||||||
Command::Api(api_args) => {
|
Command::Api(api_args) => {
|
||||||
let access_token = match api_args.access_token {
|
let access_token = match &api_args.access_token {
|
||||||
Some(a) => a,
|
Some(a) => a.clone(),
|
||||||
None => {
|
None => {
|
||||||
let config = Config::load();
|
let config = Config::load();
|
||||||
config.access_token
|
config.access_token.clone()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let api = Api::new(&access_token);
|
let api = Api::new(&access_token);
|
||||||
#[allow(unused_results)]
|
|
||||||
match api_args.command {
|
match api_args.command {
|
||||||
ApiCommand::Vehicles => {
|
ApiCommand::Vehicles => {
|
||||||
dbg!(api.vehicles().await.unwrap());
|
let vehicles = api.vehicles().await.unwrap();
|
||||||
|
dbg!(&vehicles);
|
||||||
}
|
}
|
||||||
ApiCommand::VehicleData { id } => {
|
ApiCommand::Vehicle(v) => vehicles(&api, v).await,
|
||||||
dbg!(api.vehicle_data(&id).await.unwrap());
|
|
||||||
}
|
}
|
||||||
ApiCommand::ChargeState { id } => {
|
|
||||||
dbg!(api.charge_state(&id).await.unwrap());
|
|
||||||
}
|
}
|
||||||
ApiCommand::SetChargeLimit { id, percent } => {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vehicles(api: &Api, vehicle: Vehicle) {
|
||||||
|
match vehicle.command {
|
||||||
|
VehicleCommand::Data => {
|
||||||
|
dbg!(api.vehicle_data(&vehicle.id).await.unwrap());
|
||||||
|
}
|
||||||
|
VehicleCommand::ChargeState => {
|
||||||
|
dbg!(api.charge_state(&vehicle.id).await.unwrap());
|
||||||
|
}
|
||||||
|
VehicleCommand::SetChargeLimit { percent } => {
|
||||||
dbg!(api
|
dbg!(api
|
||||||
.set_charge_limit(&id, &SetChargeLimit { percent })
|
.set_charge_limit(&vehicle.id, &SetChargeLimit { percent })
|
||||||
.await
|
.await
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
VehicleCommand::SetChargingAmps { charging_amps } => {
|
||||||
|
dbg!(api
|
||||||
|
.set_charging_amps(&vehicle.id, &SetChargingAmps { charging_amps })
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
VehicleCommand::ChargeStart => {
|
||||||
|
dbg!(api.charge_start(&vehicle.id).await.unwrap());
|
||||||
|
}
|
||||||
|
VehicleCommand::ChargeStop => {
|
||||||
|
dbg!(api.charge_stop(&vehicle.id).await.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
125
src/lib.rs
125
src/lib.rs
|
@ -9,7 +9,7 @@ use tracing::{debug, instrument, trace};
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod vehicle_state;
|
pub mod vehicles;
|
||||||
|
|
||||||
const API_URL: &str = "https://owner-api.teslamotors.com";
|
const API_URL: &str = "https://owner-api.teslamotors.com";
|
||||||
|
|
||||||
|
@ -209,6 +209,7 @@ macro_rules! get {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use get;
|
||||||
|
|
||||||
/// GET /api/1/[url] with an argument.
|
/// GET /api/1/[url] with an argument.
|
||||||
///
|
///
|
||||||
|
@ -216,11 +217,13 @@ macro_rules! get {
|
||||||
macro_rules! get_arg {
|
macro_rules! get_arg {
|
||||||
($name:ident, $return_type:ty, $url:expr, $arg_type:ty) => {
|
($name:ident, $return_type:ty, $url:expr, $arg_type:ty) => {
|
||||||
pub async fn $name(&self, arg: &$arg_type) -> miette::Result<$return_type, TeslatteError> {
|
pub async fn $name(&self, arg: &$arg_type) -> miette::Result<$return_type, TeslatteError> {
|
||||||
let url = format!(concat!("/api/1", $url), arg);
|
let url = format!($url, arg);
|
||||||
|
let url = format!("/api/1{}", url);
|
||||||
self.get(&url).await
|
self.get(&url).await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use get_arg;
|
||||||
|
|
||||||
/// POST /api/1/[url] with an argument and data
|
/// POST /api/1/[url] with an argument and data
|
||||||
macro_rules! post_arg {
|
macro_rules! post_arg {
|
||||||
|
@ -230,122 +233,32 @@ macro_rules! post_arg {
|
||||||
arg: &$arg_type,
|
arg: &$arg_type,
|
||||||
data: &$request_type,
|
data: &$request_type,
|
||||||
) -> miette::Result<(), TeslatteError> {
|
) -> miette::Result<(), TeslatteError> {
|
||||||
let url_fmt = format!($url, arg);
|
let url = format!($url, arg);
|
||||||
let url = format!(concat!("/api/1", $url), arg);
|
let url = format!("/api/1{}", url);
|
||||||
self.post(&url, data).await
|
self.post(&url, data).await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use post_arg;
|
||||||
|
|
||||||
// /// POST /api/1/vehicles/[id]/... without data
|
/// Post like above but with an empty body using the Empty struct.
|
||||||
// macro_rules! post_v {
|
macro_rules! post_arg_empty {
|
||||||
// ($name:ident, $url:expr) => {
|
($name:ident, $url:expr, $arg_type:ty) => {
|
||||||
// pub async fn $name(&self, id: &Id) -> miette::Result<(), TeslatteError> {
|
pub async fn $name(&self, arg: &$arg_type) -> miette::Result<(), TeslatteError> {
|
||||||
// let url = format!("/vehicles/{}{}", id.0, $url);
|
let url = format!($url, arg);
|
||||||
// self.post(&url, &Empty {}).await
|
let url = format!("/api/1{}", url);
|
||||||
// }
|
self.post(&url, &Empty {}).await
|
||||||
// };
|
}
|
||||||
// }
|
};
|
||||||
//
|
|
||||||
// /// POST /api/1/vehicles/[id]/... with data
|
|
||||||
// macro_rules! post_vd {
|
|
||||||
// ($name:ident, $struct:ty, $url:expr) => {
|
|
||||||
// pub async fn $name(&self, id: &Id, data: &$struct) -> miette::Result<(), TeslatteError> {
|
|
||||||
// let url = format!("/api/1/vehicles/{}{}", id.0, $url);
|
|
||||||
// self.post(&url, &data).await
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
use crate::vehicle_state::ChargeState;
|
|
||||||
use crate::vehicle_state::SetChargeLimit;
|
|
||||||
use crate::vehicle_state::Vehicle;
|
|
||||||
use crate::vehicle_state::VehicleData;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl Api {
|
|
||||||
get!(vehicles, Vec<Vehicle>, "/vehicles");
|
|
||||||
get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", Id);
|
|
||||||
get_arg!(charge_state, ChargeState, "/vehicles/{}/data_request/charge_state", Id);
|
|
||||||
post_arg!(set_charge_limit, SetChargeLimit, "/vehicles/{}/command/set_charge_limit", Id);
|
|
||||||
// post_vd!(set_charging_amps, SetChargingAmps, "/command/set_charging_amps");
|
|
||||||
// post_v!(charge_start, "/command/charge_start");
|
|
||||||
// post_v!(charge_stop, "/command/charge_stop");
|
|
||||||
}
|
}
|
||||||
|
pub(crate) use post_arg_empty;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::vehicle_state::ChargeState;
|
use crate::vehicles::ChargeState;
|
||||||
use test_log::test;
|
use test_log::test;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn json() {
|
|
||||||
let s = r#"
|
|
||||||
{
|
|
||||||
"response": {
|
|
||||||
"battery_heater_on": false,
|
|
||||||
"battery_level": 50,
|
|
||||||
"battery_range": 176.08,
|
|
||||||
"charge_amps": 5,
|
|
||||||
"charge_current_request": 5,
|
|
||||||
"charge_current_request_max": 16,
|
|
||||||
"charge_enable_request": true,
|
|
||||||
"charge_energy_added": 1.05,
|
|
||||||
"charge_limit_soc": 75,
|
|
||||||
"charge_limit_soc_max": 100,
|
|
||||||
"charge_limit_soc_min": 50,
|
|
||||||
"charge_limit_soc_std": 90,
|
|
||||||
"charge_miles_added_ideal": 5,
|
|
||||||
"charge_miles_added_rated": 5,
|
|
||||||
"charge_port_cold_weather_mode": false,
|
|
||||||
"charge_port_color": "<invalid>",
|
|
||||||
"charge_port_door_open": true,
|
|
||||||
"charge_port_latch": "Engaged",
|
|
||||||
"charge_rate": 14.8,
|
|
||||||
"charge_to_max_range": false,
|
|
||||||
"charger_actual_current": 5,
|
|
||||||
"charger_phases": 2,
|
|
||||||
"charger_pilot_current": 16,
|
|
||||||
"charger_power": 4,
|
|
||||||
"charger_voltage": 241,
|
|
||||||
"charging_state": "Charging",
|
|
||||||
"conn_charge_cable": "IEC",
|
|
||||||
"est_battery_range": 163.81,
|
|
||||||
"fast_charger_brand": "<invalid>",
|
|
||||||
"fast_charger_present": false,
|
|
||||||
"fast_charger_type": "ACSingleWireCAN",
|
|
||||||
"ideal_battery_range": 176.08,
|
|
||||||
"managed_charging_active": false,
|
|
||||||
"managed_charging_start_time": null,
|
|
||||||
"managed_charging_user_canceled": false,
|
|
||||||
"max_range_charge_counter": 0,
|
|
||||||
"minutes_to_full_charge": 350,
|
|
||||||
"not_enough_power_to_heat": null,
|
|
||||||
"off_peak_charging_enabled": false,
|
|
||||||
"off_peak_charging_times": "all_week",
|
|
||||||
"off_peak_hours_end_time": 1140,
|
|
||||||
"preconditioning_enabled": false,
|
|
||||||
"preconditioning_times": "all_week",
|
|
||||||
"scheduled_charging_mode": "StartAt",
|
|
||||||
"scheduled_charging_pending": false,
|
|
||||||
"scheduled_charging_start_time": 1647045000,
|
|
||||||
"scheduled_charging_start_time_app": 690,
|
|
||||||
"scheduled_charging_start_time_minutes": 690,
|
|
||||||
"scheduled_departure_time": 1641337200,
|
|
||||||
"scheduled_departure_time_minutes": 600,
|
|
||||||
"supercharger_session_trip_planner": false,
|
|
||||||
"time_to_full_charge": 5.83,
|
|
||||||
"timestamp": 1646978638155,
|
|
||||||
"trip_charging": false,
|
|
||||||
"usable_battery_level": 50,
|
|
||||||
"user_charge_enable_request": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
Api::parse_json::<ChargeState, _>(s, || "req".to_string()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error() {
|
fn error() {
|
||||||
let s = r#"{
|
let s = r#"{
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
|
use crate::error::TeslatteError;
|
||||||
/// Please note that these structs are generated from my own responses.
|
/// Please note that these structs are generated from my own responses.
|
||||||
///
|
///
|
||||||
/// Sometimes the API will return a null for a field where I've put in a non Option type, which
|
/// Sometimes the API will return a null for a field where I've put in a non Option type, which
|
||||||
/// will cause the deserializer to fail. Please log an issue to fix these if you come across it.
|
/// will cause the deserializer to fail. Please log an issue to fix these if you come across it.
|
||||||
use crate::{Id, VehicleId};
|
use crate::{get, get_arg, post_arg, post_arg_empty, Api, Empty, Id, VehicleId};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl Api {
|
||||||
|
get!(vehicles, Vec<Vehicle>, "/vehicles");
|
||||||
|
get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", Id);
|
||||||
|
get_arg!(charge_state, ChargeState, "/vehicles/{}/data_request/charge_state", Id);
|
||||||
|
post_arg!(set_charge_limit, SetChargeLimit, "/vehicles/{}/command/set_charge_limit", Id);
|
||||||
|
post_arg!(set_charging_amps, SetChargingAmps, "/vehicles/{}/command/set_charging_amps", Id);
|
||||||
|
post_arg_empty!(charge_start, "/vehicles/{}/command/charge_start", Id);
|
||||||
|
post_arg_empty!(charge_stop, "/vehicles/{}/command/charge_stop", Id);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct VehicleData {
|
pub struct VehicleData {
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
@ -304,3 +316,70 @@ pub struct SetChargeLimit {
|
||||||
// pub percent: Percentage,
|
// pub percent: Percentage,
|
||||||
pub percent: u8,
|
pub percent: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn json() {
|
||||||
|
let s = r#"
|
||||||
|
{
|
||||||
|
"response": {
|
||||||
|
"battery_heater_on": false,
|
||||||
|
"battery_level": 50,
|
||||||
|
"battery_range": 176.08,
|
||||||
|
"charge_amps": 5,
|
||||||
|
"charge_current_request": 5,
|
||||||
|
"charge_current_request_max": 16,
|
||||||
|
"charge_enable_request": true,
|
||||||
|
"charge_energy_added": 1.05,
|
||||||
|
"charge_limit_soc": 75,
|
||||||
|
"charge_limit_soc_max": 100,
|
||||||
|
"charge_limit_soc_min": 50,
|
||||||
|
"charge_limit_soc_std": 90,
|
||||||
|
"charge_miles_added_ideal": 5,
|
||||||
|
"charge_miles_added_rated": 5,
|
||||||
|
"charge_port_cold_weather_mode": false,
|
||||||
|
"charge_port_color": "<invalid>",
|
||||||
|
"charge_port_door_open": true,
|
||||||
|
"charge_port_latch": "Engaged",
|
||||||
|
"charge_rate": 14.8,
|
||||||
|
"charge_to_max_range": false,
|
||||||
|
"charger_actual_current": 5,
|
||||||
|
"charger_phases": 2,
|
||||||
|
"charger_pilot_current": 16,
|
||||||
|
"charger_power": 4,
|
||||||
|
"charger_voltage": 241,
|
||||||
|
"charging_state": "Charging",
|
||||||
|
"conn_charge_cable": "IEC",
|
||||||
|
"est_battery_range": 163.81,
|
||||||
|
"fast_charger_brand": "<invalid>",
|
||||||
|
"fast_charger_present": false,
|
||||||
|
"fast_charger_type": "ACSingleWireCAN",
|
||||||
|
"ideal_battery_range": 176.08,
|
||||||
|
"managed_charging_active": false,
|
||||||
|
"managed_charging_start_time": null,
|
||||||
|
"managed_charging_user_canceled": false,
|
||||||
|
"max_range_charge_counter": 0,
|
||||||
|
"minutes_to_full_charge": 350,
|
||||||
|
"not_enough_power_to_heat": null,
|
||||||
|
"off_peak_charging_enabled": false,
|
||||||
|
"off_peak_charging_times": "all_week",
|
||||||
|
"off_peak_hours_end_time": 1140,
|
||||||
|
"preconditioning_enabled": false,
|
||||||
|
"preconditioning_times": "all_week",
|
||||||
|
"scheduled_charging_mode": "StartAt",
|
||||||
|
"scheduled_charging_pending": false,
|
||||||
|
"scheduled_charging_start_time": 1647045000,
|
||||||
|
"scheduled_charging_start_time_app": 690,
|
||||||
|
"scheduled_charging_start_time_minutes": 690,
|
||||||
|
"scheduled_departure_time": 1641337200,
|
||||||
|
"scheduled_departure_time_minutes": 600,
|
||||||
|
"supercharger_session_trip_planner": false,
|
||||||
|
"time_to_full_charge": 5.83,
|
||||||
|
"timestamp": 1646978638155,
|
||||||
|
"trip_charging": false,
|
||||||
|
"usable_battery_level": 50,
|
||||||
|
"user_charge_enable_request": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
Api::parse_json::<ChargeState, _>(s, || "req".to_string()).unwrap();
|
||||||
|
}
|
Loading…
Reference in a new issue