Skip to content

Commit f4ece89

Browse files
Implement power devices
1 parent e2b4501 commit f4ece89

11 files changed

Lines changed: 255 additions & 27 deletions

File tree

moonraker-rs/src/requests/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod file_management;
22
mod printer_administration;
3+
mod switches_sensors_devices;
34

45
pub use file_management::*;
56
pub use printer_administration::*;
7+
pub use switches_sensors_devices::*;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
use std::fmt::Display;
3+
4+
use serde::{Deserialize, Serialize};
5+
use serde_json::{Map, Value};
6+
7+
use crate::{
8+
connector::read_deserialize::MoonrakerEventNotifyStatusUpdate, error::Error,
9+
moonraker_connection::MoonrakerConnection,
10+
};
11+
12+
pub trait SwitchesSensorsDevicesRequestHandler {
13+
async fn list_power_devices(&self) -> Result<Vec<PowerDevice>, Error>;
14+
async fn set_power_device_state(
15+
&self,
16+
device: &str,
17+
state: PowerDeviceAction,
18+
) -> Result<Value, Error>;
19+
}
20+
21+
impl SwitchesSensorsDevicesRequestHandler for MoonrakerConnection {
22+
async fn list_power_devices(&self) -> Result<Vec<PowerDevice>, Error> {
23+
let devices : ListPowerDevicesResult = self.send_request("machine.device_power.devices", None).await?;
24+
25+
Ok(devices.devices)
26+
}
27+
28+
async fn set_power_device_state(
29+
&self,
30+
device: &str,
31+
state: PowerDeviceAction,
32+
) -> Result<Value, Error> {
33+
let args = serde_json::json!({
34+
"device": device,
35+
"action": state,
36+
});
37+
self.send_request("machine.device_power.post_device", Some(args)).await
38+
}
39+
}
40+
41+
#[derive(Debug, Deserialize)]
42+
#[serde(rename_all = "lowercase")]
43+
pub enum PowerDeviceState {
44+
On,
45+
Off,
46+
Init,
47+
Error,
48+
}
49+
50+
impl Display for PowerDeviceState {
51+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52+
match self {
53+
PowerDeviceState::On => write!(f, "On"),
54+
PowerDeviceState::Off => write!(f, "Off"),
55+
PowerDeviceState::Init => write!(f, "Initializing"),
56+
PowerDeviceState::Error => write!(f, "Error"),
57+
}
58+
}
59+
}
60+
61+
#[derive(Debug, Serialize)]
62+
#[serde(rename_all = "lowercase")]
63+
pub enum PowerDeviceAction
64+
{
65+
On,
66+
Off,
67+
Toggle,
68+
}
69+
70+
#[derive(Debug, Deserialize)]
71+
pub struct PowerDevice {
72+
pub device: String,
73+
pub status: PowerDeviceState,
74+
pub locked_while_printing: bool,
75+
#[serde(alias = "type")]
76+
pub device_type: String,
77+
}
78+
79+
#[derive(Debug, Deserialize)]
80+
pub struct ListPowerDevicesResult
81+
{
82+
pub devices: Vec<PowerDevice>
83+
}

src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
106106

107107
register_execute_quick_action(&ui, &config.quick_actions, &moonraker_connection);
108108

