slimmemeter 0.2.1

DSMR5 serial TTY to MQTT exporter for your smart meter
use std::{path::PathBuf, time::Duration};

use clap::Parser;
use rumqttc::{AsyncClient, ConnAck, ConnectReturnCode, MqttOptions, Packet, QoS};
use tokio::task;

#[derive(Parser)]
struct Args {
    #[clap()]
    serial_path: PathBuf,
    #[clap(long, default_value = "mqtt://localhost?client_id=slimmemeter")]
    mqtt_url: String,
}

#[tokio::main]
async fn main() {
    env_logger::init();

    let args = Args::parse();

    use serial::SerialPort;

    let path = args.serial_path;
    let mut port = serial::open(&path).expect("Could not open serial TTY path");

    port.reconfigure(&|settings| {
        settings.set_baud_rate(serial::Baud115200)?;
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
        settings.set_flow_control(serial::FlowNone);
        Ok(())
    })
    .expect("Failed to configure serial TTY port");

    port.set_timeout(std::time::Duration::from_millis(11000))
        .unwrap();

    let bytes = port.bytes().map(|b| b.unwrap());

    use std::io::Read;
    let reader = dsmr5::Reader::new(bytes);

    let mut options = MqttOptions::parse_url(args.mqtt_url).unwrap();
    options.set_keep_alive(Duration::from_secs(5));

    let (client, mut eventloop) = AsyncClient::new(options, 10);
    log::info!("Connected to MQTT");

    task::spawn(async move {
        loop {
            let notification = eventloop.poll().await.unwrap();

            #[allow(clippy::single_match)]
            match notification {
                rumqttc::Event::Incoming(Packet::ConnAck(ConnAck {
                    code: ConnectReturnCode::Success,
                    ..
                })) => {
                    log::info!("Connected to MQTT");
                }
                _ => {}
            }
        }
    });

    for readout in reader {
        let telegram = readout.to_telegram().unwrap();
        let state = dsmr5::Result::<dsmr5::state::State>::from(&telegram).unwrap();

        log::info!(
            "Received P1: delivered {:?}kW",
            state.power_delivered.unwrap_or(0.)
        );

        client
            .publish(
                "slimmemeter",
                QoS::AtLeastOnce,
                false,
                serde_json::to_vec(&state).unwrap(),
            )
            .await
            .unwrap();
    }
}