feat: update ot synchronous gpio and rotate keys

This commit is contained in:
Jet 2026-03-23 01:48:49 -07:00
parent 16ad3c6181
commit 7d538f1942
No known key found for this signature in database
10 changed files with 61 additions and 48 deletions

View file

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0"
axum = "0.8"
gpiod = "0.3.0"
libc = "0.2"
noisebell-common = { path = "../../remote/noisebell-common" }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
@ -13,6 +14,5 @@ sd-notify = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "sync", "signal", "time"] }
tokio-gpiod = "0.3.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

View file

@ -7,10 +7,10 @@ use axum::extract::State;
use axum::http::{HeaderMap, StatusCode};
use axum::routing::get;
use axum::{Json, Router};
use gpiod::{Bias, Chip, Edge, EdgeDetect, Options};
use noisebell_common::{
validate_bearer, DoorStatus, PiStatusResponse, SignalLevel, WebhookPayload,
};
use tokio_gpiod::{Bias, Chip, Edge, EdgeDetect, Options};
use tracing::{error, info, warn};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -135,9 +135,7 @@ async fn main() -> Result<()> {
info!(gpio_pin, debounce_secs, port, %endpoint_url, "starting noisebell");
let chip = Chip::new("gpiochip0")
.await
.context("failed to open gpiochip0")?;
let chip = Chip::new("gpiochip0").context("failed to open gpiochip0")?;
let bias = if active_level == SignalLevel::Low {
Bias::PullUp
@ -152,13 +150,11 @@ async fn main() -> Result<()> {
.consumer("noisebell");
let mut inputs = chip
.request_lines(opts)
.await
.context(format!("failed to request GPIO line {gpio_pin}"))?;
// Read initial value
let initial_values = inputs
.get_values([false])
.await
.context("failed to read initial GPIO value")?;
// Value is true when line is active. With Active::High (default),
// true means the physical level is high.
@ -187,19 +183,22 @@ async fn main() -> Result<()> {
// Sync initial state with the cache on startup
let _ = tx.send((initial_state.as_door_status(), now));
// Spawn async edge detection task
// Spawn blocking edge detection task. The async tokio-gpiod path was
// returning repeated EAGAIN on Raspberry Pi OS even when no real GPIO
// error occurred.
let state_for_edges = state.clone();
let edge_tx = tx.clone();
let edge_handle = tokio::spawn(async move {
let mut last_event_time = std::time::Instant::now();
let _edge_handle = std::thread::spawn(move || {
let mut last_event_time = std::time::Instant::now() - Duration::from_secs(debounce_secs);
let debounce = Duration::from_secs(debounce_secs);
loop {
let event = match inputs.read_event().await {
let event = match inputs.read_event() {
Ok(event) => event,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => {
error!(error = %e, "failed to read GPIO event");
tokio::time::sleep(Duration::from_secs(1)).await;
std::thread::sleep(Duration::from_secs(1));
continue;
}
};
@ -303,7 +302,6 @@ async fn main() -> Result<()> {
.context("server error")?;
info!("shutting down, draining notification queue");
edge_handle.abort();
let _ = notify_handle.await;
info!("shutdown complete");