|
|
|
@ -2,11 +2,9 @@ extern crate paho_mqtt as mqtt;
|
|
|
|
|
extern crate serde;
|
|
|
|
|
extern crate serde_json;
|
|
|
|
|
extern crate ureq;
|
|
|
|
|
extern crate xflags;
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
result::Result,
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
use std::{result::Result, time::Duration};
|
|
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
@ -42,13 +40,6 @@ impl From<std::io::Error> for Error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Change these for your situation
|
|
|
|
|
const DFLT_BROKER: &str = "tcp://10.0.0.1:1883";
|
|
|
|
|
const DFLT_CLIENT: &str = "solax2mqtt";
|
|
|
|
|
const QOS: i32 = 1;
|
|
|
|
|
const HTTP_ENDPOINT: &str = "http://5.8.8.8";
|
|
|
|
|
const HTTP_BODY: &str = "optType=ReadRealTimeData&pwd=SXxxxxxxxx";
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
struct SolaxResponse {
|
|
|
|
|
sn: String,
|
|
|
|
@ -187,10 +178,10 @@ impl TryFrom<SolaxResponse> for MQTTData {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mqtt_connect() -> Result<mqtt::Client, mqtt::Error> {
|
|
|
|
|
fn mqtt_connect(uri: String, client_id: String) -> Result<mqtt::Client, mqtt::Error> {
|
|
|
|
|
let mqtt_opts = mqtt::CreateOptionsBuilder::new()
|
|
|
|
|
.server_uri(DFLT_BROKER.to_string())
|
|
|
|
|
.client_id(DFLT_CLIENT.to_string())
|
|
|
|
|
.server_uri(uri)
|
|
|
|
|
.client_id(client_id)
|
|
|
|
|
.finalize();
|
|
|
|
|
|
|
|
|
|
// Create a client.
|
|
|
|
@ -212,19 +203,20 @@ fn mqtt_connect() -> Result<mqtt::Client, mqtt::Error> {
|
|
|
|
|
return Ok(mqtt_client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn gather() -> Result<MQTTData, Error> {
|
|
|
|
|
let response = ureq::post(HTTP_ENDPOINT).send_string(HTTP_BODY)?;
|
|
|
|
|
fn gather(uri: &str, password: &str) -> Result<MQTTData, Error> {
|
|
|
|
|
let body = format!("optType=ReadRealTimeData&pwd={}", password);
|
|
|
|
|
let response = ureq::post(uri).send_string(&body)?;
|
|
|
|
|
let solax_response: SolaxResponse = response.into_json()?;
|
|
|
|
|
|
|
|
|
|
solax_response.try_into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn publish(mqtt_client: &mqtt::Client, data: MQTTData) -> Option<Error> {
|
|
|
|
|
fn publish(mqtt_client: &mqtt::Client, data: MQTTData, qos: i32) -> Option<Error> {
|
|
|
|
|
let content = serde_json::to_string(&data).ok()?;
|
|
|
|
|
let msg = mqtt::Message::new(
|
|
|
|
|
format!("solax2mqtt/{}/SENSOR/inverter", data.inverter.adapter_sn),
|
|
|
|
|
content,
|
|
|
|
|
QOS,
|
|
|
|
|
qos,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Err(e) = mqtt_client.publish(msg) {
|
|
|
|
@ -235,12 +227,28 @@ fn publish(mqtt_client: &mqtt::Client, data: MQTTData) -> Option<Error> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let mqtt_client = mqtt_connect().unwrap();
|
|
|
|
|
let flags = xflags::parse_or_exit! {
|
|
|
|
|
/// URL of the MQTT server to publish to, e.g. tcp://10.0.0.1:1883
|
|
|
|
|
required --mqtt-url url: String
|
|
|
|
|
/// Client ID to use when connecting to the MQTT server. Defaults to solax2mqtt
|
|
|
|
|
optional --mqtt-client-id id: String
|
|
|
|
|
/// QOS class to publish the messages as. Defaults to 1
|
|
|
|
|
optional --mqtt-qos qos: i32
|
|
|
|
|
/// URL of the Solax PocketWIFI adapter. Defaults to http://5.8.8.8
|
|
|
|
|
optional --solax-url url: String
|
|
|
|
|
/// Admin password of the PocketWIFI adapter. Often its serial number
|
|
|
|
|
required --solax-password password: String
|
|
|
|
|
};
|
|
|
|
|
let solax_url = flags.solax_url.unwrap_or("http://5.8.8.8".to_string());
|
|
|
|
|
let mqtt_client_id = flags.mqtt_client_id.unwrap_or("solax2mqtt".to_string());
|
|
|
|
|
let mqtt_qos = flags.mqtt_qos.unwrap_or(1);
|
|
|
|
|
|
|
|
|
|
let mqtt_client = mqtt_connect(flags.mqtt_url, mqtt_client_id).unwrap();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
match gather() {
|
|
|
|
|
match gather(&solax_url, &flags.solax_password) {
|
|
|
|
|
Ok(msg) => {
|
|
|
|
|
if let Some(e) = publish(&mqtt_client, msg) {
|
|
|
|
|
if let Some(e) = publish(&mqtt_client, msg, mqtt_qos) {
|
|
|
|
|
println!("Failed to publish data to MQTT, exiting: {:?}", e);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|