button presses -> sse -> web display

This commit is contained in:
2020-10-14 12:58:31 -07:00
parent 3b86c9fe21
commit 069099c7bb
3 changed files with 67 additions and 12 deletions

View File

@@ -7,6 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
futures-util = "0.3.6"
log = "0.4.11" log = "0.4.11"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
rppal = "0.11.3" rppal = "0.11.3"

View File

@@ -1,25 +1,43 @@
use std::thread::sleep; use std::convert::Infallible;
use std::time::Duration; use std::sync::{Arc, Mutex};
use futures_util::{FutureExt,
select,
StreamExt};
#[allow(unused_imports)] use log::{debug, error, info, trace};
use rppal::gpio::{ Gpio, Level, Trigger };
use tokio::sync::mpsc;
use warp::{Filter, sse};
use log::info;
use rppal::gpio::{Gpio, Level, Trigger};
use warp::Filter;
const BUTTON_PIN: u8 = 26; const BUTTON_PIN: u8 = 26;
fn button_pressed(_level: Level) { fn button_pressed(_level: Level, tx: &mut mpsc::Sender<()>) {
println!("pushed"); info!("DOORBELL PRESS");
sleep(Duration::from_millis(25)); // if rx full, we get some free debouncing
// if rx is closed, program is terminating anyway
let _ignore_err = tx.try_send(());
} }
#[tokio::main] #[tokio::main(basic_scheduler)]
async fn main() { async fn main() {
pretty_env_logger::init(); pretty_env_logger::init();
let gpio = Gpio::new().expect("gpio init"); let gpio = Gpio::new().expect("gpio init");
let mut pin = gpio.get(BUTTON_PIN).expect("pin init").into_input_pulldown(); let mut pin = gpio.get(BUTTON_PIN).expect("pin init").into_input_pulldown();
pin.set_async_interrupt(Trigger::RisingEdge, button_pressed).expect("set interrupt"); let (mut tx, rx) = mpsc::channel(1);
pin.set_async_interrupt(Trigger::RisingEdge, move |level| button_pressed(level, &mut tx))
.expect("set interrupt");
let clients = Arc::new(Mutex::new(Vec::new()));
let clients_filter = clients.clone();
let clients_filter = warp::any().map(move || clients_filter.clone());
let pushes = rx.for_each(|()| {
debug!("received signal from interrupt");
clients.lock().unwrap().retain(|tx: &mpsc::UnboundedSender<()>| tx.send(()).is_ok());
futures_util::future::ready(())
});
// GET / // GET /
let root = warp::path::end() let root = warp::path::end()
@@ -27,5 +45,27 @@ async fn main() {
.map(|| info!("GET /")).untuple_one() .map(|| info!("GET /")).untuple_one()
.and(warp::fs::file("./static/main.html")); .and(warp::fs::file("./static/main.html"));
warp::serve(root).run(([0, 0, 0, 0], 8060)).await; // GET /events
let events = warp::path!("events")
.and(warp::get())
.and(clients_filter)
.map(|clients: Arc<Mutex<Vec<_>>>| {
info!("GET /events");
let (tx, rx) = mpsc::unbounded_channel();
clients.lock().unwrap().push(tx);
let stream = rx.map(|()| {
debug!("sending sse");
Ok::<_, Infallible>((sse::event("ring"),
sse::data("")))
});
sse::reply(stream)
});
let routes = root.or(events);
select! {
_ = warp::serve(routes).run(([0, 0, 0, 0], 8060)).fuse() => (),
_ = pushes.fuse() => ()
}
} }

View File

@@ -1,9 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Fáfnir Doorbell</title> <title>Fáfnir Doorbell</title>
</head> </head>
<body> <body>
<section id="log">
<h1>Incoming Events</h1>
</section>
<template id="ring">
<p>Ring!
</template>
<script>
"use strict";
let sse = new EventSource('events');
sse.addEventListener('ring', msg => {
const template = document.querySelector('#ring').content.cloneNode(true);
document.querySelector('#log').appendChild(template);
});
</script>
</body> </body>
</html> </html>