Add CLI args for configurable values
parent
d168b045aa
commit
672798197b
|
@ -460,6 +460,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"ureq",
|
||||
"xflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -681,3 +682,18 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xflags"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf19f5031a1a812e96fede16f8161218883079946cea87619d3613db1efd268"
|
||||
dependencies = [
|
||||
"xflags-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xflags-macros"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2afbd7f2039bb6cad2dd45f0c5dff49c0d4e26118398768b7a605524d4251809"
|
||||
|
|
|
@ -10,6 +10,7 @@ paho-mqtt = "*"
|
|||
serde = { version = "*", features = [ "derive" ] }
|
||||
serde_json = "*"
|
||||
ureq = { version = "*", features = [ "json" ] }
|
||||
xflags = "*"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
28
README.md
28
README.md
|
@ -38,7 +38,7 @@ solax2mqtt/<adapter-serial>/SENSOR/inverter {
|
|||
|
||||
Most of the data for this project came from [`squishykid/solax`](https://github.com/squishykid/solax).
|
||||
|
||||
Configuration is currently done by editing the source code and recompiling.
|
||||
## Alternatives
|
||||
|
||||
PocketWIFI is dreadfully insecure. Use Modbus-RTU or, at the very least, a
|
||||
PocketLAN, if you can. I'll be switching as soon as I'm able to.
|
||||
|
@ -48,9 +48,29 @@ This is effectively a custom PocketWIFI module built out of an ESP8266. It's
|
|||
possible I could put one of these together and make it talk MQTT instead of
|
||||
having this separate daemon running. One for later.
|
||||
|
||||
The PocketWIFI squirts MQTTS to their cloud servers, but they don't verify TLS
|
||||
so it can be trivially intercepted. However, the messages are in a useless (to
|
||||
me) binary format.
|
||||
The PocketWIFI squirts MQTTS to their cloud servers and don't verify TLS, so it
|
||||
can be trivially intercepted. However, the messages are in a useless (to me)
|
||||
binary format.
|
||||
|
||||
## Usage
|
||||
|
||||
First, get TCP access to the PocketWIFI module. Over the AP, it's at `5.8.8.8`.
|
||||
If you tell it to connect to your WIFI network, your DHCP server will assign an
|
||||
IP. Next, run `solax2mqtt`:
|
||||
|
||||
```
|
||||
solax2mqtt --mqtt-url tcp://<host>:<port> \
|
||||
--solax-url http://<host>[:port] \
|
||||
--solax-password <admin-password>
|
||||
```
|
||||
|
||||
That's it. Errors will be outputted to the console; failure to connect to the
|
||||
Solax is not fatal (it will keep retrying). Failure to connect to the MQTT
|
||||
server is (the process will exit).
|
||||
|
||||
You can also solax2mqtt --help` to see a few other options.
|
||||
|
||||
## `mqtt2prometheus`
|
||||
|
||||
Once the metrics are in MQTT, they can be sent to prometheus using
|
||||
[`mqtt2prometheus`](https://github.com/hikhvar/mqtt2prometheus):
|
||||
|
|
50
src/main.rs
50
src/main.rs
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue