Explicit login, token save, ergonomics
This commit is contained in:
parent
14164037d3
commit
d490970ab0
188
src/main.rs
188
src/main.rs
@ -1,12 +1,12 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use easee::api;
|
use easee::api::ChargingSession;
|
||||||
use easee::stream;
|
use easee::stream;
|
||||||
|
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
#[derive(Debug,Clone,ValueEnum)]
|
#[derive(Debug,Clone,Copy,ValueEnum)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Start,
|
Start,
|
||||||
Stop,
|
Stop,
|
||||||
@ -14,93 +14,133 @@ enum Command {
|
|||||||
Resume,
|
Resume,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,ValueEnum)]
|
||||||
|
enum Session {
|
||||||
|
Ongoing,
|
||||||
|
Latest
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone,Subcommand)]
|
#[derive(Debug,Clone,Subcommand)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
Status { id: Option<String> },
|
Login,
|
||||||
Ongoing,
|
Status,
|
||||||
Latest,
|
Session {
|
||||||
|
#[arg(default_value = "ongoing")]
|
||||||
|
session: Session
|
||||||
|
},
|
||||||
|
Charge { command: Command },
|
||||||
Stream,
|
Stream,
|
||||||
Command { id: String, command: Command }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Parser)]
|
#[derive(Debug,Parser)]
|
||||||
struct CLI {
|
struct CLI {
|
||||||
#[arg(short,long,env)]
|
#[arg(short,long)]
|
||||||
username: String,
|
debug: bool,
|
||||||
#[arg(short,long,env)]
|
|
||||||
password: String,
|
#[arg(short,long)]
|
||||||
|
charger_id: Vec<String>,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SAVED_TOKEN_PATH: &str = ".easee_token";
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|
||||||
tracing::subscriber::set_global_default(tracing_subscriber::FmtSubscriber::new())
|
|
||||||
.expect("Tracing subscriber failed");
|
|
||||||
|
|
||||||
let args = CLI::parse();
|
let args = CLI::parse();
|
||||||
let mut ctx = api::Context::from_login(&args.username, &args.password)?;
|
if args.debug {
|
||||||
|
tracing::subscriber::set_global_default(tracing_subscriber::FmtSubscriber::new())
|
||||||
info!("Logged in");
|
.expect("Tracing subscriber failed");
|
||||||
|
|
||||||
let sites = ctx.sites()?;
|
|
||||||
let chargers = ctx.chargers()?;
|
|
||||||
info!("{} sites and {} chargers available", sites.len(), chargers.len());
|
|
||||||
|
|
||||||
match args.mode {
|
|
||||||
Mode::Status { id } => {
|
|
||||||
for c in &chargers {
|
|
||||||
if id.as_deref().map(|id| id == &c.id).unwrap_or(true) {
|
|
||||||
println!("{}: {:?}", c.id, c.state(&mut ctx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Mode::Ongoing => {
|
|
||||||
for c in &chargers {
|
|
||||||
println!("{}: {:?}", c.id, c.ongoing_session(&mut ctx));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Mode::Latest => {
|
|
||||||
for c in &chargers {
|
|
||||||
println!("{:?}", c.latest_session(&mut ctx));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Mode::Stream => {
|
|
||||||
let mut stream = stream::Stream::open(&mut ctx)?;
|
|
||||||
for c in &chargers {
|
|
||||||
stream.subscribe(&c.id)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stream = easee::signalr::Stream::from_ws(stream);
|
|
||||||
loop {
|
|
||||||
println!("{:?}", stream.recv()?);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mode::Command { id, command } => {
|
|
||||||
|
|
||||||
for c in &chargers {
|
|
||||||
if c.id == id {
|
|
||||||
match command {
|
|
||||||
Command::Start => c.start(&mut ctx)?,
|
|
||||||
Command::Stop => c.stop(&mut ctx)?,
|
|
||||||
Command::Pause => c.pause(&mut ctx)?,
|
|
||||||
Command::Resume => c.resume(&mut ctx)?,
|
|
||||||
}
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("Charger not found.");
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Mode::Login = args.mode {
|
||||||
|
use std::io::Write;
|
||||||
|
let stdin = std::io::stdin();
|
||||||
|
let mut stderr = std::io::stderr();
|
||||||
|
|
||||||
|
let mut username = String::new();
|
||||||
|
let mut password = String::new();
|
||||||
|
|
||||||
|
write!(stderr, "Username: ")?;
|
||||||
|
stdin.read_line(&mut username)?;
|
||||||
|
write!(stderr, "Password: ")?;
|
||||||
|
stdin.read_line(&mut password)?;
|
||||||
|
|
||||||
|
let username = username.trim();
|
||||||
|
let password = password.trim();
|
||||||
|
|
||||||
|
let ctx = easee::api::Context::from_login(&username, &password)?;
|
||||||
|
eprintln!("Login successful.");
|
||||||
|
|
||||||
|
std::fs::write(SAVED_TOKEN_PATH, ctx.save().as_bytes())?;
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let saved = std::fs::read_to_string(SAVED_TOKEN_PATH)
|
||||||
|
.context("Cannot read saved token (did you log in ?)")?;
|
||||||
|
let mut ctx = easee::api::Context::from_saved(&saved)?;
|
||||||
|
|
||||||
|
let chargers;
|
||||||
|
if args.charger_id.is_empty() {
|
||||||
|
chargers = ctx.chargers()?;
|
||||||
|
info!("{} chargers available.", chargers.len());
|
||||||
|
} else {
|
||||||
|
chargers = args.charger_id.iter()
|
||||||
|
.map(|id| ctx.charger(&id))
|
||||||
|
.collect::<Result<_,_>>()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Mode::Stream = args.mode {
|
||||||
|
let mut stream = stream::Stream::open(&mut ctx)?;
|
||||||
|
for c in &chargers {
|
||||||
|
stream.subscribe(&c.id)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stream = easee::signalr::Stream::from_ws(stream);
|
||||||
|
loop {
|
||||||
|
println!("{:?}", stream.recv()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in &chargers {
|
||||||
|
match args.mode {
|
||||||
|
Mode::Status => {
|
||||||
|
println!("{}: {:?}", c.id, c.state(&mut ctx));
|
||||||
|
},
|
||||||
|
Mode::Session { session: Session::Ongoing }=> {
|
||||||
|
show_session(&c.ongoing_session(&mut ctx)?);
|
||||||
|
},
|
||||||
|
Mode::Session { session: Session::Latest } => {
|
||||||
|
show_session(&c.latest_session(&mut ctx)?);
|
||||||
|
},
|
||||||
|
Mode::Charge { command } => {
|
||||||
|
match command {
|
||||||
|
Command::Start => c.start(&mut ctx)?,
|
||||||
|
Command::Stop => c.stop(&mut ctx)?,
|
||||||
|
Command::Pause => c.pause(&mut ctx)?,
|
||||||
|
Command::Resume => c.resume(&mut ctx)?,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_other => {
|
||||||
|
unreachable!("Stream was already ruled out above")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_session(s: &Option<ChargingSession>) {
|
||||||
|
let Some(s) = s.as_ref() else { return };
|
||||||
|
|
||||||
|
let duration = std::time::Duration::from_secs(
|
||||||
|
s.charge_duration_in_seconds.unwrap_or(0) as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}\t{}\t{}kWh",
|
||||||
|
s.charger_id.as_deref().unwrap_or("<none>"),
|
||||||
|
humantime::format_duration(duration),
|
||||||
|
s.session_energy,
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user