120 lines
3.0 KiB
Rust
120 lines
3.0 KiB
Rust
/*!
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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<F: Fn(StatusMessage)>(&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<rumqttc::ClientError> for Error {
|
|
fn from(e: rumqttc::ClientError) -> Self {
|
|
Self::MQTTClient(e)
|
|
}
|
|
}
|
|
|
|
impl From<rumqttc::ConnectionError> for Error {
|
|
fn from(e: rumqttc::ConnectionError) -> Self {
|
|
Self::MQTTConnection(e)
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> 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<T> = std::result::Result<T, Error>;
|