109+
register_misc_set_power_device(&ui, &moonraker_connection);
110+
register_misc_fetch_power_devices(&ui, &moonraker_connection);
111+
109112
tokio::task::block_in_place(|| {
110113
ui.run().unwrap();
111114
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::sync::Arc;
2+
3+
use moonraker_rs::{moonraker_connection::MoonrakerConnection, requests::{PrinterAdministrationRequestHandler, SwitchesSensorsDevicesRequestHandler}};
4+
use slint::{ComponentHandle, ModelRc, SharedString, VecModel};
5+
6+
use crate::{AppWindow, PowerDevice, PowerDevices, PrinterAdministration};
7+
8+
9+
pub fn register_misc_fetch_power_devices(ui : &AppWindow, moonraker_connection : &Arc<MoonrakerConnection>)
10+
{
11+
let moonraker_connection = moonraker_connection.clone();
12+
let ui_weak = ui.as_weak();
13+
14+
ui.global::<PowerDevices>().on_fetch_power_devices(move || {
15+
let moonraker_connection = moonraker_connection.clone();
16+
let ui_weak = ui_weak.clone();
17+
18+
slint::spawn_local(async move {
19+
let devices = match moonraker_connection.list_power_devices().await
20+
{
21+
Ok(d) => d,
22+
Err(e) => {
23+
moonraker_connection.send_request_error(format!("Failed to fetch power devices: {}", e));
24+
return;
25+
}
26+
};
27+
28+
let ui = ui_weak.upgrade().unwrap();
29+
30+
let devices: Vec<PowerDevice> = devices.iter().map(|power_device| {
31+
PowerDevice {
32+
device: SharedString::from(&power_device.device),
33+
status: SharedString::from(&power_device.status.to_string()),
34+
locked_while_printing: power_device.locked_while_printing,
35+
device_type: SharedString::from(&power_device.device_type),
36+
}
37+
}).collect();
38+
39+
ui.global::<PowerDevices>().set_power_devices(ModelRc::new(VecModel::from(devices)));
40+
}).unwrap();
41+
});
42+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::sync::Arc;
2+
3+
use moonraker_rs::{moonraker_connection::MoonrakerConnection, requests::{PowerDeviceAction, PrinterAdministrationRequestHandler, SwitchesSensorsDevicesRequestHandler}};
4+
use slint::{ComponentHandle, ModelRc, SharedString, VecModel};
5+
6+
use crate::{AppWindow, PowerDevice, PowerDevices, PrinterAdministration};
7+
8+
9+
pub fn register_misc_set_power_device(ui : &AppWindow, moonraker_connection : &Arc<MoonrakerConnection>)
10+
{
11+
let moonraker_connection = moonraker_connection.clone();
12+
13+
ui.global::<PowerDevices>().on_set_power_device_state(move |device, state| {
14+
let moonraker_connection = moonraker_connection.clone();
15+
16+
tokio::spawn(async move {
17+
if let Err(e) = moonraker_connection.set_power_device_state(&device.to_string(), if state { PowerDeviceAction::On } else { PowerDeviceAction::Off }).await
18+
{
19+
moonraker_connection.send_request_error(format!("Failed to set power device state for {}: {}", device.to_string(), e));
20+
return;
21+
}
22+
});
23+
});
24+
}

src/ui_functions/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub mod util_create_temperature_list;
1111
pub mod settings_set_ui_settings;
1212
pub mod quick_action_execute;
1313
pub mod util_time_in_seconds_to_string;
14+
pub mod misc_set_power_device;
15+
pub mod misc_fetch_power_devices;
1416

1517
pub use util_format_bytes::*;
1618
pub use filesystem_fetch_metadata::*;
@@ -24,4 +26,6 @@ pub use printer_execute_gcode_command::*;
2426
pub use util_create_temperature_list::*;
2527
pub use settings_set_ui_settings::*;
2628
pub use quick_action_execute::*;
27-
pub use util_time_in_seconds_to_string::*;
29+
pub use util_time_in_seconds_to_string::*;
30+
pub use misc_set_power_device::*;
31+
pub use misc_fetch_power_devices::*;

ui/assets/power_device.svg

Lines changed: 1 addition & 0 deletions
Loading

ui/constants.slint

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export global Icons {
2828
out property <image> settings: @image-url("assets/settings.svg");
2929
out property <image> temperature: @image-url("assets/temperature.svg");
3030
out property <image> action: @image-url("assets/action.svg");
31+
out property <image> power-device: @image-url("assets/power_device.svg");
3132
}
3233

3334
export global Constants {

ui/pages/quick-actions-page.slint

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,62 @@
11
import { Page } from "../components/page.slint";
22
import { ScrollView } from "std-widgets.slint";
3-
import { QuickActions } from "../state.slint";
3+
import { QuickActions, PowerDevices } from "../state.slint";
44
import { Icons, Constants } from "../constants.slint";
5-
import { Palette, StyleMetrics } from "std-widgets.slint";
5+
import { Palette, StyleMetrics, Switch } from "std-widgets.slint";
66
import { SmallButton } from "../components/small-button.slint";
77
import { VerticalScrollable } from "../components/vertical.slint";
88
import { HorizontalStretch } from "../components/horizontal.slint";
9+
import { PowerDevice } from "../types.slint";
910

10-
component QuickAction inherits Rectangle
11+
component PowerDevice inherits HorizontalStretch
1112
{
12-
in property <string> quick-action;
13-
width: 100%;
13+
in property <PowerDevice> power-device;
1414

15-
HorizontalStretch {
16-
Image {
17-
source: Icons.action;
18-
colorize: Palette.foreground;
19-
width: Constants.thumbnail-size;
20-
height: Constants.thumbnail-size;
21-
}
15+
Image {
16+
source: Icons.power-device;
17+
colorize: Palette.foreground;
18+
}
2219

23-
Text {
24-
text: quick-action;
25-
horizontal-stretch: 1;
26-
vertical-alignment: center;
27-
overflow: elide;
20+
Text {
21+
text: power-device.device;
22+
horizontal-stretch: 1;
23+
vertical-alignment: center;
24+
overflow: elide;
25+
}
26+
27+
Switch {
28+
text: self.checked ? "On" : "Off";
29+
vertical-stretch: 1;
30+
checked: power-device.status == "On";
31+
toggled() => {
32+
PowerDevices.set_power_device_state(power-device.device, self.checked);
2833
}
34+
}
35+
}
36+
37+
component QuickAction inherits HorizontalStretch
38+
{
39+
in property <string> quick-action;
40+
41+
Image {
42+
source: Icons.action;
43+
colorize: Palette.foreground;
44+
}
45+
46+
Text {
47+
text: quick-action;
48+
horizontal-stretch: 1;
49+
vertical-alignment: center;
50+
overflow: elide;
51+
}
2952

30-
SmallButton {
31-
text: "Run";
32-
width: 50px;
33-
vertical-stretch: 1;
34-
border-radius: Constants.radius-md;
35-
clicked => { QuickActions.execute_quick_action(quick-action); }
53+
SmallButton {
54+
text: "Run";
55+
width: 50px;
56+
vertical-stretch: 1;
57+
border-radius: Constants.radius-md;
58+
clicked => {
59+
QuickActions.execute_quick_action(quick-action);
3660
}
3761
}
3862
}
@@ -41,11 +65,19 @@ export component QuickActionsPage inherits Page
4165
{
4266
header: "Quick Actions";
4367

44-
// TODO: Power devices
45-
68+
init => {
69+
PowerDevices.fetch_power_devices();
70+
}
71+
4672
VerticalScrollable {
73+
for power-device in PowerDevices.power_devices: PowerDevice {
74+
power-device: power-device;
75+
height: Constants.thumbnail-size;
76+
}
77+
4778
for quick-action in QuickActions.quick-actions: QuickAction {
4879
quick-action: quick-action;
80+
height: Constants.thumbnail-size;
4981
}
5082
}
5183

@@ -56,4 +88,25 @@ export component QuickActionsPage inherits Page
5688
horizontal-alignment: center;
5789
}
5890
}
91+
}
92+
93+
component LivePreviewTest {
94+
width: 480px - 100px;
95+
height: 272px - 40px;
96+
97+
init => {
98+
QuickActions.quick-actions = [
99+
"Test1",
100+
"Test2"
101+
];
102+
103+
PowerDevices.power_devices = [
104+
{ device: "Test device", status: "Off", locked_while_printing: false, device_type: "Simulated" },
105+
{ device: "Test device 2", status: "On", locked_while_printing: false, device_type: "Simulated" }
106+
]
107+
}
108+
109+
QuickActionsPage {
110+
111+
}
59112
}

ui/state.slint

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TemperatureSensor, HeaterFan, Heater, MoonrakerFile } from "types.slint";
1+
import { TemperatureSensor, HeaterFan, Heater, MoonrakerFile, PowerDevice } from "types.slint";
22
import { Palette } from "std-widgets.slint";
33
import { Icons } from "constants.slint";
44

@@ -96,4 +96,11 @@ export global ActiveUi
9696
{
9797
in-out property <length> width: 480px;
9898
in-out property <length> height: 272px;
99+
}
100+
101+
export global PowerDevices
102+
{
103+
in-out property <[PowerDevice]> power_devices: [];
104+
callback set_power_device_state(device : string, state: bool);
105+
callback fetch_power_devices();
99106
}

0 commit comments

Comments
 (0)