summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs65
-rw-r--r--src/main.rs63
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));
+ }
+}