diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 65 | ||||
| -rw-r--r-- | src/main.rs | 63 |
2 files changed, 128 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..819fc96 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,65 @@ +use serde::Deserialize; + +#[derive(Debug)] +pub struct PrinterState { + pub name: String, + pub bed_temp: f32, +} + +#[derive(Debug, Deserialize)] +pub struct Config { + // This allows you to have a list of different printer types + pub printers: Vec<Printer>, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Printer { + Prusa { + name: String, + host: String, + api_key: String, + }, + Bambu { + name: String, + host: String, + access_code: String, + serial_number: String, + }, +} + +pub fn prusa_fetch(client: &reqwest::blocking::Client, printer: &Printer) { + let Printer::Prusa { + name, + host, + api_key, + } = printer + else { + panic!("Expected a Prusa printer, but received a different variant!"); + }; + + let url = format!("http://{}/api/v1/status", host); + + let mut req = client.get(&url); + req = req.header("X-Api-Key", api_key); + + match req.send() { + Err(e) => { + eprintln!("Could not reach Prusa printer {} at {}: {}", name, host, e); + return; + } + Ok(resp) => { + if !resp.status().is_success() { + eprintln!("HTTP {}: {}", resp.status(), url); + if resp.status().as_u16() == 403 { + eprintln!("Invalid PrusaLink key for {}.", name); + } + return; + } + match resp.text() { + Err(e) => eprintln!("Failed to parse response for Prusa printer {}: {}", host, e), + Ok(text) => println!("{}", text), + } + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7660108 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,63 @@ +use printstats::*; +use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS}; +use std::collections::HashMap; +use std::fs; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::Mutex; + +#[tokio::main] +async fn main() { + let content = fs::read_to_string("config.toml").expect("Couldn't read config.toml"); + let config: Config = toml::from_str(&content).expect("Couldn't parse config.toml"); + let state = Arc::new(Mutex::new(HashMap::<String, PrinterState>::new())); + + for printer in &config.printers { + match printer { + Printer::Prusa { name, host, .. } => { + println!("Found Prusa: {} at {}", name, host); + } + Printer::Bambu { + name, + host, + serial_number, + .. + } => { + println!("Found Bambu: {} at {}", name, host); + let state_clone = Arc::clone(&state); + tokio::spawn(async move { + let mut mqttoptions = MqttOptions::new(name, host, 1883); + mqttoptions.set_keep_alive(Duration::from_secs(5)); + + let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10); + client + .subscribe(format!("device/{}/report", serial_number), QoS::AtMostOnce) + .await + .unwrap(); + + loop { + // eventloop.poll() yields back to Tokio when there's no data + match eventloop.poll().await { + Ok(notification) => { + if let Event::Incoming(Packet::Publish(p)) = notification { + let mut lock = state_clone.lock().await; + // TODO - Update struct + println!("Updated state from {:?}", p.payload); + } + } + Err(_) => { + tokio::time::sleep(Duration::from_secs(5)).await; // Simple retry + } + } + } + }); + } + } + } + + loop { + print!("\x1B[2J\x1B[1;1H"); // clear screen + + std::thread::sleep(Duration::from_secs(5)); + } +} |
