From 5253f25a215c386e03a2f6b4597c19985e47a9b3 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Mon, 4 Sep 2023 19:35:22 +0100 Subject: [PATCH] Add initial async framework To move to being able to accept commands controlling button images move to using the async version of streamdeck. --- Cargo.lock | 46 ++++++++++++++++++++++++++++++++--- Cargo.toml | 3 ++- src/main.rs | 70 +++++++++++++++++++++++++++-------------------------- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e8565f..089f382 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-recursion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -215,8 +226,10 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bef35ad55f9ef3a82a7a6724a4069e931a323f75cde270ad9780c2e1991371b" dependencies = [ + "async-recursion", "hidapi", "image", + "tokio", ] [[package]] @@ -465,9 +478,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", @@ -590,6 +603,7 @@ dependencies = [ "elgato-streamdeck", "paho-mqtt", "rust-ini", + "tokio", ] [[package]] @@ -944,15 +958,39 @@ dependencies = [ [[package]] name = "tiff" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" dependencies = [ "flate2", "jpeg-decoder", "weezl", ] +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "num_cpus", + "pin-project-lite", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "unicode-ident" version = "1.0.9" diff --git a/Cargo.toml b/Cargo.toml index 5d07574..1ae3215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] clap = { version = "4.0.32", features = ["derive"] } -elgato-streamdeck = "0.3.5" +elgato-streamdeck = { version = "0.3.5", features = ["async"] } paho-mqtt = "0.12.1" rust-ini = "0.18.0" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/src/main.rs b/src/main.rs index 95ae7b0..89fc007 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,10 @@ // Copyright 2023 Jonathan McDowell use clap::Parser; -use elgato_streamdeck as streamdeck; +use elgato_streamdeck::{list_devices, new_hidapi, AsyncStreamDeck, StreamDeckInput}; use ini::Ini; use paho_mqtt as mqtt; use std::process; -use std::time::Duration; #[derive(Parser)] struct Args { @@ -19,7 +18,8 @@ struct Args { verbose: bool, } -fn main() { +#[tokio::main] +async fn main() { let args = Args::parse(); let conf = Ini::load_from_file(&args.config).unwrap_or_else(|err| { println!("Failed to load config file ({}): {}", args.config, err); @@ -27,11 +27,11 @@ fn main() { }); let host = format!("mqtts://{}:8883", &conf["MQTT"]["broker"]); - let hid = streamdeck::new_hidapi().unwrap_or_else(|err| { + let hid = new_hidapi().unwrap_or_else(|err| { println!("Failed to create HID API context: {}", err); process::exit(1); }); - let mut decks = streamdeck::list_devices(&hid); + let mut decks = list_devices(&hid); if decks.len() == 0 { println!("Failed to find Stream Deck device"); @@ -40,7 +40,7 @@ fn main() { let (kind, serial) = decks.remove(0); - let deck = streamdeck::StreamDeck::connect(&hid, kind, &serial).unwrap_or_else(|err| { + let deck = AsyncStreamDeck::connect(&hid, kind, &serial).unwrap_or_else(|err| { println!("Failed to connect to Stream Deck: {}", err); process::exit(1); }); @@ -48,8 +48,8 @@ fn main() { println!( "Connected to '{:?}' ('{}') with version '{}'", kind, - deck.serial_number().unwrap(), - deck.firmware_version().unwrap() + deck.serial_number().await.unwrap(), + deck.firmware_version().await.unwrap() ); if args.verbose { @@ -61,8 +61,7 @@ fn main() { process::exit(1); }); - let ssl_opts = mqtt::SslOptionsBuilder::new() - .finalize(); + let ssl_opts = mqtt::SslOptionsBuilder::new().finalize(); let lwt_topic = format!("{}/LWT", &conf["MQTT"]["prefix"]); @@ -100,41 +99,44 @@ fn main() { let quit = false; while !quit { - let input = deck.read_input(Some(Duration::new(5, 0))).unwrap(); + let input = deck.read_input(5.0).await.unwrap(); if !input.is_empty() { match input { - streamdeck::StreamDeckInput::ButtonStateChange(keys) => { - for key in 0..keys.len() { - if keys[key] != keystates[key] { - - if args.verbose { - println!("Button state change, key {} pressed -> {}", key, keys[key]); + StreamDeckInput::ButtonStateChange(keys) => { + for key in 0..keys.len() { + if keys[key] != keystates[key] { + if args.verbose { + println!( + "Button state change, key {} pressed -> {}", + key, keys[key] + ); + } + + let msg = mqtt::MessageBuilder::new() + .topic(format!("{}/button{}", &conf["MQTT"]["prefix"], key)) + .payload(if keys[key] { "ON" } else { "OFF" }) + .qos(1) + .finalize(); + + if let Err(e) = cli.publish(msg).wait() { + println!("Error sending key state update message: {:?}", e); + } + + keystates[key] = keys[key]; } - - let msg = mqtt::MessageBuilder::new() - .topic(format!("{}/button{}", &conf["MQTT"]["prefix"], key)) - .payload(if keys[key] { "ON" } else { "OFF"} ) - .qos(1) - .finalize(); - - if let Err(e) = cli.publish(msg).wait() { - println!("Error sending key state update message: {:?}", e); - } - - keystates[key] = keys[key]; } } - }, - _ => { - if args.verbose { - println!("Unexpected Stream Deck event: {:?}", input); + _ => { + if args.verbose { + println!("Unexpected Stream Deck event: {:?}", input); + } } - }, } } } + cli.remove_message_callback(); let tok = cli.disconnect(None); tok.wait().unwrap(); } -- 2.39.5