131 lines
4.0 KiB
Rust
131 lines
4.0 KiB
Rust
use env_logger::Env;
|
|
use hidapi::HidApi;
|
|
use log::info;
|
|
use rumqttc::{Client, MqttOptions, QoS};
|
|
use std::collections::HashMap;
|
|
use std::error::Error;
|
|
use std::path::Path;
|
|
use std::thread;
|
|
|
|
mod config;
|
|
mod device;
|
|
|
|
struct State<'a> {
|
|
mqtt_servers: HashMap<String, Client>,
|
|
conf: &'a config::Config,
|
|
key_state: [bool; 16],
|
|
}
|
|
|
|
impl<'a> State<'a> {
|
|
fn init(c: &'a config::Config) -> Self {
|
|
let mut s = State {
|
|
mqtt_servers: HashMap::new(),
|
|
conf: &c,
|
|
key_state: [false; 16],
|
|
};
|
|
|
|
for (id, srv) in c.mqtt_servers.iter() {
|
|
let mut opts = MqttOptions::new(
|
|
srv.client_id
|
|
.as_ref()
|
|
.unwrap_or(&"samspad-client".to_owned()),
|
|
&srv.host,
|
|
srv.port.unwrap_or(1883),
|
|
);
|
|
// Only set credentials if we have both user and pass specified
|
|
if let (Some(u), Some(p)) = (&srv.user, &srv.pass) {
|
|
opts.set_credentials(u, p);
|
|
}
|
|
let (cli, mut conn) = Client::new(opts, 10);
|
|
thread::spawn(move || {
|
|
for notification in conn.iter() {
|
|
match notification {
|
|
Ok(n) => println!("Notification = {:?}", n),
|
|
Err(e) => {
|
|
println!("MQTT error: {}", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
s.mqtt_servers.insert(id.to_string(), cli);
|
|
}
|
|
s
|
|
}
|
|
|
|
fn handle_button(&mut self, ev: &device::ButtonEvent) {
|
|
let layer = &self.conf.layers["default"];
|
|
match ev {
|
|
device::ButtonEvent::KeyDown(key_id) => {
|
|
self.key_state[*key_id as usize] = true;
|
|
let mapping = if let Some(mapping) = layer.mappings.get(&format!("key_{}", key_id))
|
|
{
|
|
mapping
|
|
} else {
|
|
&layer.default_mapping
|
|
};
|
|
self.execute_mapping(mapping);
|
|
}
|
|
device::ButtonEvent::KeyUp(key_id) => {
|
|
self.key_state[*key_id as usize] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn execute_mapping(&mut self, m: &config::Mapping) {
|
|
match m {
|
|
config::Mapping::Print(s) => println!("{}", s),
|
|
config::Mapping::Trigger(config::Action::MQTTPub { topic, payload }) => {
|
|
let mut topic = topic.to_owned();
|
|
let srv = &self.conf.mqtt_servers["default"];
|
|
if let Some(prefix) = &srv.topic_prefix {
|
|
topic.insert_str(0, &prefix);
|
|
}
|
|
let cli = &mut self.mqtt_servers.get_mut("default").unwrap();
|
|
cli.publish(topic, QoS::AtLeastOnce, false, payload.as_bytes())
|
|
.unwrap();
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn get_led_data(&self) -> [u8; 48] {
|
|
let data = self
|
|
.key_state
|
|
.iter()
|
|
.flat_map(|s| if *s { [255, 255, 255] } else { [0, 0, 0] })
|
|
.collect::<Vec<u8>>();
|
|
|
|
data[0..48].try_into().unwrap()
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn Error>> {
|
|
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
|
let conf = config::Config::from_file(Path::new("./config.dhall"))?;
|
|
|
|
let mut state = State::init(&conf);
|
|
|
|
let hid = HidApi::new().unwrap();
|
|
|
|
// TODO: handle multiple devices
|
|
let device = device::get_devices(&hid, true)
|
|
.get(0)
|
|
.expect("No device found")
|
|
.open_device(&hid)
|
|
.expect("Couldn't open device");
|
|
|
|
let pad = device::ButtonPad::new(&device);
|
|
for ev in pad {
|
|
if let Ok(ev) = ev {
|
|
info!("{:?}", ev);
|
|
state.handle_button(&ev);
|
|
let mut data = state.get_led_data().to_vec();
|
|
data.insert(0, 0x0);
|
|
data.insert(0, 0x0);
|
|
log::info!("{:?}", data);
|
|
device.write(data.as_slice())?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|