Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: Rename Api to OwnerApi. Remove ResponseData<T>. Add VehicleApi trait. #8

Merged
merged 2 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::env;
use teslatte::auth::AccessToken;
use teslatte::products::Product;
use teslatte::Api;
use teslatte::{OwnerApi, VehicleApi};

#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();

let api = match env::var("TESLA_ACCESS_TOKEN") {
Ok(t) => Api::new(AccessToken(t), None),
Ok(t) => OwnerApi::new(AccessToken(t), None),
Err(_) => {
let api = Api::from_interactive_url().await.unwrap();
let api = OwnerApi::from_interactive_url().await.unwrap();
println!("TOKEN: {:?}", api.access_token);
api
}
Expand All @@ -21,7 +21,7 @@ async fn main() {

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!");
}
Expand All @@ -38,21 +38,21 @@ async fn main() {

Product::Solar(e) => {
let site_info = api.energy_sites_site_info(&e.energy_site_id).await.unwrap();
dbg!(&*site_info);
dbg!(&site_info);

let live_info = api
.energy_sites_live_status(&e.energy_site_id)
.await
.unwrap();
dbg!(&*live_info);
dbg!(&live_info);
}

Product::Powerwall(p) => {
let live_info = api
.energy_sites_live_status(&p.energy_site_id)
.await
.unwrap();
dbg!(&*live_info);
dbg!(&live_info);
}
}
}
Expand Down
22 changes: 12 additions & 10 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::TeslatteError::{CouldNotFindCallbackCode, CouldNotFindState};
use crate::{Api, TeslatteError};
use crate::{OwnerApi, TeslatteError};
use derive_more::{Display, FromStr};
use rand::Rng;
use reqwest::Client;
Expand All @@ -22,11 +22,11 @@ struct Callback {
state: String,
}

