Implement sensor status debouncing
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.
This commit is contained in:
3 files changed
+53
-15
No files matched your search
@@ -9,6 +9,8 @@ mqtt:
|
|||||||
rooms:
|
rooms:
|
||||||
- "!iFCnqQcvrVIeehrjQO:chat.labolyon.fr"
|
- "!iFCnqQcvrVIeehrjQO:chat.labolyon.fr"
|
||||||
|
|
||||||
|
debounce_delay: 10 # seconds
|
||||||
|
|
||||||
# username / password login
|
# username / password login
|
||||||
# /!\ will create a new device every time the bot starts
|
# /!\ will create a new device every time the bot starts
|
||||||
username: "login"
|
username: "login"
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub enum AuthConfig {
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub mqtt: MQTTConfig,
|
pub mqtt: MQTTConfig,
|
||||||
pub rooms: Vec<String>,
|
pub rooms: Vec<String>,
|
||||||
|
pub debounce_delay: u64,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub auth: AuthConfig,
|
pub auth: AuthConfig,
|
||||||
pub homeserver_uri: String
|
pub homeserver_uri: String
|
||||||
|
|||||||
+50
-15
@@ -24,7 +24,8 @@ mod matrix;
|
|||||||
mod mqtt;
|
mod mqtt;
|
||||||
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{Notify, broadcast},
|
sync::{Notify, broadcast, mpsc},
|
||||||
|
time::{sleep, Duration},
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,11 +49,16 @@ struct Args {
|
|||||||
config: String,
|
config: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||||
enum LolOpenerStatus {
|
enum DoorSensorStatus {
|
||||||
Open,
|
Open,
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct LolOpenerState {
|
||||||
|
sensor_status: Option<DoorSensorStatus>,
|
||||||
|
update_pending: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
@@ -80,25 +86,54 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
matrix_ready.notified().await;
|
matrix_ready.notified().await;
|
||||||
info!("Matrix is ready");
|
info!("Matrix is ready");
|
||||||
|
|
||||||
let current_status = Arc::new(Mutex::new(None));
|
let current_state = Arc::new(Mutex::new(LolOpenerState {
|
||||||
let current_status = Arc::clone(¤t_status);
|
sensor_status: None,
|
||||||
|
update_pending: false
|
||||||
|
}));
|
||||||
|
let current_state = Arc::clone(¤t_state);
|
||||||
|
|
||||||
|
let (debounce_tx, mut debounce_rx) = mpsc::unbounded_channel::<(DoorSensorStatus, String)>();
|
||||||
|
let debounce_delay = config.debounce_delay;
|
||||||
|
let rooms = config.rooms.clone();
|
||||||
|
let handle = tokio::spawn(async move {
|
||||||
|
|
||||||
|
while let Some((new_status, msg)) = debounce_rx.recv().await {
|
||||||
|
let should_wait = {
|
||||||
|
//XXX: Panic if the lock is poisoned
|
||||||
|
let state = current_state.lock().unwrap();
|
||||||
|
!state.update_pending
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_wait {
|
||||||
|
sleep(Duration::from_secs(debounce_delay)).await;
|
||||||
|
//XXX: Panic if the lock is poisoned
|
||||||
|
let mut state = current_state.lock().unwrap();
|
||||||
|
state.update_pending = false;
|
||||||
|
|
||||||
|
if Some(new_status) != state.sensor_status {
|
||||||
|
debug!("Sending msg {}", msg);
|
||||||
|
//XXX: Panic if the matrix module can't get the message
|
||||||
|
bcast_tx.send((msg, rooms.clone())).unwrap();
|
||||||
|
state.sensor_status = Some(new_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut mqtt = MQTT::new(config.mqtt.clone()).await;
|
let mut mqtt = MQTT::new(config.mqtt.clone()).await;
|
||||||
mqtt.subscribe(move |status: StatusMessage| {
|
mqtt.subscribe(move |status: StatusMessage| {
|
||||||
let (new_status, msg) = match status {
|
let (new_status, msg) = match status {
|
||||||
StatusMessage { contact: true, .. } => (Some(LolOpenerStatus::Closed), "🔴 Le LOL est fermé :("),
|
StatusMessage { contact: true, .. } => (DoorSensorStatus::Closed, "🔴 Le LOL est fermé :("),
|
||||||
StatusMessage { contact: false, .. } => (Some(LolOpenerStatus::Open), "🟢 Le LOL est ouvert :)"),
|
StatusMessage { contact: false, .. } => (DoorSensorStatus::Open, "🟢 Le LOL est ouvert :)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
//XXX: Panic if the lock is poisoned (why would that happen?)
|
//XXX: Panic if the channel is closed
|
||||||
let mut current_status = current_status.lock().unwrap();
|
debounce_tx.send((new_status, String::from(msg))).unwrap();
|
||||||
if new_status != *current_status {
|
|
||||||
debug!("Sending msg {}", msg);
|
|
||||||
//XXX: Panic if the matrix module can't get the message
|
|
||||||
bcast_tx.send((msg.into(), config.rooms.clone())).unwrap();
|
|
||||||
*current_status = new_status;
|
|
||||||
}
|
|
||||||
}).await.unwrap(); //XXX: Panic if the mqtt broker fails
|
}).await.unwrap(); //XXX: Panic if the mqtt broker fails
|
||||||
|
|
||||||
|
handle.abort();
|
||||||
|
handle.await.unwrap();
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user