Skip to main content

streamdeck_rs/
registration.rs

1use super::{Color, DeviceSize, DeviceType};
2use serde::{de, ser};
3use serde_derive::{Deserialize, Serialize};
4use std::fmt;
5use std::str::FromStr;
6use thiserror::Error;
7
8/// Information about a connected device.
9///
10/// [Official Documentation](https://developer.elgato.com/documentation/stream-deck/sdk/registration-procedure/#info-parameter)
11#[derive(Debug, Deserialize, Serialize)]
12pub struct RegistrationInfoDevice {
13    /// The ID of the specific device.
14    pub id: String,
15    /// The user-specified name of the device.
16    ///
17    /// Added in Stream Deck software version 4.3.
18    pub name: Option<String>,
19    /// The size of the device.
20    pub size: DeviceSize,
21    /// The type of the device.
22    #[serde(rename = "type")]
23    pub _type: Option<DeviceType>,
24}
25
26/// The language the Stream Deck software is running in.
27///
28/// [Official Documentation](https://docs.elgato.com/streamdeck/sdk/references/websocket/plugin#registrationinfo)
29#[derive(Debug)]
30pub enum Language {
31    English,
32    French,
33    German,
34    Spanish,
35    Japanese,
36    Korean,
37    /// Unlike the other languages which are not specifically localized to a country, Chinese is specifically zh-CN.
38    ChineseChina,
39    /// Traditional Chinese (zh-TW).
40    ChineseTaiwan,
41    /// A language that was not documented in the SDK.
42    Unknown(String),
43}
44
45impl<'de> de::Deserialize<'de> for Language {
46    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47    where
48        D: de::Deserializer<'de>,
49    {
50        struct Visitor;
51
52        impl<'de> de::Visitor<'de> for Visitor {
53            type Value = Language;
54
55            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
56                formatter.write_str("a string")
57            }
58
59            fn visit_str<E>(self, value: &str) -> Result<Language, E>
60            where
61                E: de::Error,
62            {
63                Ok(match value {
64                    "en" => Language::English,
65                    "fr" => Language::French,
66                    "de" => Language::German,
67                    "es" => Language::Spanish,
68                    "ja" => Language::Japanese,
69                    // Old lowercase representation used by older SDKs.
70                    "zh_cn" => Language::ChineseChina,
71                    // Current documented representations.
72                    "ko" => Language::Korean,
73                    "zh_CN" => Language::ChineseChina,
74                    "zh_TW" => Language::ChineseTaiwan,
75                    value => Language::Unknown(value.to_string()),
76                })
77            }
78        }
79
80        deserializer.deserialize_str(Visitor)
81    }
82}
83
84impl ser::Serialize for Language {
85    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86    where
87        S: ser::Serializer,
88    {
89        let lang = match self {
90            Language::English => "en",
91            Language::French => "fr",
92            Language::German => "de",
93            Language::Spanish => "es",
94            Language::Japanese => "ja",
95            Language::Korean => "ko",
96            Language::ChineseChina => "zh_CN",
97            Language::ChineseTaiwan => "zh_TW",
98            Language::Unknown(value) => value,
99        };
100
101        serializer.serialize_str(lang)
102    }
103}
104
105/// The platform on which the Stream Deck software is running.
106#[derive(Debug)]
107pub enum Platform {
108    /// Mac OS X
109    Mac,
110    /// Windows
111    Windows,
112    /// A platform not documented in the 4.0.0 SDK.
113    Unknown(String),
114}
115
116impl ser::Serialize for Platform {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: ser::Serializer,
120    {
121        let platform = match self {
122            Platform::Mac => "mac",
123            Platform::Windows => "windows",
124            Platform::Unknown(s) => s,
125        };
126
127        serializer.serialize_str(platform)
128    }
129}
130
131impl<'de> de::Deserialize<'de> for Platform {
132    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133    where
134        D: de::Deserializer<'de>,
135    {
136        struct Visitor;
137
138        impl<'de> de::Visitor<'de> for Visitor {
139            type Value = Platform;
140
141            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
142                formatter.write_str("a string")
143            }
144
145            fn visit_str<E>(self, value: &str) -> Result<Platform, E>
146            where
147                E: de::Error,
148            {
149                Ok(match value {
150                    "mac" => Platform::Mac,
151                    "windows" => Platform::Windows,
152                    value => Platform::Unknown(value.to_string()),
153                })
154            }
155        }
156
157        deserializer.deserialize_str(Visitor)
158    }
159}
160
161/// Information about the Stream Deck software.
162///
163/// [Official Documentation](https://docs.elgato.com/streamdeck/sdk/references/websocket/plugin#registrationinfo)
164#[derive(Debug, Deserialize, Serialize)]
165pub struct RegistrationInfoApplication {
166    /// Font being used by the Stream Deck application.
167    pub font: String,
168    /// Users preferred language; this is used by the Stream Deck application for localization.
169    pub language: Language,
170    /// Operating system.
171    pub platform: Platform,
172    /// Operating system version, e.g. "10" for Windows 10.
173    #[serde(rename = "platformVersion")]
174    pub platform_version: String,
175    /// Stream Deck application version.
176    pub version: String,
177}
178
179/// Information about the plugin
180///
181/// [Official Documentation](https://developer.elgato.com/documentation/stream-deck/sdk/registration-procedure/#info-parameter)
182#[derive(Serialize, Deserialize, Debug, Clone)]
183#[serde(rename_all = "camelCase")]
184pub struct RegistrationInfoPlugin {
185    /// Version of the plugin as per the manifest
186    pub version: String,
187    /// Unique identifier of the plugin
188    pub uuid: String,
189}
190
191/// The user's preferred colors
192///
193/// [Official Documentation](https://docs.elgato.com/streamdeck/sdk/references/websocket/plugin#registrationinfo)
194#[derive(Clone, Deserialize, Serialize, Debug, Default)]
195#[serde(rename_all = "camelCase")]
196pub struct UserColors {
197    /// Color that denotes the background of a button that is being moused over.
198    pub button_mouse_over_background_color: Option<Color>,
199    /// Color that denotes the background of a pressed button.
200    pub button_pressed_background_color: Option<Color>,
201    /// Color that denotes the border of a pressed button.
202    pub button_pressed_border_color: Option<Color>,
203    /// Color that denotes the text of a pressed button.
204    pub button_pressed_text_color: Option<Color>,
205    /// Color of highlighted text.
206    pub highlight_color: Option<Color>,
207}
208
209/// Information about the environment the plugin is being loaded into.
210///
211/// [Official Documentation](https://developer.elgato.com/documentation/stream-deck/sdk/registration-procedure/#info-parameter)
212#[derive(Debug, Deserialize, Serialize)]
213#[serde(rename_all = "camelCase")]
214pub struct RegistrationInfo {
215    pub application: RegistrationInfoApplication,
216    pub plugin: RegistrationInfoPlugin,
217    pub device_pixel_ratio: f64,
218    pub devices: Vec<RegistrationInfoDevice>,
219    pub colors: UserColors,
220}
221
222/// Registration parameters provided to the plugin on startup.
223///
224/// [Official Documentation](https://developer.elgato.com/documentation/stream-deck/sdk/registration-procedure/#compiled-plugin-registration)
225#[derive(Deserialize)]
226pub struct RegistrationParams {
227    /// The web socket port listening for the plugin.
228    pub port: u16,
229    /// The uuid of the plugin.
230    pub uuid: String,
231    /// The event the plugin should send to register with the Stream Deck software.
232    pub event: String,
233    /// Information about the environment the plugin is being loaded into.
234    pub info: RegistrationInfo,
235}
236
237/// An error that occurred while collecting the registration parameters.
238#[derive(Debug, Error)]
239pub enum RegistrationParamsError {
240    /// The port number was not found.
241    #[error("port not provided")]
242    NoPort,
243    /// The port number was found but could not be parsed.
244    #[error("port could not be parsed")]
245    BadPort(#[source] std::num::ParseIntError),
246    /// The uuid was not found.
247    #[error("uuid not provided")]
248    NoUuid,
249    /// The registration event to send was not found.
250    #[error("event not provided")]
251    NoEvent,
252    /// The registration environment info was not found.
253    #[error("info not provided")]
254    NoInfo,
255    /// The registration environment info could not be parsed.
256    #[error("info could not be parsed")]
257    BadInfo(#[from] serde_json::Error),
258}
259
260impl RegistrationParams {
261    /// Pull the registration parameters out of a command line.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// RegistrationParams::from_args(env::args())
267    /// ```
268    pub fn from_args<I: IntoIterator<Item = String>>(
269        args: I,
270    ) -> Result<RegistrationParams, RegistrationParamsError> {
271        let mut iter = args.into_iter();
272        let mut port = None;
273        let mut uuid = None;
274        let mut event = None;
275        let mut info = None;
276
277        loop {
278            match iter.next().as_deref() {
279                Some("-port") => port = iter.next().map(|a| u16::from_str(&a)),
280                Some("-pluginUUID") => uuid = iter.next(),
281                Some("-registerEvent") => event = iter.next(),
282                Some("-info") => info = iter.next().map(|a| serde_json::from_str(&a)),
283                Some(_) => {}
284                None => break,
285            }
286        }
287        let port = port
288            .ok_or(RegistrationParamsError::NoPort)?
289            .map_err(RegistrationParamsError::BadPort)?;
290        let uuid = uuid.ok_or(RegistrationParamsError::NoUuid)?;
291        let event = event.ok_or(RegistrationParamsError::NoEvent)?;
292        let info = info
293            .ok_or(RegistrationParamsError::NoInfo)?
294            .map_err(RegistrationParamsError::BadInfo)?;
295
296        Ok(RegistrationParams {
297            port,
298            uuid,
299            event,
300            info,
301        })
302    }
303}