ajazz_sdk/
asynchronous.rs

1//! Code from this module is using [block_in_place](tokio::task::block_in_place),
2//! and so they cannot be used in [current_thread](tokio::runtime::Builder::new_current_thread) runtimes
3
4use std::sync::Arc;
5use std::time::Duration;
6
7use hidapi::{HidApi, HidResult};
8use image::DynamicImage;
9use tokio::sync::Mutex;
10use tokio::task::block_in_place;
11use tokio::time::sleep;
12
13use crate::{AjazzError, AjazzInput, DeviceState, Event, Kind};
14use crate::device::{handle_input_state_change, Ajazz};
15use crate::hid::list_devices;
16use crate::images::convert_image_async;
17
18/// Actually refreshes the device list, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
19pub fn refresh_device_list_async(hidapi: &mut HidApi) -> HidResult<()> {
20    block_in_place(move || hidapi.refresh_devices())
21}
22
23/// Returns a list of devices as (Kind, Serial Number) that could be found using HidApi,
24/// can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
25///
26/// **WARNING:** To refresh the list, use [refresh_device_list]
27pub fn list_devices_async(hidapi: &HidApi) -> Vec<(Kind, String)> {
28    block_in_place(move || list_devices(hidapi))
29}
30
31/// Ajazz device interface suitable to be used in async, uses [block_in_place](block_in_place)
32/// so this wrapper cannot be used in [current_thread](tokio::runtime::Builder::new_current_thread) runtimes
33#[derive(Clone)]
34pub struct AsyncAjazz {
35    kind: Kind,
36    device: Arc<Mutex<Ajazz>>,
37}
38
39/// Static functions of the struct
40impl AsyncAjazz {
41    /// Attempts to connect to the device, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
42    pub fn connect(
43        hidapi: &HidApi,
44        kind: Kind,
45        serial: &str,
46    ) -> Result<AsyncAjazz, AjazzError> {
47        let device = block_in_place(move || Ajazz::connect(hidapi, kind, serial))?;
48
49        Ok(AsyncAjazz {
50            kind,
51            device: Arc::new(Mutex::new(device)),
52        })
53    }
54
55    /// Attempts to connect to the device, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
56    pub fn connect_with_retries(
57        hidapi: &HidApi,
58        kind: Kind,
59        serial: &str,
60        attempts: u8,
61    ) -> Result<AsyncAjazz, AjazzError> {
62        let device = block_in_place(move || {
63            Ajazz::connect_with_retries(hidapi, kind, serial, attempts)
64        })?;
65
66        Ok(AsyncAjazz {
67            kind,
68            device: Arc::new(Mutex::new(device)),
69        })
70    }
71}
72
73/// Instance methods of the struct
74impl AsyncAjazz {
75    /// Returns kind of the Stream Deck
76    pub fn kind(&self) -> Kind {
77        self.kind
78    }
79
80    /// Returns manufacturer string of the device
81    pub async fn manufacturer(&self) -> Result<String, AjazzError> {
82        let device = self.device.lock().await;
83        block_in_place(move || device.manufacturer())
84    }
85
86    /// Returns product string of the device
87    pub async fn product(&self) -> Result<String, AjazzError> {
88        let device = self.device.lock().await;
89        block_in_place(move || device.product())
90    }
91
92    /// Returns serial number of the device
93    pub async fn serial_number(&self) -> Result<String, AjazzError> {
94        let device = self.device.lock().await;
95        block_in_place(move || device.serial_number())
96    }
97
98    /// Returns firmware version of the StreamDeck
99    pub async fn firmware_version(&self) -> Result<String, AjazzError> {
100        let device = self.device.lock().await;
101        block_in_place(move || device.firmware_version())
102    }
103
104    /// Reads button states, awaits until there's data.
105    /// Poll rate determines how often button state gets checked
106    pub async fn read_input(&self, poll_rate: f32) -> Result<AjazzInput, AjazzError> {
107        loop {
108            let device = self.device.lock().await;
109            let data = block_in_place(move || device.read_input(None))?;
110
111            if !data.is_empty() {
112                return Ok(data);
113            }
114
115            sleep(Duration::from_secs_f32(1.0 / poll_rate)).await;
116        }
117    }
118
119    /// Resets the device
120    pub async fn reset(&self) -> Result<(), AjazzError> {
121        let device = self.device.lock().await;
122        block_in_place(move || device.reset())
123    }
124
125    /// Sets brightness of the device, value range is 0 - 100
126    pub async fn set_brightness(&self, percent: u8) -> Result<(), AjazzError> {
127        let device = self.device.lock().await;
128        block_in_place(move || device.set_brightness(percent))
129    }
130
131    /// Sets button's image to blank, changes must be flushed with `.flush()` before
132    /// they will appear on the device!
133    pub async fn clear_button_image(&self, key: u8) -> Result<(), AjazzError> {
134        let device = self.device.lock().await;
135        block_in_place(move || device.clear_button_image(key))
136    }
137
138    /// Sets blank images to every button, changes must be flushed with `.flush()` before
139    /// they will appear on the device!
140    pub async fn clear_all_button_images(&self) -> Result<(), AjazzError> {
141        let device = self.device.lock().await;
142        block_in_place(move || device.clear_all_button_images())
143    }
144
145    /// Sets specified button's image, changes must be flushed with `.flush()` before
146    /// they will appear on the device!
147    pub async fn set_button_image(
148        &self,
149        key: u8,
150        image: DynamicImage,
151    ) -> Result<(), AjazzError> {
152        let image = convert_image_async(self.kind, image)?;
153
154        let device = self.device.lock().await;
155        block_in_place(move || device.set_button_image_data(key, &image))
156    }
157
158    /// Sets specified button's image, changes must be flushed with `.flush()` before
159    /// they will appear on the device!
160    pub async fn set_button_image_data(
161        &self,
162        key: u8,
163        image_data: &[u8],
164    ) -> Result<(), AjazzError> {
165        let device = self.device.lock().await;
166        block_in_place(move || device.set_button_image_data(key, image_data))
167    }
168
169    /// Set logo image
170    pub async fn set_logo_image(&self, image: DynamicImage) -> Result<(), AjazzError> {
171        let device = self.device.lock().await;
172        block_in_place(move || device.set_logo_image(image))
173    }
174
175    /// Sleeps the device
176    pub async fn sleep(&self) -> Result<(), AjazzError> {
177        let device = self.device.lock().await;
178        block_in_place(move || device.sleep())
179    }
180
181    /// Make periodic events to the device, to keep it alive
182    pub async fn keep_alive(&self) -> Result<(), AjazzError> {
183        let device = self.device.lock().await;
184        block_in_place(move || device.keep_alive())
185    }
186
187    /// Shutdown the device
188    pub async fn shutdown(&self) -> Result<(), AjazzError> {
189        let device = self.device.lock().await;
190        block_in_place(move || device.shutdown())
191    }
192
193    /// Flushes the button's image to the device
194    pub async fn flush(&self) -> Result<(), AjazzError> {
195        let device = self.device.lock().await;
196        block_in_place(move || device.flush())
197    }
198
199    /// Returns button state reader for this device
200    pub fn get_reader(&self) -> Arc<AsyncDeviceStateReader> {
201        Arc::new(AsyncDeviceStateReader {
202            device: self.clone(),
203            states: Mutex::new(DeviceState {
204                buttons: vec![false; self.kind.key_count() as usize],
205                encoders: vec![false; self.kind.encoder_count() as usize],
206            }),
207        })
208    }
209}
210
211/// Button reader that keeps state of the Stream Deck and returns events instead of full states
212pub struct AsyncDeviceStateReader {
213    device: AsyncAjazz,
214    states: Mutex<DeviceState>,
215}
216
217impl AsyncDeviceStateReader {
218    /// Reads states and returns updates
219    pub async fn read(&self, poll_rate: f32) -> Result<Vec<Event>, AjazzError> {
220        let input = self.device.read_input(poll_rate).await?;
221        let mut current_state = self.states.lock().await;
222
223        let updates = handle_input_state_change(input, &mut current_state)?;
224        Ok(updates)
225    }
226}