impl Api {
impl OwnerApi {
/// Show a URL for the user to click on to log into tesla.com, the ask them to paste the
/// URL they end up on, which is a 404 page. The URL contains OAuth information needed to
/// complete authentication for an access key.
pub async fn from_interactive_url() -> Result<Api, TeslatteError> {
pub async fn from_interactive_url() -> Result<OwnerApi, TeslatteError> {
let login_form = Self::get_login_url_for_user().await;
println!("{}", "-".repeat(80));
println!("{}", login_form.url);
Expand All @@ -40,12 +40,12 @@ page, where the URL will start with https://auth.tesla.com/void/callback?code=..
let callback_url = ask_input("Enter the whole URL of the 404 page: ");
println!(); // Newline to make the next output more separated and clear.

Api::from_callback_url(&login_form, &callback_url).await
OwnerApi::from_callback_url(&login_form, &callback_url).await
}

/// Generate a [LoginForm] containing a URL the user should visit.
///
/// See [Api::from_callback_url()] for the next step.
/// See [OwnerApi::from_callback_url()] for the next step.
pub async fn get_login_url_for_user() -> LoginForm {
let code = Code::new();
let state = random_string(8);
Expand All @@ -54,11 +54,11 @@ page, where the URL will start with https://auth.tesla.com/void/callback?code=..
}

/// Parse a callback URL that the user was redirected to after logging in via
/// [Api::from_interactive_url()].
/// [OwnerApi::from_interactive_url()].
pub async fn from_callback_url(
login_form: &LoginForm,
callback_url: &str,
) -> Result<Api, TeslatteError> {
) -> Result<OwnerApi, TeslatteError> {
let callback = Self::extract_callback_from_url(callback_url)?;
if callback.state != login_form.state {
return Err(TeslatteError::StateMismatch {
Expand All @@ -70,12 +70,14 @@ page, where the URL will start with https://auth.tesla.com/void/callback?code=..
let bearer = Self::exchange_auth_for_bearer(&login_form.code, &callback.code).await?;
let access_token = AccessToken(bearer.access_token);
let refresh_token = RefreshToken(bearer.refresh_token);
Ok(Api::new(access_token, Some(refresh_token)))
Ok(OwnerApi::new(access_token, Some(refresh_token)))
}

pub async fn from_refresh_token(refresh_token: &RefreshToken) -> Result<Api, TeslatteError> {
pub async fn from_refresh_token(
refresh_token: &RefreshToken,
) -> Result<OwnerApi, TeslatteError> {
let response = Self::refresh_token(refresh_token).await?;
Ok(Api::new(
Ok(OwnerApi::new(
response.access_token,
Some(response.refresh_token),
))
Expand Down
34 changes: 0 additions & 34 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,3 @@
pub mod energy;
pub mod powerwall;
pub mod vehicle;

use crate::error::TeslatteError;
use crate::ResponseData;
use std::process::exit;

pub fn print_json<T>(result: Result<ResponseData<T>, TeslatteError>) {
match result {
Ok(data) => print_json_data(data),
Err(TeslatteError::ServerError { ref body, .. }) if body.is_some() => {
print_json_str(&body.clone().unwrap())
}
Err(e) => {
eprintln!("{}", e);
exit(1);
}
}
}
pub fn print_json_data<T>(data: ResponseData<T>) {
// TODO: pretty print cli option
print_json_str(data.body());
}

pub fn print_json_str(body: &str) {
#[cfg(feature = "cli-pretty-json")]
{
use colored_json::prelude::*;
println!("{}", body.to_colored_json_auto().unwrap());
}

#[cfg(not(feature = "cli-pretty-json"))]
{
println!("{}", body);
}
}
13 changes: 6 additions & 7 deletions src/cli/energy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::cli::print_json;
use crate::energy_sites::{CalendarHistoryValues, HistoryKind, HistoryPeriod};
use crate::products::EnergySiteId;
use crate::Api;
use crate::OwnerApi;
use chrono::DateTime;
use clap::{Args, Subcommand};
use miette::{IntoDiagnostic, WrapErr};
Expand All @@ -23,16 +22,16 @@ pub struct EnergySiteArgs {
}

impl EnergySiteArgs {
pub async fn run(&self, api: &Api) -> miette::Result<()> {
pub async fn run(&self, api: &OwnerApi) -> miette::Result<()> {
match &self.command {
EnergySiteCommand::SiteStatus => {
print_json(api.energy_sites_site_status(&self.id).await);
api.energy_sites_site_status(&self.id).await?;
}
EnergySiteCommand::LiveStatus => {
print_json(api.energy_sites_live_status(&self.id).await);
api.energy_sites_live_status(&self.id).await?;
}
EnergySiteCommand::SiteInfo => {
print_json(api.energy_sites_site_info(&self.id).await);
api.energy_sites_site_info(&self.id).await?;
}
EnergySiteCommand::CalendarHistory(args) => {
let start_date = args
Expand All @@ -54,7 +53,7 @@ impl EnergySiteArgs {
start_date,
end_date,
};
print_json(api.energy_sites_calendar_history(&values).await);
api.energy_sites_calendar_history(&values).await?;
}
}
Ok(())
Expand Down
25 changes: 11 additions & 14 deletions src/cli/powerwall.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::cli::print_json_data;
use crate::energy_sites::{HistoryKind, HistoryPeriod};
use crate::powerwall::{PowerwallEnergyHistoryValues, PowerwallId};
use crate::Api;
use crate::OwnerApi;
use clap::{Args, Subcommand};

#[derive(Debug, Subcommand)]
Expand All @@ -21,22 +20,20 @@ pub struct PowerwallArgs {
}

impl PowerwallArgs {
pub async fn run(&self, api: &Api) -> miette::Result<()> {
pub async fn run(&self, api: &OwnerApi) -> miette::Result<()> {
match self.command {
PowerwallCommand::Status => {
print_json_data(api.powerwall_status(&self.id).await?);
api.powerwall_status(&self.id).await?;
}
PowerwallCommand::History => {
print_json_data(
api.powerwall_energy_history(&PowerwallEnergyHistoryValues {
powerwall_id: self.id.clone(),
period: HistoryPeriod::Day,
kind: HistoryKind::Power,
start_date: None,
end_date: None,
})
.await?,
);
api.powerwall_energy_history(&PowerwallEnergyHistoryValues {
powerwall_id: self.id.clone(),
period: HistoryPeriod::Day,
kind: HistoryKind::Power,
start_date: None,
end_date: None,
})
.await?;
}
}
Ok(())
Expand Down
43 changes: 21 additions & 22 deletions src/cli/vehicle.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::cli::print_json;
use crate::vehicles::{
SetChargeLimit, SetChargingAmps, SetScheduledCharging, SetScheduledDeparture, SetTemperatures,
};
use crate::{Api, VehicleId};
use crate::{OwnerApi, VehicleApi, VehicleId};
use clap::{Args, Subcommand};

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -74,64 +73,64 @@ pub struct VehicleArgs {
}

impl VehicleArgs {
pub async fn run(self, api: &Api) -> miette::Result<()> {
pub async fn run(self, api: &OwnerApi) -> miette::Result<()> {
match self.command {
VehicleCommand::VehicleData => {
print_json(api.vehicle_data(&self.id).await);
api.vehicle_data(&self.id).await?;
}
VehicleCommand::SetChargeLimit(limit) => {
print_json(api.set_charge_limit(&self.id, &limit).await);
api.set_charge_limit(&self.id, &limit).await?;
}
VehicleCommand::SetChargingAmps(charging_amps) => {
print_json(api.set_charging_amps(&self.id, &charging_amps).await);
api.set_charging_amps(&self.id, &charging_amps).await?;
}
VehicleCommand::ChargeStart => {
print_json(api.charge_start(&self.id).await);
api.charge_start(&self.id).await?;
}
VehicleCommand::ChargeStop => {
print_json(api.charge_stop(&self.id).await);
api.charge_stop(&self.id).await?;
}
VehicleCommand::ChargePortDoorOpen => {
print_json(api.charge_port_door_open(&self.id).await);
api.charge_port_door_open(&self.id).await?;
}
VehicleCommand::ChargePortDoorClose => {
print_json(api.charge_port_door_close(&self.id).await);
api.charge_port_door_close(&self.id).await?;
}
VehicleCommand::ChargeStandard => {
print_json(api.charge_standard(&self.id).await);
api.charge_standard(&self.id).await?;
}
VehicleCommand::ChargeMaxRange => {
print_json(api.charge_max_range(&self.id).await);
api.charge_max_range(&self.id).await?;
}
VehicleCommand::SetScheduledCharging(charging) => {
print_json(api.set_scheduled_charging(&self.id, &charging).await);
api.set_scheduled_charging(&self.id, &charging).await?;
}
VehicleCommand::SetScheduledDeparture(departure) => {
print_json(api.set_scheduled_departure(&self.id, &departure).await);
api.set_scheduled_departure(&self.id, &departure).await?;
}
VehicleCommand::HonkHorn => {
print_json(api.honk_horn(&self.id).await);
api.honk_horn(&self.id).await?;
}
VehicleCommand::FlashLights => {
print_json(api.flash_lights(&self.id).await);
api.flash_lights(&self.id).await?;
}
VehicleCommand::EnableHvac => {
print_json(api.auto_conditioning_start(&self.id).await);
api.auto_conditioning_start(&self.id).await?;
}
VehicleCommand::DisableHvac => {
print_json(api.auto_conditioning_stop(&self.id).await);
api.auto_conditioning_stop(&self.id).await?;
}
VehicleCommand::HvacTemperature(temps) => {
print_json(api.set_temps(&self.id, &temps).await);
api.set_temps(&self.id, &temps).await?;
}
VehicleCommand::DoorUnlock => {
print_json(api.door_unlock(&self.id).await);
api.door_unlock(&self.id).await?;
}
VehicleCommand::DoorLock => {
print_json(api.door_lock(&self.id).await);
api.door_lock(&self.id).await?;
}
VehicleCommand::RemoteStartDrive => {
print_json(api.remote_start_drive(&self.id).await);
api.remote_start_drive(&self.id).await?;
}
}
Ok(())
Expand Down
18 changes: 10 additions & 8 deletions src/energy_sites.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::products::EnergySiteId;
use crate::{get_arg, get_args, join_query_pairs, rfc3339, Api, Values};
use crate::{join_query_pairs, pub_get_arg, pub_get_args, rfc3339, OwnerApi, 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);
impl OwnerApi {
pub_get_arg!(energy_sites_site_status, SiteStatus, "/energy_sites/{}/site_status", EnergySiteId);
pub_get_arg!(energy_sites_live_status, LiveStatus, "/energy_sites/{}/live_status", EnergySiteId);
pub_get_arg!(energy_sites_site_info, SiteInfo, "/energy_sites/{}/site_info", EnergySiteId);
pub_get_args!(energy_sites_calendar_history, CalendarHistory, "/energy_sites/{}/calendar_history", CalendarHistoryValues);
}

#[derive(Debug, Clone, Deserialize)]
Expand All @@ -22,8 +22,10 @@ pub struct SiteStatus {
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
// TODO: Unknown type. Was null.
pub powerwall_tesla_electric_interested_in: Option<()>,
// battery
pub resource_type: String,
pub site_name: String,
pub storm_mode_enabled: bool,
pub sync_grid_alert_enabled: bool,
Expand Down
Loading