Compare commits
No commits in common. "3d342892f1e1c2dc6afb5a2640818b93b1e330c5" and "b72e0d956ea7815fdca494c96cd378863667d09c" have entirely different histories.
3d342892f1
...
b72e0d956e
@ -10,7 +10,6 @@ authors = ["Maxime Augier <max@xolus.net>"]
|
|||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = "1.0.121"
|
serde_json = "1.0.121"
|
||||||
serde_repr = "0.1.19"
|
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
ureq = { version = "2.10.0", features = ["json"] }
|
ureq = { version = "2.10.0", features = ["json"] }
|
||||||
|
25
README.md
25
README.md
@ -1,25 +0,0 @@
|
|||||||
# Easee-rs - Bindings for the Easee.com Cloud API.
|
|
||||||
|
|
||||||
Work in progress.
|
|
||||||
|
|
||||||
## Features and Todo
|
|
||||||
|
|
||||||
- Authn/z
|
|
||||||
- [x] Token authentication
|
|
||||||
- [ ] Persistence of tokens
|
|
||||||
- Core functionality
|
|
||||||
- [x] Enumerate sites and chargers
|
|
||||||
- [x] Read energy meter
|
|
||||||
- [x] Read charger status
|
|
||||||
- [ ] Control charging (start/pause/resume/stop)
|
|
||||||
- [ ] Control dynamic current limits
|
|
||||||
- Ergonomics
|
|
||||||
- [ ] Enums for protocol constants
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: https://easee.com
|
|
87
src/api.rs
87
src/api.rs
@ -1,7 +1,7 @@
|
|||||||
use std::{io, time::{Duration, Instant}};
|
use std::{io, time::{Duration, Instant}};
|
||||||
|
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
|
||||||
use serde_repr::Deserialize_repr;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, info, instrument};
|
use tracing::{debug, info, instrument};
|
||||||
|
|
||||||
@ -15,33 +15,13 @@ pub struct Context {
|
|||||||
const API_BASE: &'static str = "https://api.easee.com/api/";
|
const API_BASE: &'static str = "https://api.easee.com/api/";
|
||||||
const REFRESH_TOKEN_DELAY: Duration = Duration::from_secs(600);
|
const REFRESH_TOKEN_DELAY: Duration = Duration::from_secs(600);
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NaiveDateTime(pub chrono::NaiveDateTime);
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for NaiveDateTime {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>
|
fn parse_iso8601<'d, D: Deserializer<'d>>(de: D) -> Result<NaiveDateTime, D::Error> {
|
||||||
{
|
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
let s = <&str as Deserialize>::deserialize(d)?;
|
let s = <&str as Deserialize>::deserialize(de)?;
|
||||||
let dt = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
|
Ok(NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
|
||||||
.map_err(D::Error::custom)?;
|
.map_err(D::Error::custom)?)
|
||||||
Ok(NaiveDateTime(dt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UtcDateTime(pub chrono::DateTime<chrono::Utc>);
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for UtcDateTime {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error>
|
|
||||||
{
|
|
||||||
use serde::de::Error;
|
|
||||||
let s = <&str as Deserialize>::deserialize(d)?;
|
|
||||||
let dt = chrono::DateTime::parse_from_str(s, "%+")
|
|
||||||
.map_err(D::Error::custom)?
|
|
||||||
.to_utc();
|
|
||||||
Ok(UtcDateTime(dt))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@ -51,26 +31,20 @@ pub struct Charger {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub product_code: u32,
|
pub product_code: u32,
|
||||||
pub color: Option<i32>,
|
pub color: Option<i32>,
|
||||||
|
#[serde(deserialize_with="parse_iso8601")]
|
||||||
pub created_on: NaiveDateTime,
|
pub created_on: NaiveDateTime,
|
||||||
|
|
||||||
|
#[serde(deserialize_with="parse_iso8601")]
|
||||||
pub updated_on: NaiveDateTime,
|
pub updated_on: NaiveDateTime,
|
||||||
pub level_of_access: u32,
|
pub level_of_access: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize_repr, Debug)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum ChargerOpMode {
|
|
||||||
Zero = 0,
|
|
||||||
One = 1,
|
|
||||||
Paused = 2,
|
|
||||||
Charging = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[serde(rename_all="camelCase")]
|
#[serde(rename_all="camelCase")]
|
||||||
pub struct ChargerState {
|
pub struct ChargerState {
|
||||||
pub smart_charging: bool,
|
pub smart_charging: bool,
|
||||||
pub cable_locked: bool,
|
pub cable_locked: bool,
|
||||||
pub charger_op_mode: ChargerOpMode,
|
pub charger_op_mode: u32,
|
||||||
pub total_power: f64,
|
pub total_power: f64,
|
||||||
pub session_energy: f64,
|
pub session_energy: f64,
|
||||||
pub energy_per_hour: f64,
|
pub energy_per_hour: f64,
|
||||||
@ -88,7 +62,8 @@ pub struct ChargerState {
|
|||||||
pub dynamic_circuit_current_p2: u32,
|
pub dynamic_circuit_current_p2: u32,
|
||||||
pub dynamic_circuit_current_p3: u32,
|
pub dynamic_circuit_current_p3: u32,
|
||||||
|
|
||||||
pub latest_pulse: UtcDateTime,
|
//#[serde(deserialize_with="parse_iso8601")]
|
||||||
|
//pub latest_pulse: NaiveDateTime,
|
||||||
pub charger_firmware: u32,
|
pub charger_firmware: u32,
|
||||||
pub voltage: f64,
|
pub voltage: f64,
|
||||||
|
|
||||||
@ -140,28 +115,6 @@ pub struct ChargerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Deserialize)]
|
#[derive(Debug,Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ChargingSession {
|
|
||||||
pub charger_id: Option<String>,
|
|
||||||
pub session_energy: f64,
|
|
||||||
//pub session_start: Option<NaiveDateTime>,
|
|
||||||
//pub session_stop: Option<NaiveDateTime>,
|
|
||||||
pub session_id: Option<i32>,
|
|
||||||
pub charge_duration_in_seconds: Option<u32>,
|
|
||||||
//pub first_energy_transfer_period_start: Option<NaiveDateTime>,
|
|
||||||
//pub last_energy_transfer_period_end: Option<NaiveDateTime>,
|
|
||||||
#[serde(rename = "pricePrKwhIncludingVat")]
|
|
||||||
pub price_per_kwh_including_vat: Option<f64>,
|
|
||||||
pub price_per_kwh_excluding_vat: Option<f64>,
|
|
||||||
pub vat_percentage: Option<f64>,
|
|
||||||
pub currency_id: Option<String>,
|
|
||||||
pub cost_including_vat: Option<f64>,
|
|
||||||
pub cost_excluding_vat: Option<f64>,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug,Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -296,14 +249,6 @@ impl Context {
|
|||||||
Ok(resp.into_json_with_error()?)
|
Ok(resp.into_json_with_error()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_get<T: DeserializeOwned>(&mut self, path: &str) -> Result<Option<T>, ApiError> {
|
|
||||||
match self.get(path) {
|
|
||||||
Ok(r) => Ok(Some(r)),
|
|
||||||
Err(ApiError::Ureq(ureq::Error::Status(404, _))) => Ok(None),
|
|
||||||
Err(other) => Err(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post<T: DeserializeOwned, P: Serialize>(&mut self, path: &str, params: &P) -> Result<T, ApiError> {
|
fn post<T: DeserializeOwned, P: Serialize>(&mut self, path: &str, params: &P) -> Result<T, ApiError> {
|
||||||
self.check_expired()?;
|
self.check_expired()?;
|
||||||
let url: String = format!("{}{}", API_BASE, path);
|
let url: String = format!("{}{}", API_BASE, path);
|
||||||
@ -346,12 +291,4 @@ impl Charger {
|
|||||||
let url = format!("chargers/{}/state", self.id);
|
let url = format!("chargers/{}/state", self.id);
|
||||||
ctx.get(&url)
|
ctx.get(&url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ongoing_session(&self, ctx: &mut Context) -> Result<Option<ChargingSession>, ApiError> {
|
|
||||||
ctx.maybe_get(&format!("chargers/{}/sessions/ongoing", &self.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn latest_session(&self, ctx: &mut Context) -> Result<Option<ChargingSession>, ApiError> {
|
|
||||||
ctx.maybe_get(&format!("chargers/{}/sessions/latest", &self.id))
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user