Add tibber

This commit is contained in:
hodasemi 2023-10-16 13:37:31 +02:00
parent abe8b2a91e
commit 6783b914d5
5 changed files with 211 additions and 2 deletions

26
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'home_server'",
"cargo": {
"args": [
"build",
"--bin=home_server",
"--package=home_server"
],
"filter": {
"name": "home_server",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

View file

@ -12,4 +12,5 @@ reqwest = "0.11.20"
serde = { version="1.0", features = ["derive"] } serde = { version="1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
futures = "0.3.28" futures = "0.3.28"
tokio = { version="1.32.0", features=["macros", "rt-multi-thread"] } tokio = { version="1.32.0", features=["macros", "rt-multi-thread"] }
tibber = "0.5.0"

View file

@ -1,4 +1,5 @@
use std::{ use std::{
fs,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread, thread,
time::{Duration, SystemTime, UNIX_EPOCH}, time::{Duration, SystemTime, UNIX_EPOCH},
@ -9,11 +10,14 @@ use crate::db::DataBase;
mod db; mod db;
mod devices; mod devices;
mod tasmota; mod tasmota;
mod tibber;
use ::tibber::TimeResolution::Daily;
use anyhow::Result; use anyhow::Result;
use devices::Devices; use devices::Devices;
use futures::{future::try_join_all, try_join}; use futures::{future::try_join_all, try_join};
use tasmota::Tasmota; use tasmota::Tasmota;
use tibber::TibberHandler;
fn since_epoch() -> Result<u64> { fn since_epoch() -> Result<u64> {
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()) Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
@ -23,8 +27,13 @@ fn since_epoch() -> Result<u64> {
async fn main() -> Result<()> { async fn main() -> Result<()> {
let db_future = DataBase::new("home_server.db"); let db_future = DataBase::new("home_server.db");
let devices_future = Devices::read("devices.conf"); let devices_future = Devices::read("devices.conf");
let tibber_future = TibberHandler::new(fs::read_to_string("tibber_token.txt")?);
let (db, devices) = try_join!(db_future, devices_future)?; let (db, devices, tibber) = try_join!(db_future, devices_future, tibber_future)?;
let prices_today = tibber.prices_today().await?;
let prices_tomorrow = tibber.prices_tomorrow().await?;
let consumption = tibber.consumption(Daily, 1).await?;
let shared_db = Arc::new(Mutex::new(db)); let shared_db = Arc::new(Mutex::new(db));
let tasmota_plugs: Vec<Tasmota> = devices let tasmota_plugs: Vec<Tasmota> = devices

172
src/tibber.rs Normal file
View file

@ -0,0 +1,172 @@
use std::sync::Arc;
use anyhow::{Error, Result};
use tibber::{Consumption, HomeId, House, PriceInfo, TibberSession, TimeResolution, User};
pub struct TibberHandler {
session: Arc<TibberSession>,
pub user: User,
pub homes: Vec<(HomeId, House)>,
}
impl TibberHandler {
pub async fn new(token: impl ToString + Send + 'static) -> Result<Self> {
tokio::task::spawn_blocking(move || {
let session = Arc::new(TibberSession::new(token.to_string()));
let user = session.get_user().map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting user information: {err:?}"
))
})?;
let mut homes = Vec::new();
for home_id in user.homes.clone().into_iter() {
let house = session.get_home(&home_id).map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting house information: {err:?}"
))
})?;
homes.push((home_id, house));
}
Ok(Self {
homes,
user,
session,
})
})
.await?
}
async fn get_data<F, T>(&self, f: F) -> Result<Vec<(House, T)>>
where
F: Fn(&TibberSession, &HomeId) -> Result<T> + Send + Sync + Copy + 'static,
T: Send + Sync + 'static,
{
let mut v = Vec::new();
for (home_id, house) in self.homes.iter() {
v.push((
house.clone(),
tokio::task::spawn_blocking({
let session = self.session.clone();
let home_id = home_id.clone();
move || f(&session, &home_id)
})
.await??,
));
}
Ok(v)
}
pub async fn current_prices(&self) -> Result<Vec<(House, PriceInfo)>> {
let mut v = Vec::new();
for (home_id, house) in self.homes.iter() {
v.push((
house.clone(),
tokio::task::spawn_blocking(move || {
self.session.get_current_price(home_id).map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting current price: {err:?}"
))
})
})
.await?,
));
}
Ok(v)
}
pub async fn prices_today(&self) -> Result<Vec<(House, Vec<PriceInfo>)>> {
let mut v = Vec::new();
for (home_id, house) in self.homes.iter() {
v.push((
house.clone(),
tokio::task::spawn_blocking(move || {
self.session.get_prices_today(home_id).map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting prices of today: {err:?}"
))
})
})
.await?,
));
}
Ok(v)
}
pub async fn prices_tomorrow(&self) -> Result<Vec<(House, Vec<PriceInfo>)>> {
let mut v = Vec::new();
for (home_id, house) in self.homes.iter() {
v.push((
house.clone(),
tokio::task::spawn_blocking(move || {
self.session.get_prices_tomorrow(home_id).map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting prices for tomorrow: {err:?}"
))
})
})
.await?,
));
}
Ok(v)
}
pub async fn consumption(
&self,
resolution: TimeResolution,
last: u32,
) -> Result<Vec<(House, Vec<Consumption>)>> {
let mut v = Vec::new();
for (home_id, house) in self.homes.iter() {
v.push((
house.clone(),
tokio::task::spawn_blocking({
let session = self.session.clone();
let home_id = home_id.clone();
move || {
session
.get_consuption(&home_id, resolution, last)
.map_err(|err| {
Error::msg(format!(
"TibberHandler: failed getting consumption: {err:?}"
))
})
}
})
.await??,
));
}
Ok(v)
}
}
#[cfg(test)]
mod test {
use super::TibberHandler;
use anyhow::Result;
use std::fs;
#[tokio::test]
async fn test_connection() -> Result<()> {
TibberHandler::new(fs::read_to_string("tibber_token.txt")?).await?;
Ok(())
}
}

1
tibber_token.txt Normal file
View file

@ -0,0 +1 @@
dkvF6ax77CP4v9-sxYyjWUD-9NunpzVPRYHfLJ8a9ps