# It is not intended for manual editing.
version = 3
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
[[package]]
name = "ahash"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
[[package]]
name = "bitflags"
version = "1.3.2"
"crossbeam-utils",
]
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
]
[[package]]
-name = "crossbeam-utils"
-version = "0.8.15"
+name = "crossbeam-deque"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
- "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
]
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crunchy"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
+
[[package]]
name = "dlv-list"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
[[package]]
name = "elgato-streamdeck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fdeflate"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
[[package]]
name = "futures"
version = "0.3.28"
"wasi",
]
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "half"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
+dependencies = [
+ "crunchy",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
"bytemuck",
"byteorder",
"color_quant",
+ "exr",
+ "gif",
"jpeg-decoder",
"num-traits",
+ "png",
+ "qoi",
+ "tiff",
]
[[package]]
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "json"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
[[package]]
name = "mqttdeck"
version = "0.1.0"
"clap",
"elgato-streamdeck",
"hidapi",
+ "image",
+ "json",
"paho-mqtt",
"rust-ini",
"tokio",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
[[package]]
name = "proc-macro-error"
version = "1.0.4"
"unicode-ident",
]
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
[[package]]
name = "quote"
version = "1.0.28"
"proc-macro2",
]
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
[[package]]
name = "rust-ini"
version = "0.18.0"
"windows-sys",
]
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
[[package]]
name = "slab"
version = "0.4.8"
"autocfg",
]
+[[package]]
+name = "smallvec"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+
[[package]]
name = "strsim"
version = "0.10.0"
"syn 2.0.18",
]
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
[[package]]
name = "tokio"
version = "1.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
[[package]]
name = "winapi"
version = "0.3.9"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
clap = { version = "4.0.32", features = ["derive"] }
elgato-streamdeck = { version = "0.5.0", features = ["async"] }
hidapi = { version = "2.6.3", default-features = false, features = ["linux-static-hidraw"] }
+image = "0.24.7"
+json = "0.12.4"
paho-mqtt = "0.12.5"
rust-ini = "0.18.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use clap::Parser;
use elgato_streamdeck::{list_devices, new_hidapi, AsyncStreamDeck, StreamDeckInput};
+use image::{DynamicImage, ImageBuffer, RgbImage};
use ini::Ini;
use paho_mqtt as mqtt;
use std::process;
verbose: bool,
}
+fn str_to_colour(colour: &str) -> Result<[u8; 3], &'static str> {
+ match colour {
+ "black" => Ok([0, 0, 0]),
+ "blue" => Ok([0, 0, 255]),
+ "cyan" => Ok([0, 255, 255]),
+ "green" => Ok([0, 255, 0]),
+ "magenta" => Ok([255, 0, 255]),
+ "red" => Ok([255, 0, 0]),
+ "white" => Ok([255, 255, 255]),
+ "yellow" => Ok([255, 255, 0]),
+ _ => Err("Unknown colour"),
+ }
+}
+
+async fn send_command_response(cli: &mqtt::AsyncClient, topic: &str, resp: &str) {
+ let msg = mqtt::MessageBuilder::new()
+ .topic(topic)
+ .payload(resp)
+ .qos(1)
+ .finalize();
+
+ if let Err(e) = cli.publish(msg).wait() {
+ println!("Error sending response to command: {}", e);
+ }
+}
+
+async fn handle_commands(
+ cli: mqtt::AsyncClient,
+ strm: mqtt::AsyncReceiver<Option<mqtt::Message>>,
+ deck: AsyncStreamDeck,
+) {
+ while let Ok(msg_opt) = strm.recv().await {
+ if let Some(msg) = msg_opt {
+ let data = std::str::from_utf8(msg.payload()).unwrap();
+ let command = json::parse(data).unwrap();
+ let resp_topic = msg.topic().replace("/cmnd", "/result");
+
+ if command["action"].is_null() {
+ send_command_response(&cli, &resp_topic, "no action supplied").await;
+ continue;
+ }
+
+ match command["action"].as_str() {
+ Some("set-colour") => {
+ if command["button"].is_null() {
+ send_command_response(&cli, &resp_topic, "no button supplied").await;
+ continue;
+ }
+ if command["colour"].is_null() {
+ send_command_response(&cli, &resp_topic, "no colour supplied").await;
+ continue;
+ }
+
+ let colour = match str_to_colour(command["colour"].as_str().unwrap()) {
+ Ok(colour) => colour,
+ Err(e) => {
+ send_command_response(&cli, &resp_topic, e).await;
+ continue;
+ }
+ };
+
+ let mut img: RgbImage = ImageBuffer::new(72, 72);
+
+ for (_, _, pixel) in img.enumerate_pixels_mut() {
+ *pixel = image::Rgb(colour);
+ }
+
+ deck.set_button_image(
+ command["button"].as_u8().unwrap(),
+ DynamicImage::ImageRgb8(img),
+ )
+ .await
+ .unwrap();
+ send_command_response(&cli, &resp_topic, "OK").await;
+ }
+ _ => send_command_response(&cli, &resp_topic, "Unknown action").await,
+ }
+ } else {
+ println!("Lost connection. Attempting reconnect...");
+ }
+ }
+}
+
#[tokio::main]
async fn main() {
let args = Args::parse();
println!("Connecting to the MQTT server");
}
- let cli = mqtt::AsyncClient::new(host).unwrap_or_else(|err| {
+ let mut cli = mqtt::AsyncClient::new(host).unwrap_or_else(|err| {
println!("Error creating the MQTT client: {}", err);
process::exit(1);
});
+ // Get an MQTT stream, allow for 10 messages
+ let strm = cli.get_stream(10);
+ let cmd_topic = format!("{}/cmnd", &conf["MQTT"]["prefix"]);
+
let ssl_opts = mqtt::SslOptionsBuilder::new().finalize();
let lwt_topic = format!("{}/LWT", &conf["MQTT"]["prefix"]);
println!("Error sending LWT present message: {:?}", e);
}
+ // Subscribe to our command topic
+ cli.subscribe(cmd_topic, 0);
+
let mut keystates = vec![false; kind.key_count() as usize];
let quit = false;
+ // Kick off a handler for our incoming MQTT commands
+ tokio::spawn(handle_commands(cli.clone(), strm, deck.clone()));
+
while !quit {
let input = deck.read_input(5.0).await.unwrap();