feat: add basic rss feat support
This commit is contained in:
parent
183b2c2c88
commit
452b8b49c3
13 changed files with 232 additions and 166 deletions
|
|
@ -3,6 +3,9 @@ name = "noisebell"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
axum = "0.8"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::sync::atomic::{AtomicU8, AtomicU64, Ordering};
|
||||
use std::sync::atomic::{AtomicU64, AtomicU8, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
|
|
@ -42,7 +42,6 @@ impl LocalDoorState {
|
|||
Self::Closed => DoorStatus::Closed,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
|
|
@ -58,10 +57,7 @@ impl AppState {
|
|||
}
|
||||
|
||||
fn unix_timestamp() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
|
||||
}
|
||||
|
||||
async fn get_status(
|
||||
|
|
@ -124,11 +120,7 @@ async fn main() -> Result<()> {
|
|||
.unwrap_or_else(|_| "true".into())
|
||||
.parse()
|
||||
.context("NOISEBELL_ACTIVE_LOW must be true or false")?;
|
||||
let active_level = if active_low {
|
||||
SignalLevel::Low
|
||||
} else {
|
||||
SignalLevel::High
|
||||
};
|
||||
let active_level = if active_low { SignalLevel::Low } else { SignalLevel::High };
|
||||
|
||||
let inbound_api_key = std::env::var("NOISEBELL_INBOUND_API_KEY")
|
||||
.context("NOISEBELL_INBOUND_API_KEY is required")?;
|
||||
|
|
@ -137,33 +129,20 @@ async fn main() -> Result<()> {
|
|||
|
||||
let chip = Chip::new("gpiochip0").context("failed to open gpiochip0")?;
|
||||
|
||||
let bias = if active_level == SignalLevel::Low {
|
||||
Bias::PullUp
|
||||
} else {
|
||||
Bias::PullDown
|
||||
};
|
||||
let bias = if active_level == SignalLevel::Low { Bias::PullUp } else { Bias::PullDown };
|
||||
|
||||
// Keep the line requested and poll its value. Edge-triggered reads have
|
||||
// proven unreliable on Raspberry Pi OS even though the raw line level is
|
||||
// correct, so we debounce from sampled levels instead.
|
||||
let opts = Options::input([gpio_pin])
|
||||
.bias(bias)
|
||||
.consumer("noisebell");
|
||||
let inputs = chip
|
||||
.request_lines(opts)
|
||||
.context(format!("failed to request GPIO line {gpio_pin}"))?;
|
||||
let opts = Options::input([gpio_pin]).bias(bias).consumer("noisebell");
|
||||
let inputs =
|
||||
chip.request_lines(opts).context(format!("failed to request GPIO line {gpio_pin}"))?;
|
||||
|
||||
// Read initial value
|
||||
let initial_values = inputs
|
||||
.get_values([false])
|
||||
.context("failed to read initial GPIO value")?;
|
||||
let initial_values = inputs.get_values([false]).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.
|
||||
let initial_raw_level = if initial_values[0] {
|
||||
SignalLevel::High
|
||||
} else {
|
||||
SignalLevel::Low
|
||||
};
|
||||
let initial_raw_level = if initial_values[0] { SignalLevel::High } else { SignalLevel::Low };
|
||||
let initial_state = LocalDoorState::from_raw_level(initial_raw_level, active_level);
|
||||
|
||||
let now = unix_timestamp();
|
||||
|
|
@ -205,11 +184,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
};
|
||||
|
||||
let new_raw_level = if values[0] {
|
||||
SignalLevel::High
|
||||
} else {
|
||||
SignalLevel::Low
|
||||
};
|
||||
let new_raw_level = if values[0] { SignalLevel::High } else { SignalLevel::Low };
|
||||
let new_state = LocalDoorState::from_raw_level(new_raw_level, active_level);
|
||||
|
||||
if new_state != pending_state {
|
||||
|
|
@ -218,9 +193,7 @@ async fn main() -> Result<()> {
|
|||
} else if new_state != current_state && pending_since.elapsed() >= debounce {
|
||||
current_state = new_state;
|
||||
let previous_state = LocalDoorState::from_atomic(
|
||||
state_for_edges
|
||||
.door_state
|
||||
.swap(new_state as u8, Ordering::Relaxed),
|
||||
state_for_edges.door_state.swap(new_state as u8, Ordering::Relaxed),
|
||||
);
|
||||
|
||||
if previous_state == new_state {
|
||||
|
|
@ -229,9 +202,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
let timestamp = unix_timestamp();
|
||||
state_for_edges
|
||||
.last_changed
|
||||
.store(timestamp, Ordering::Relaxed);
|
||||
state_for_edges.last_changed.store(timestamp, Ordering::Relaxed);
|
||||
let _ = edge_tx.send((new_state.as_door_status(), timestamp));
|
||||
}
|
||||
|
||||
|
|
@ -252,12 +223,8 @@ async fn main() -> Result<()> {
|
|||
let payload = WebhookPayload { status, timestamp };
|
||||
|
||||
for attempt in 0..=retry_attempts {
|
||||
let result = client
|
||||
.post(&endpoint_url)
|
||||
.bearer_auth(&api_key)
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await;
|
||||
let result =
|
||||
client.post(&endpoint_url).bearer_auth(&api_key).json(&payload).send().await;
|
||||
match result {
|
||||
Ok(resp) if resp.status().is_success() => break,
|
||||
_ => {
|
||||
|
|
@ -279,9 +246,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(get_status))
|
||||
.with_state(state);
|
||||
let app = Router::new().route("/", get(get_status)).with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind((&*bind_address, port))
|
||||
.await
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue