Start midea backend
This commit is contained in:
parent
f8ef41f59d
commit
aff80fa0f7
3 changed files with 154 additions and 2 deletions
61
src/db.rs
61
src/db.rs
|
@ -1,7 +1,7 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use rusqlite::{Connection, ToSql};
|
||||
use rusqlite::{Connection, OptionalExtension, ToSql};
|
||||
|
||||
use crate::devices::{Devices, DevicesWithName};
|
||||
|
||||
|
@ -54,6 +54,16 @@ impl DataBase {
|
|||
[],
|
||||
)?;
|
||||
|
||||
self.sql.execute(
|
||||
"CREATE TABLE IF NOT EXISTS credentials (
|
||||
id INTEGER PRIMARY KEY,
|
||||
key VARCHAR(60) NOT NULL,
|
||||
device_id BIGINT NOT NULL,
|
||||
cred VARCHAR(256) NOT NULL
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -202,6 +212,55 @@ impl DataBase {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn write_credential(&self, key: &str, device_id: u64, credential: &str) -> Result<()> {
|
||||
if self
|
||||
.sql
|
||||
.prepare(&format!(
|
||||
"
|
||||
SELECT cred
|
||||
FROM credentials
|
||||
WHERE key=\"{key}\" AND device_id={device_id}
|
||||
"
|
||||
))?
|
||||
.exists([])?
|
||||
{
|
||||
self.sql.execute(
|
||||
&format!(
|
||||
"
|
||||
UPDATE credentials
|
||||
SET cred=\"{credential}\"
|
||||
WHERE key=\"{key}\" AND device_id={device_id}
|
||||
"
|
||||
),
|
||||
[],
|
||||
)?;
|
||||
} else {
|
||||
self.sql.execute(
|
||||
&format!(
|
||||
"INSERT INTO crendetials (key, device_id, cred)
|
||||
VALUES (\"{key}\", {device_id}, \"{credential}\")"
|
||||
),
|
||||
[],
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_credential(&self, key: &str, device_id: u64) -> Result<Option<String>> {
|
||||
Ok(self
|
||||
.sql
|
||||
.prepare(&format!(
|
||||
"
|
||||
SELECT cred
|
||||
FROM credentials
|
||||
WHERE key=\"{key}\" AND device_id={device_id}
|
||||
"
|
||||
))?
|
||||
.query_row([], |row| Ok(row.get(0)?))
|
||||
.optional()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -9,6 +9,7 @@ use crate::{db::DataBase, web_server::plug_data_range};
|
|||
mod data;
|
||||
mod db;
|
||||
mod devices;
|
||||
mod midea_helper;
|
||||
mod tasmota;
|
||||
mod web_server;
|
||||
|
||||
|
@ -17,6 +18,7 @@ use actix_web::{web::Data, App, HttpServer};
|
|||
use anyhow::Result;
|
||||
use devices::Devices;
|
||||
use futures::{future::try_join_all, try_join, Future};
|
||||
use midea_helper::MideaDishwasher;
|
||||
use tasmota::Tasmota;
|
||||
use web_server::*;
|
||||
|
||||
|
@ -50,6 +52,7 @@ async fn run_web_server(
|
|||
devices: Devices,
|
||||
plugs: Vec<Tasmota>,
|
||||
db: Arc<Mutex<DataBase>>,
|
||||
dishwasher: Vec<Arc<MideaDishwasher>>,
|
||||
) -> Result<()> {
|
||||
const IP: &str = "0.0.0.0";
|
||||
const PORT: u16 = 8062;
|
||||
|
@ -61,6 +64,7 @@ async fn run_web_server(
|
|||
.app_data(Data::new(devices.clone()))
|
||||
.app_data(Data::new(db.clone()))
|
||||
.app_data(Data::new(plugs.clone()))
|
||||
.app_data(Data::new(dishwasher.clone()))
|
||||
.service(Files::new("/images", "resources/images/").show_files_listing())
|
||||
.service(Files::new("/css", "resources/css").show_files_listing())
|
||||
.service(Files::new("/js", "resources/js").show_files_listing())
|
||||
|
@ -96,9 +100,15 @@ async fn main() -> Result<()> {
|
|||
.map(|(plug, _)| Tasmota::new(plug))
|
||||
.collect();
|
||||
|
||||
let dishwasher = MideaDishwasher::create(shared_db.clone())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| Arc::new(d))
|
||||
.collect();
|
||||
|
||||
try_join!(
|
||||
read_power_usage(tasmota_plugs.clone(), shared_db.clone()),
|
||||
run_web_server(devices, tasmota_plugs, shared_db)
|
||||
run_web_server(devices, tasmota_plugs, shared_db, dishwasher)
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
|
83
src/midea_helper.rs
Normal file
83
src/midea_helper.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use midea::*;
|
||||
|
||||
use crate::db::DataBase;
|
||||
|
||||
enum LoginInfo {
|
||||
Cloud { mail: String, password: String },
|
||||
Token { token: String, key: String },
|
||||
}
|
||||
|
||||
impl LoginInfo {
|
||||
const MIDEA_KEY_EMAIL: &str = "midea_cloud_mail";
|
||||
const MIDEA_KEY_PW: &str = "midea_cloud_pw";
|
||||
const MIDEA_KEY_TOKEN: &str = "midea_token";
|
||||
const MIDEA_KEY_KEY: &str = "midea_key";
|
||||
|
||||
fn new(db: &Arc<Mutex<DataBase>>, device_id: u64) -> Result<LoginInfo> {
|
||||
let db_lock = db.lock().unwrap();
|
||||
|
||||
let token = db_lock.read_credential(Self::MIDEA_KEY_TOKEN, device_id)?;
|
||||
let key = db_lock.read_credential(Self::MIDEA_KEY_KEY, device_id)?;
|
||||
|
||||
if token.is_none() || key.is_none() {
|
||||
let mail = db_lock.read_credential(Self::MIDEA_KEY_EMAIL, device_id)?;
|
||||
let pw = db_lock.read_credential(Self::MIDEA_KEY_PW, device_id)?;
|
||||
|
||||
if mail.is_none() || pw.is_none() {
|
||||
bail!("missing credentials");
|
||||
}
|
||||
|
||||
Ok(LoginInfo::Cloud {
|
||||
mail: mail.unwrap(),
|
||||
password: pw.unwrap(),
|
||||
})
|
||||
} else {
|
||||
Ok(LoginInfo::Token {
|
||||
token: token.unwrap(),
|
||||
key: key.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MideaDishwasher {
|
||||
device_info: DeviceInfo,
|
||||
device: Device,
|
||||
}
|
||||
|
||||
impl MideaDishwasher {
|
||||
pub async fn create(db: Arc<Mutex<DataBase>>) -> Result<Vec<Self>> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
for device_info in Startup::discover()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|device_info| device_info.device_type == 0xE1)
|
||||
{
|
||||
if let Ok(res) = LoginInfo::new(&db, device_info.id) {
|
||||
let (token, key) = match res {
|
||||
LoginInfo::Cloud { mail, password } => {
|
||||
let mut cloud = Cloud::new(mail, password)?;
|
||||
|
||||
cloud.login().await?;
|
||||
|
||||
cloud.keys(device_info.id).await?
|
||||
}
|
||||
LoginInfo::Token { token, key } => (token, key),
|
||||
};
|
||||
|
||||
let device = Device::connect(device_info.clone(), &token, &key).await?;
|
||||
|
||||
v.push(Self {
|
||||
device_info,
|
||||
device,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue