const COMPANY_KEY: &str = "bnrl_frRFjEz8Mkn";
const BASE_URL: &str = "https://web.shinemonitor.com/public";
use crate::response::auth::{Auth, AuthDat};
use crate::response::pid::Pid;
use crate::response::ping::SimplePing;
use crate::response::plant::Plant;
use crate::response::solar::Solar;
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use reqwest::Error;
use serde::de::DeserializeOwned;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct ShineMonitorClient {
host: &'static str,
}
fn get_salt() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Error gettting salt(system time)")
.as_secs()
}
fn gen_sha1(input: String) -> String {
let mut sh = Sha1::new();
sh.input_str(input.as_str());
sh.result_str()
}
impl Default for ShineMonitorClient {
fn default() -> Self {
ShineMonitorClient::new(BASE_URL)
}
}
impl ShineMonitorClient {
pub fn new(host: &'static str) -> Self {
ShineMonitorClient { host }
}
async fn get<R: DeserializeOwned>(&self, parameters: &str) -> Result<R, Error> {
reqwest::get(format!("{}{}", self.host, parameters))
.await?
.json()
.await
}
pub async fn ping(&self) -> Result<SimplePing, Error> {
self.get("/").await
}
fn auth_url(&self, user: &str, pass: &str) -> String {
let (salt, action, passsha1): (u64, String, String) = (
get_salt(),
format!("&action=auth&usr={}&company-key={}", user, COMPANY_KEY),
gen_sha1(pass.to_string()),
);
let sign = gen_sha1(format!("{}{}{}", salt, passsha1, action));
format!("/?sign={}&salt={}{}", sign, salt, action)
}
pub async fn auth(&self, user: &str, pass: &str) -> Result<Auth, Error> {
self.get(self.auth_url(user, pass).as_str()).await
}
}
impl Auth {
fn pid_url(&self) -> String {
let (sal, auth): (u64, &AuthDat) = (get_salt(), &self.dat);
let (token, secret): (String, String) = (auth.clone().token, auth.clone().secret);
let action = "&action=queryPlantsInfo&i18n=pt_BR&lang=pt_BR";
let sign = gen_sha1(format!("{}{}{}{}", sal, secret, token, action));
format!(
"{}/?sign={}&salt={}&token={}{}",
BASE_URL, sign, sal, token, action
)
}
pub async fn pid(&self) -> Result<Pid, Error> {
reqwest::get(self.pid_url().as_str()).await?.json().await
}
async fn plant_url(&self) -> String {
let (sal, auth): (u64, &AuthDat) = (get_salt(), &self.dat);
let (token, secret): (String, String) = (auth.clone().token, auth.clone().secret);
let pid = self
.pid()
.await
.expect("Failed to get Pid")
.dat
.info
.first()
.expect("Unable to get first plant")
.pid;
let action = format!(
"&action=queryPlantInfo&plantid={}&i18n=pt_BR&lang=pt_BR",
pid
);
let sign = gen_sha1(format!("{}{}{}{}", sal, secret, token, action));
format!(
"{}/?sign={}&salt={}&token={}{}",
BASE_URL, sign, sal, token, action
)
}
pub async fn plant(&self) -> Result<Plant, Error> {
let plant_url = self.plant_url().await;
let res = reqwest::get(plant_url).await?.json().await;
res
}
async fn solar_url(&self) -> String {
let (sal, auth): (u64, &AuthDat) = (get_salt(), &self.dat);
let (token, secret): (String, String) = (auth.clone().token, auth.clone().secret);
let pid = self
.pid()
.await
.expect("faield to get pid")
.dat
.info
.first()
.expect("failed to get first plant")
.pid;
let action = format!("&action=queryPlantCurrentData&plantid={}&par=ENERGY_TODAY,ENERGY_MONTH,ENERGY_YEAR,ENERGY_TOTAL,ENERGY_PROCEEDS,ENERGY_CO2,CURRENT_TEMP,CURRENT_RADIANT,BATTERY_SOC,ENERGY_COAL,ENERGY_SO2",pid);
let sign = gen_sha1(format!(
"{}{}{}{}",
sal,
secret.as_str(),
token.as_str(),
action
));
format!(
"{}/?sign={}&salt={}&token={}{}",
BASE_URL,
sign.as_str(),
sal,
token.as_str(),
action
)
}
pub async fn solar(&self) -> Result<Solar, Error> {
let url = self.solar_url().await;
reqwest::get(url)
.await
.expect("Could not get solar data")
.json()
.await
}
}