rusty_patio/streamdeck/client/
mod.rs

1use std::collections::HashMap;
2
3use anyhow::Result;
4use base64::{engine::general_purpose, Engine as _};
5use serde::Serialize;
6use serde_json::Value;
7
8use crate::payloads::{
9    event_context::StreamDeckEventContextMessage, log_message::StreamDeckLogMessage,
10    open_url::StreamDeckOpenUrlMessage, register::StreamDeckPluginRegister,
11    send_to_pi::StreamDeckSendToPIMessage, send_to_plugin::StreamDeckSendToPluginMessage,
12    set_feedback::StreamDeckSetFeedbackMessage,
13    set_feedback_layout::StreamDeckSetFeedbackLayoutMessage, set_image::StreamDeckSetImageMessage,
14    set_settings::StreamDeckSetSettingsMessage, set_state::StreamDeckSetStateMessage,
15    set_title::StreamDeckSetTitleMessage, switch_to_profile::StreamDeckSwitchToProfileMessage,
16};
17
18use super::events::{event_received::EventReceived, event_title::StreamDeckEventTitle};
19
20#[derive(Clone)]
21pub struct StreamDeckClientTransmitter {
22    transmit_message: tokio::sync::mpsc::UnboundedSender<String>,
23}
24
25impl StreamDeckClientTransmitter {
26    pub fn new(transmit_message: tokio::sync::mpsc::UnboundedSender<String>) -> Self {
27        StreamDeckClientTransmitter { transmit_message }
28    }
29
30    async fn send_message(&mut self, message: String) -> Result<()> {
31        self.transmit_message.send(message).map_err(|e| e.into())
32    }
33
34    async fn send_json_message<T: Sized + Serialize>(&mut self, message: T) -> Result<()> {
35        self.send_message(serde_json::to_string(&message).unwrap())
36            .await
37    }
38
39    pub async fn register_plugin(&mut self, event: &String, uuid: &String) {
40        self.send_json_message(StreamDeckPluginRegister::new(event, uuid))
41            .await
42            .unwrap();
43    }
44
45    pub async fn log_message(&mut self, message: String) -> Result<()> {
46        self.send_json_message(StreamDeckLogMessage::new(message))
47            .await
48    }
49
50    pub async fn set_image(
51        &mut self,
52        context: String,
53        image_path: String,
54        target: u8,
55        state: Option<u8>,
56    ) -> Result<()> {
57        let image_bytes = tokio::fs::read(&image_path).await?;
58        let encoded = general_purpose::STANDARD.encode(&image_bytes);
59        let mime_type = mime_guess::from_path(&image_path).first();
60
61        if let Some(mime_type) = mime_type {
62            self.send_json_message(StreamDeckSetImageMessage::new(
63                context,
64                format!("data:{};base64,{}", mime_type, encoded),
65                target,
66                state,
67            ))
68            .await
69        } else {
70            Err(anyhow::anyhow!("Could not determine mime type"))
71        }
72    }
73
74    pub async fn switch_to_profile(
75        &mut self,
76        context: String,
77        device: String,
78        profile: String,
79    ) -> Result<()> {
80        self.send_json_message(StreamDeckSwitchToProfileMessage::new(
81            context, device, profile,
82        ))
83        .await
84    }
85
86    pub async fn send_to_property_inspector(
87        &mut self,
88        context: String,
89        action: String,
90        payload: HashMap<String, Value>,
91    ) -> Result<()> {
92        self.send_json_message(StreamDeckSendToPIMessage::new(action, context, payload))
93            .await
94    }
95
96    pub async fn set_title(
97        &mut self,
98        context: String,
99        title: String,
100        target: u8,
101        state: Option<u8>,
102    ) -> Result<()> {
103        self.send_json_message(StreamDeckSetTitleMessage::new(
104            context, title, target, state,
105        ))
106        .await
107    }
108
109    pub async fn set_settings<T: Sized + Serialize>(
110        &mut self,
111        context: String,
112        payload: T,
113    ) -> Result<()> {
114        self.send_json_message(StreamDeckSetSettingsMessage {
115            event: StreamDeckEventTitle::SET_SETTINGS.to_string(),
116            context,
117            payload,
118        })
119        .await
120    }
121
122    pub async fn set_global_settings<T: Sized + Serialize>(
123        &mut self,
124        context: String,
125        payload: T,
126    ) -> Result<()> {
127        self.send_json_message(StreamDeckSetSettingsMessage {
128            event: StreamDeckEventTitle::SET_GLOBAL_SETTINGS.to_string(),
129            context,
130            payload,
131        })
132        .await
133    }
134
135    pub async fn open_url(&mut self, url: String) -> Result<()> {
136        self.send_json_message(StreamDeckOpenUrlMessage::new(url))
137            .await
138    }
139
140    pub async fn get_settings(&mut self, context: String) -> Result<()> {
141        self.send_json_message(StreamDeckEventContextMessage {
142            event: StreamDeckEventTitle::GET_SETTINGS.to_string(),
143            context,
144        })
145        .await
146    }
147
148    pub async fn get_global_settings(&mut self, context: String) -> Result<()> {
149        self.send_json_message(StreamDeckEventContextMessage {
150            event: StreamDeckEventTitle::GET_GLOBAL_SETTINGS.to_string(),
151            context,
152        })
153        .await
154    }
155
156    pub async fn show_alert(&mut self, context: String) -> Result<()> {
157        self.send_json_message(StreamDeckEventContextMessage {
158            event: StreamDeckEventTitle::SHOW_ALERT.to_string(),
159            context,
160        })
161        .await
162    }
163
164    pub async fn show_ok(&mut self, context: String) -> Result<()> {
165        self.send_json_message(StreamDeckEventContextMessage {
166            event: StreamDeckEventTitle::SHOW_OK.to_string(),
167            context,
168        })
169        .await
170    }
171
172    pub async fn set_feedback(&mut self, context: String, payload: HashMap<String, Value>) {
173        self.send_json_message(StreamDeckSetFeedbackMessage::new(context, payload))
174            .await
175            .unwrap();
176    }
177
178    pub async fn set_feedback_layout(&mut self, context: String, layout: String) {
179        self.send_json_message(StreamDeckSetFeedbackLayoutMessage::new(context, layout))
180            .await
181            .unwrap();
182    }
183
184    pub async fn set_state(&mut self, context: String, state: u8) {
185        self.send_json_message(StreamDeckSetStateMessage::new(context, state))
186            .await
187            .unwrap();
188    }
189
190    pub async fn send_to_plugin(
191        &mut self,
192        context: String,
193        action: String,
194        payload: HashMap<String, Value>,
195    ) {
196        self.send_json_message(StreamDeckSendToPluginMessage::new(context, action, payload))
197            .await
198            .unwrap();
199    }
200}
201
202pub struct StreamDeckClient {
203    pub received_events: tokio::sync::mpsc::UnboundedReceiver<EventReceived>,
204    pub transmitter: StreamDeckClientTransmitter,
205}
206
207impl StreamDeckClient {
208    pub fn new(
209        received_events: tokio::sync::mpsc::UnboundedReceiver<EventReceived>,
210        transmit_message: tokio::sync::mpsc::UnboundedSender<String>,
211    ) -> Self {
212        StreamDeckClient {
213            received_events,
214            transmitter: StreamDeckClientTransmitter::new(transmit_message),
215        }
216    }
217}