use serde::{Deserialize, Serialize}; #[derive(Debug, Default, Serialize, Clone)] pub enum PrintStatus { #[default] Unknown, Prepare, Idle, Printing, Paused, Finished, Stopped, Error, Attention, Busy, } pub trait UpdateFrom { fn update_from(&mut self, source: &T); } #[derive(Debug, Default, Serialize, Clone)] pub struct BambuPrinterState { pub layer_num: u32, pub total_layer_num: u32, pub print_speed: Option, } #[derive(Debug, Default, Serialize, Clone)] pub struct PrusaPrinterState { pub flow: u32, pub speed: u32, pub fan_hotend: u32, pub fan_print: u32, pub time_printing: u32, } #[derive(Debug, Default, Serialize, Clone)] pub struct PrinterState { pub status: PrintStatus, pub bed_temp: f32, pub target_bed_temp: f32, pub nozzle_temp: f32, pub target_nozzle_temp: f32, pub progress: f32, pub time_remaining: u32, pub prusa: Option, pub bambu: Option, } #[derive(Debug, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] enum PrusaState { Idle, Printing, Paused, Finished, Stopped, Error, Attention, Busy, } #[derive(Debug, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] enum BambuState { Prepare, Idle, Running, Pause, Finish, Failed, } #[derive(Debug, Deserialize)] struct PrusaJob { progress: f32, time_remaining: u32, time_printing: u32, } #[derive(Debug, Deserialize)] struct PrusaPrinterInfo { state: PrusaState, temp_nozzle: f32, target_nozzle: f32, temp_bed: f32, target_bed: f32, flow: u32, speed: u32, fan_hotend: u32, fan_print: u32, } #[derive(Debug, Deserialize)] pub struct PrusaStatus { job: Option, printer: PrusaPrinterInfo, } impl UpdateFrom for PrinterState { fn update_from(&mut self, status: &PrusaStatus) { self.status = match status.printer.state { PrusaState::Idle => PrintStatus::Idle, PrusaState::Printing => PrintStatus::Printing, PrusaState::Paused => PrintStatus::Paused, PrusaState::Finished => PrintStatus::Finished, PrusaState::Stopped => PrintStatus::Stopped, PrusaState::Error => PrintStatus::Error, PrusaState::Attention => PrintStatus::Attention, PrusaState::Busy => PrintStatus::Busy, }; let p = &status.printer; self.bed_temp = p.temp_bed; self.target_bed_temp = p.target_bed; self.nozzle_temp = p.temp_nozzle; self.target_nozzle_temp = p.target_nozzle; self.prusa = Some(PrusaPrinterState { flow: p.flow, speed: p.speed, fan_hotend: p.fan_hotend, fan_print: p.fan_print, time_printing: status.job.as_ref().map_or(0, |j| j.time_printing), }); if let Some(job) = &status.job { self.progress = job.progress; self.time_remaining = job.time_remaining; } } } #[derive(Debug, Deserialize)] struct BambuPrintMessage { bed_target_temper: Option, bed_temper: Option, gcode_state: Option, layer_num: Option, mc_percent: Option, mc_remaining_time: Option, nozzle_target_temper: Option, nozzle_temper: Option, print_speed: Option, total_layer_num: Option, } #[derive(Debug, Deserialize)] pub struct BambuStatus { print: Option, } impl UpdateFrom for PrinterState { fn update_from(&mut self, status: &BambuStatus) { if let Some(print) = &status.print { if let Some(v) = print.bed_temper { self.bed_temp = v; } if let Some(v) = print.bed_target_temper { self.target_bed_temp = v; } if let Some(v) = print.nozzle_temper { self.nozzle_temp = v; } if let Some(v) = print.nozzle_target_temper { self.target_nozzle_temp = v; } if let Some(v) = print.mc_percent { self.progress = v as f32; } if let Some(v) = print.mc_remaining_time { // Bambu remaining time is in minutes self.time_remaining = v * 60; } if print.layer_num.is_some() || print.total_layer_num.is_some() || print.print_speed.is_some() { let bambu = self.bambu.get_or_insert_with(BambuPrinterState::default); if let Some(v) = print.layer_num { bambu.layer_num = v; } if let Some(v) = print.total_layer_num { bambu.total_layer_num = v; } if print.print_speed.is_some() { bambu.print_speed = print.print_speed.clone(); } } if let Some(gcode_state) = &print.gcode_state { self.status = match gcode_state { BambuState::Prepare => PrintStatus::Prepare, BambuState::Idle => PrintStatus::Idle, BambuState::Running => PrintStatus::Printing, BambuState::Pause => PrintStatus::Paused, BambuState::Finish => PrintStatus::Finished, BambuState::Failed => PrintStatus::Error, }; } } } } #[derive(Debug, Deserialize)] pub struct Config { // This allows you to have a list of different printer types pub printers: Vec, } impl Config { pub fn load(toml: &str) -> Result { toml::from_str(toml) } } #[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, }, }