/*! * matrix-lolopener v0.1.0 * * Copyright (C) 2026 The 1312 Media Collective * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ use rumqttc::{ MqttOptions, AsyncClient, EventLoop, Event, Incoming, QoS, }; use std::{ time::Duration, fmt, }; use serde::Deserialize; use tracing::{ info, debug }; use crate::config::MQTTConfig; #[derive(Deserialize, Debug)] pub struct StatusMessage { pub contact: bool, } pub struct MQTT { config: MQTTConfig, client: AsyncClient, eventloop: EventLoop, } impl MQTT { pub async fn new(config: MQTTConfig) -> Self { let mut mqttoptions = MqttOptions::new(config.id.clone(), config.host.clone(), config.port); mqttoptions.set_keep_alive(Duration::from_secs(5)); let (client, eventloop) = AsyncClient::new(mqttoptions, 1); MQTT { config, client, eventloop, } } pub async fn subscribe(&mut self, callback: F) -> Result<()> { self.client.subscribe(self.config.topic.clone(), QoS::AtMostOnce).await?; loop { let notification = self.eventloop.poll().await?; debug!("Notification = {:?}", notification); if let Event::Incoming(Incoming::Publish(p)) = notification { let msg: StatusMessage = serde_json::from_slice(p.payload.as_ref())?; info!("Message: {:?}", msg); callback(msg); } } #[allow(unreachable_code)] Ok(()) } } #[derive(Debug)] #[allow(dead_code)] // we use Debug, so inner fields are never read pub enum Error { MQTTClient(rumqttc::ClientError), MQTTConnection(rumqttc::ConnectionError), InvalidFormat(serde_json::Error), } impl From for Error { fn from(e: rumqttc::ClientError) -> Self { Self::MQTTClient(e) } } impl From for Error { fn from(e: rumqttc::ConnectionError) -> Self { Self::MQTTConnection(e) } } impl From for Error { fn from(e: serde_json::Error) -> Self { Self::InvalidFormat(e) } } impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> { write!(fmt, "{self:?}") } } pub type Result = std::result::Result;