From 1a4694bad569df35a1aa1361936d2f64e5d6504a Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 2 Feb 2024 15:27:46 +0000 Subject: [PATCH] Add API struct --- src/fronious.rs | 108 +++++++++++++++++++++++++++++++++++------------- src/main.rs | 10 ++++- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/fronious.rs b/src/fronious.rs index bdccd6d..63d71c8 100644 --- a/src/fronious.rs +++ b/src/fronious.rs @@ -1,6 +1,7 @@ +use reqwest::{blocking::Client, Url}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::{collections::HashMap, net::IpAddr}; +use std::{borrow::Borrow, collections::HashMap, net::IpAddr}; use thiserror::Error; #[derive(Debug, Error)] @@ -17,6 +18,80 @@ pub enum Error { Response(Status), } +pub struct Fronius { + client: Client, + base_url: Url, +} + +impl Fronius { + pub fn connect(ip: IpAddr) -> Result { + let client = Client::new(); + + let mut url = reqwest::Url::parse("http://placeholder.local/solar_api/GetAPIVersion.cgi") + .expect("Initial base URL should be valid"); + url.set_ip_host(ip) + .expect("Base URL should be a valid base"); + let api_version: ApiVersion = client.get(url.clone()).send()?.json()?; + + if api_version.api_version != 1 { + return Err(Error::UnsupportedApiVersion(api_version.api_version)); + } + + url.set_path(&api_version.base_url); + + Ok(Self { + client, + base_url: url, + }) + } + + fn make_request_inner(&self, url: Url) -> Result { + let response: FroniousResponse = self.client.get(url).send()?.json()?; + + if response.head.status.code != StatusCode::Okay { + return Err(Error::Response(response.head.status)); + } + + Ok(response.body) + } + + pub fn make_request(&self, endpoint: &str, params: I) -> Result + where + T: DeserializeOwned, + I: IntoIterator, + I::Item: Borrow<(K, V)>, + K: AsRef, + V: AsRef, + { + let mut url = self + .base_url + .join(endpoint) + .map_err(|_e| Error::InvalidEndpoint(endpoint.to_string()))?; + url.query_pairs_mut().extend_pairs(params); + let body = self.make_request_inner(url)?; + + Ok(T::deserialize(body)?) + } + + pub fn get_inverter_realtime_data_device( + &self, + device_id: DeviceId, + ) -> Result { + let device_id = u8::from(device_id).to_string(); + + let response: CommonResponseBody<_> = self.make_request( + "GetInverterRealtimeData.cgi", + [ + ("Scope", "Device"), + ("DeviceId", &device_id), + ("DataCollection", C::param_value()), + ], + )?; + + Ok(response.data) + } +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct FroniousResponse { @@ -24,7 +99,7 @@ pub struct FroniousResponse { body: T, } -#[derive(Debug, Serialize_repr, Deserialize_repr)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)] #[repr(u8)] pub enum StatusCode { Okay = 0, @@ -82,20 +157,14 @@ pub struct UnitAndValues { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub struct ApiVersion { +struct ApiVersion { #[serde(rename = "APIVersion")] - api_version: i8, + api_version: u64, #[serde(rename = "BaseURL")] base_url: String, compatibility_range: String, } -pub fn get_api_version(ip: IpAddr) -> Result> { - let mut url = reqwest::Url::parse("http://placeholder.local/solar_api/GetAPIVersion.cgi")?; - let _ = url.set_ip_host(ip); - Ok(reqwest::blocking::Client::new().get(url).send()?.json()?) -} - pub struct DeviceId(u8); #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] @@ -141,22 +210,3 @@ impl DataCollection for CumulationInverterData { "CumulationInverterData" } } - -pub fn get_inverter_realtime_data_device( - ip: IpAddr, - device_id: DeviceId, -) -> Result>, Box> { - let device_id = u8::from(device_id).to_string(); - let params = [ - ("Scope", "Device"), - ("DeviceId", &device_id), - ("DataCollection", C::param_value()), - ]; - - let mut url = reqwest::Url::parse_with_params( - "http://placeholder.local/solar_api/v1/GetInverterRealtimeData.cgi", - ¶ms, - )?; - let _ = url.set_ip_host(ip); - Ok(reqwest::blocking::Client::new().get(url).send()?.json()?) -} diff --git a/src/main.rs b/src/main.rs index 5b2d5f0..0857475 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,17 @@ use std::net::IpAddr; + +use fronious::{CumulationInverterData, DeviceId, Fronius}; mod fronious; fn main() -> Result<(), Box> { let ip = IpAddr::V4(std::net::Ipv4Addr::new(10, 69, 0, 50)); - println!("{:#?}", fronious::get_api_version(ip)?); + let fronius = Fronius::connect(ip)?; + println!( + "{:?}", + fronius.get_inverter_realtime_data_device::( + DeviceId::try_from(0).unwrap(), + )? + ); //println!("{:#?}", fronious::get_inverter_realtime_data(ip, fronious::Scope::System)?); Ok(()) }