This introduces a configurable debouncing delay before which no update is sent to matrix, which avoids flooding the matrix room with updates when the door is opened and closed in quick succession. It also allows to quickly open and close the door without sending notifications. The debouncing window can be configured in config.yml, see example.
102 lines
2.5 KiB
Rust
102 lines
2.5 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 serde::Deserialize;
|
|
use std::{fs, io, fmt};
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
pub struct MQTTConfig {
|
|
pub id: String,
|
|
pub host: String,
|
|
pub port: u16,
|
|
pub topic: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
pub enum AuthConfig {
|
|
PasswordAuthConfig {
|
|
username: String,
|
|
password: String,
|
|
},
|
|
TokenAuthConfig {
|
|
user_id: String,
|
|
device_id: String,
|
|
access_token: String,
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct Config {
|
|
pub mqtt: MQTTConfig,
|
|
pub rooms: Vec<String>,
|
|
pub debounce_delay: u64,
|
|
#[serde(flatten)]
|
|
pub auth: AuthConfig,
|
|
pub homeserver_uri: String
|
|
}
|
|
|
|
impl Config {
|
|
pub fn load(config_file: &str) -> Result<Self> {
|
|
|
|
let serialized_config = fs::read_to_string(config_file).map_err(|e| {
|
|
match e.kind() {
|
|
io::ErrorKind::NotFound => Error::FileNotFound(config_file.into()),
|
|
io::ErrorKind::PermissionDenied => Error::PermissionDenied(),
|
|
_ => e.into(),
|
|
}
|
|
})?;
|
|
|
|
let config: Config = serde_yaml::from_str(&serialized_config)?;
|
|
|
|
Ok(config)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)] // we use Debug, so inner fields are never read
|
|
pub enum Error {
|
|
FileNotFound(String),
|
|
PermissionDenied(),
|
|
InvalidFormat(serde_yaml::Error),
|
|
Io(io::Error),
|
|
}
|
|
|
|
impl From<serde_yaml::Error> for Error {
|
|
fn from(e: serde_yaml::Error) -> Self {
|
|
Self::InvalidFormat(e)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(e: io::Error) -> Self {
|
|
Self::Io(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>;
|