Add config and control support
This commit is contained in:
parent
b6135dd717
commit
8e698e71c1
39
src/config.rs
Normal file
39
src/config.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use serde::Deserialize;
|
||||
use anyhow::Result;
|
||||
use serde_json;
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Charger {
|
||||
pub id: String,
|
||||
pub channel_id: String,
|
||||
pub owners: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Mattermost {
|
||||
pub base: String,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Prometheus {
|
||||
pub base: String
|
||||
}
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Config {
|
||||
pub easee_token_path: String,
|
||||
pub prometheus: Prometheus,
|
||||
pub mattermost: Mattermost,
|
||||
pub chargers: Vec<Charger>,
|
||||
}
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Regulator {
|
||||
power_bias_watts: f64,
|
||||
monophase_volts: f64,
|
||||
}
|
||||
|
||||
pub fn load_config(path: &str) -> Result<Config> {
|
||||
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||
}
|
121
src/control.rs
Normal file
121
src/control.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::Infallible;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::Result;
|
||||
use easee::api::{self, ChargerOpMode, Context};
|
||||
use easee::observation::{self, Event, ObservationError};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::mattermost::{self, Channel};
|
||||
use crate::prom::PromClient;
|
||||
use crate::config::{self, Config};
|
||||
|
||||
use observation::{Observation,PilotMode};
|
||||
|
||||
struct Charger {
|
||||
inner: api::Charger,
|
||||
owners: Vec<String>,
|
||||
current: Option<(f64, f64, f64)>
|
||||
}
|
||||
|
||||
impl Charger {
|
||||
pub fn from_api(inner: api::Charger, configs: &[config::Charger]) -> Self {
|
||||
let owners = configs.iter()
|
||||
.find(|c| c.id == inner.id)
|
||||
.map(|c| &c.owners)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Charger { inner, owners, current: None }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn start(mut ctx: Context, config: Config, mut chargers: Vec<api::Charger>) -> Result<Infallible> {
|
||||
|
||||
let mattermost = mattermost::Context::new(config.mattermost.base, &config.mattermost.token)?;
|
||||
let mut stream = observation::Stream::from_context(&mut ctx)?;
|
||||
|
||||
// TODO
|
||||
let chargers: HashMap<String, Charger> = chargers.into_iter()
|
||||
.filter_map(|c| {
|
||||
stream.subscribe(&c.id).ok()?;
|
||||
let name = c.name.clone();
|
||||
Some((name, Charger::from_api(c, &config.chargers)))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let chargers = Arc::new(Mutex::new(chargers));
|
||||
|
||||
// TODO get channel on a per-charger basis
|
||||
let channel = mattermost.channel("9d9o1a5qf7fofk3wqfa493gkfe");
|
||||
|
||||
|
||||
|
||||
info!("Controller started");
|
||||
mattermost.send_to_channel(&channel, "Easee Controller started")?;
|
||||
|
||||
loop {
|
||||
|
||||
let evt = match stream.recv() {
|
||||
Ok(e) => e,
|
||||
Err(ObservationError::Stream(stream_error)) => Err(stream_error)?,
|
||||
Err(other) => { error!("Cannot process message: {}", other); continue },
|
||||
};
|
||||
|
||||
let mut chargers = chargers.lock().unwrap();
|
||||
|
||||
let Some(charger) = chargers.get_mut(&evt.charger)
|
||||
else { warn!("Received message for unknown charger {}", &evt.charger); continue };
|
||||
|
||||
let result = handle_event(evt, charger, &mattermost, &channel);
|
||||
|
||||
if let Err(err) = result {
|
||||
error!("Error handling observation: {:?}", err);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(evt: Event, charger: &mut Charger, ctx: &mattermost::Context, channel: &Channel) -> Result<()> {
|
||||
|
||||
let send = |msg: &str| ctx.send_to_channel(channel, msg);
|
||||
|
||||
match evt.observation {
|
||||
Observation::PilotMode(mode) => {
|
||||
match mode {
|
||||
PilotMode::Disconnected => send("Car Disconnected"),
|
||||
PilotMode::Connected => send("Car Connected"),
|
||||
PilotMode::Charging => send("Car Charging"),
|
||||
PilotMode::NeedsVentilation => send("Car needs ventilation"),
|
||||
PilotMode::FaultDetected => send("Fault detected"),
|
||||
PilotMode::Unknown => send("Unknown"),
|
||||
}
|
||||
},
|
||||
Observation::ChargerOpMode(mode) => {
|
||||
match mode {
|
||||
ChargerOpMode::Unknown => send("Unknown"),
|
||||
ChargerOpMode::Disconnected => send("Charger disconnected"),
|
||||
ChargerOpMode::Paused => send("Charge paused"),
|
||||
ChargerOpMode::Charging => send("Charging"),
|
||||
ChargerOpMode::Finished => send("Charging finished"),
|
||||
ChargerOpMode::Error => send("Charger error"),
|
||||
ChargerOpMode::Ready => send("Charger ready"),
|
||||
}?;
|
||||
ctx.set_status(mode)
|
||||
},
|
||||
other => Ok(info!("{}: {:?}", evt.charger, other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adjust_power(prom: PromClient) -> Result<()> {
|
||||
|
||||
loop {
|
||||
let export_power = prom.current_power()?;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
16
src/main.rs
16
src/main.rs
@ -4,12 +4,14 @@ use anyhow::{Context as AnyhowContext, Result};
|
||||
use clap::ValueEnum;
|
||||
use clap::{Parser, Subcommand};
|
||||
use easee::api::{ApiError, Charger, ChargerState, ChargingSession, Context};
|
||||
use easee::{observation, stream};
|
||||
use easee::observation;
|
||||
|
||||
use tracing::info;
|
||||
|
||||
mod prom;
|
||||
mod mattermost;
|
||||
mod config;
|
||||
mod control;
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
enum Command {
|
||||
@ -38,6 +40,7 @@ enum Mode {
|
||||
},
|
||||
Stream,
|
||||
Power,
|
||||
Control,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -48,8 +51,8 @@ struct CLI {
|
||||
#[arg(short, long)]
|
||||
charger_id: Vec<String>,
|
||||
|
||||
#[arg(short, long, default_value = "http://localhost:9090")]
|
||||
prometheus: String,
|
||||
#[arg(short, long, default_value = "./config.json")]
|
||||
config: String,
|
||||
|
||||
#[command(subcommand)]
|
||||
mode: Mode,
|
||||
@ -154,6 +157,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
let mut ctx = load_context()?;
|
||||
let config = config::load_config(&args.config)?;
|
||||
|
||||
match args.mode {
|
||||
Mode::Login => login()?,
|
||||
@ -178,8 +182,12 @@ fn main() -> Result<()> {
|
||||
}
|
||||
Mode::Stream => stream(&args.charger_id)?,
|
||||
Mode::Power => {
|
||||
let pow = prom::current_power(&*args.prometheus)?;
|
||||
let pow = prom::PromClient::new(config.prometheus.base).current_power()?;
|
||||
println!("P1:{}W P2:{}W P3:{}W", pow.0, pow.1, pow.2);
|
||||
},
|
||||
Mode::Control => {
|
||||
let chargers = load_chargers(&mut ctx, &args.charger_id)?;
|
||||
control::start(ctx, config, chargers)?;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user