diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..23fd35f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 05941f5..321c7f9 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -39,13 +39,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" [[package]] -name = "app" -version = "0.1.0" +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ - "serde", - "serde_json", - "tauri", - "tauri-build", + "num-traits", ] [[package]] @@ -800,6 +799,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + [[package]] name = "flate2" version = "1.0.22" @@ -1466,6 +1474,18 @@ version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0005d08a8f7b65fb8073cb697aa0b12b631ed251ce73d862ce50eeb52ce3b50" +[[package]] +name = "libusb1-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8772b7e8d4d988e19684aec5a3f5e470ecaf5c705cf0303da3973508e873027" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.5" @@ -1868,6 +1888,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "palette" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9735f7e1e51a3f740bacd5dc2724b61a7806f23597a8736e679f38ee3435d18" +dependencies = [ + "approx", + "num-traits", + "palette_derive", + "phf 0.9.0", +] + +[[package]] +name = "palette_derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7799c3053ea8a6d8a1193c7ba42f534e7863cf52e378a7f90406f4a645d33bad" +dependencies = [ + "find-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pango" version = "0.14.8" @@ -1956,6 +2000,17 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros 0.9.0", + "phf_shared 0.9.0", + "proc-macro-hack", +] + [[package]] name = "phf" version = "0.10.1" @@ -1987,6 +2042,16 @@ dependencies = [ "rand 0.7.3", ] +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared 0.9.0", + "rand 0.8.4", +] + [[package]] name = "phf_generator" version = "0.10.0" @@ -2011,6 +2076,20 @@ dependencies = [ "syn", ] +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator 0.9.1", + "phf_shared 0.9.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -2034,6 +2113,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.10.0" @@ -2406,6 +2494,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "rusb" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b454219aa5007af92a042ec13b2035325318a21d3c6be18bf592f841430794" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -2641,6 +2739,18 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +[[package]] +name = "slidershim" +version = "0.1.0" +dependencies = [ + "palette", + "rusb", + "serde", + "serde_json", + "tauri", + "tauri-build", +] + [[package]] name = "smallvec" version = "1.8.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f8a45b2..32a5cf9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "app" +name = "slidershim" version = "0.1.0" -description = "A Tauri App" -authors = ["you"] +description = "slidershim" +authors = ["4yn"] license = "" repository = "" -default-run = "app" +default-run = "slidershim" edition = "2018" build = "src/build.rs" @@ -18,6 +18,8 @@ tauri-build = { version = "1.0.0-beta.4" } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.0.0-beta.8", features = ["api-all", "system-tray"] } +rusb = "0.9.0" +palette = "0.6.0" [features] default = [ "custom-protocol" ] diff --git a/src-tauri/src/bin/test.rs b/src-tauri/src/bin/test.rs new file mode 100644 index 0000000..77b26f0 --- /dev/null +++ b/src-tauri/src/bin/test.rs @@ -0,0 +1,20 @@ +extern crate slidershim; + +use std::io; + +use slidershim::slider_io::{config::Config, manager::Manager}; + +fn main() { + let config = Config::from_str( + r#"{ + "deviceMode": "yuancon", + "outputMode": "none", + "ledMode": "reactive-8" + }"#, + ); + + let manager = Manager::new(config); + + let mut input = String::new(); + let string = io::stdin().read_line(&mut input).unwrap(); +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..6405d83 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] +#![feature(div_duration)] + +pub mod slider_io; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 645c76e..0dc9a7b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,6 +2,9 @@ all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] +#![feature(div_duration)] + +mod slider_io; use tauri::{ AppHandle, CustomMenuItem, Event, Manager, Runtime, SystemTray, SystemTrayEvent, SystemTrayMenu, diff --git a/src-tauri/src/slider_io/config.rs b/src-tauri/src/slider_io/config.rs new file mode 100644 index 0000000..5b8b37b --- /dev/null +++ b/src-tauri/src/slider_io/config.rs @@ -0,0 +1,109 @@ +use serde_json::Value; +use std::convert::TryFrom; + +#[derive(Debug, Clone)] +pub enum DeviceMode { + None, + Tasoller { version: i64 }, + Yuancon, + Brokenithm { ground_only: bool }, +} + +#[derive(Debug, Clone, Copy)] +pub enum KeyboardLayout { + Tasoller, + Yuancon, + Deemo, +} + +#[derive(Debug, Clone)] +pub enum OutputMode { + None, + Keyboard { + layout: KeyboardLayout, + sensitivity: u8, + }, + Websocket { + url: String, + }, +} + +#[derive(Debug, Clone, Copy)] +pub enum ReactiveLayout { + Four, + Eight, + Sixteen, +} + +#[derive(Debug, Clone)] +pub enum LedMode { + None, + Reactive { layout: ReactiveLayout }, + Attract, + Test, + Websocket { url: String }, +} + +#[derive(Debug, Clone)] +pub struct Config { + pub raw: String, + pub device_mode: DeviceMode, + pub output_mode: OutputMode, + pub led_mode: LedMode, +} + +impl Config { + pub fn from_str(s: &str) -> Config { + let v: Value = serde_json::from_str(s).unwrap(); + + Config { + raw: s.to_string(), + device_mode: match v["deviceMode"].as_str().unwrap() { + "none" => DeviceMode::None, + "tasoller-one" => DeviceMode::Tasoller { version: 1 }, + "tasoller-two" => DeviceMode::Tasoller { version: 2 }, + "yuancon" => DeviceMode::Yuancon, + "brokenithm" => DeviceMode::Brokenithm { ground_only: false }, + "brokenithm-ground" => DeviceMode::Brokenithm { ground_only: true }, + _ => panic!("Invalid device mode"), + }, + output_mode: match v["outputMode"].as_str().unwrap() { + "none" => OutputMode::None, + "kb-32-tasoller" => OutputMode::Keyboard { + layout: KeyboardLayout::Tasoller, + sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64().unwrap()).unwrap(), + }, + "kb-32-yuancon" => OutputMode::Keyboard { + layout: KeyboardLayout::Yuancon, + sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64().unwrap()).unwrap(), + }, + "kb-6-deemo" => OutputMode::Keyboard { + layout: KeyboardLayout::Deemo, + sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64().unwrap()).unwrap(), + }, + "websocket" => OutputMode::Websocket { + url: v["outputWebsocketUrl"].to_string(), + }, + _ => panic!("Invalid output mode"), + }, + led_mode: match v["ledMode"].as_str().unwrap() { + "none" => LedMode::None, + "reactive-4" => LedMode::Reactive { + layout: ReactiveLayout::Four, + }, + "reactive-8" => LedMode::Reactive { + layout: ReactiveLayout::Eight, + }, + "reactive-16" => LedMode::Reactive { + layout: ReactiveLayout::Sixteen, + }, + "attract" => LedMode::Attract, + "test" => LedMode::Test, + "websocket" => LedMode::Websocket { + url: v["ledWebsocketUrl"].to_string(), + }, + _ => panic!("Invalid led mode"), + }, + } + } +} diff --git a/src-tauri/src/slider_io/controller_state.rs b/src-tauri/src/slider_io/controller_state.rs new file mode 100644 index 0000000..26c3785 --- /dev/null +++ b/src-tauri/src/slider_io/controller_state.rs @@ -0,0 +1,58 @@ +use std::{ + sync::{Arc, Mutex}, + time::Instant, +}; + +pub struct ControllerState { + pub ground_state: [u8; 32], + pub air_state: [u8; 6], + pub extra_state: [u8; 3], +} + +impl ControllerState { + pub fn new() -> Self { + Self { + ground_state: [0; 32], + air_state: [0; 6], + extra_state: [0; 3], + } + } +} + +pub struct LedState { + pub led_state: [u8; 3 * 31], + pub dirty: bool, + pub start: Instant, +} + +impl LedState { + pub fn new() -> Self { + Self { + led_state: [0; 3 * 31], + dirty: false, + start: Instant::now(), + } + } +} + +pub struct FullState { + pub controller_state: Arc>, + pub led_state: Arc>, +} + +impl FullState { + pub fn new() -> Self { + Self { + controller_state: Arc::new(Mutex::new(ControllerState::new())), + led_state: Arc::new(Mutex::new(LedState::new())), + } + } + + pub fn clone_controller(&self) -> Arc> { + Arc::clone(&self.controller_state) + } + + pub fn clone_led(&self) -> Arc> { + Arc::clone(&self.led_state) + } +} diff --git a/src-tauri/src/slider_io/device.rs b/src-tauri/src/slider_io/device.rs new file mode 100644 index 0000000..672a180 --- /dev/null +++ b/src-tauri/src/slider_io/device.rs @@ -0,0 +1,88 @@ +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread::{self, JoinHandle}, +}; + +use crate::slider_io::{config::DeviceMode, controller_state::FullState, hid}; + +pub struct DeviceThread { + thread: Option>, + stop_signal: Arc, +} + +impl DeviceThread { + pub fn new(state: &FullState, mode: DeviceMode) -> Self { + let controller_state = state.clone_controller(); + let led_state = state.clone_led(); + let stop_signal = Arc::new(AtomicBool::new(false)); + + let stop_signal_clone = Arc::clone(&stop_signal); + Self { + thread: Some(match mode { + DeviceMode::None => thread::spawn(|| {}), + DeviceMode::Tasoller { .. } => thread::spawn(|| {}), + DeviceMode::Yuancon => thread::spawn(move || { + hid::poll_controller( + 0x1973, + 0x2001, + move |buf| { + if (buf.len != 34) { + return; + } + + let mut controller_state_handle = controller_state.lock().unwrap(); + controller_state_handle + .ground_state + .clone_from_slice(&buf.data[2..34]); + for i in 0..6 { + controller_state_handle.air_state[i ^ 1] = + if buf.data[0] & (1 << i) == 0 { 1 } else { 0 }; + } + for i in 0..3 { + controller_state_handle.extra_state[i] = + if buf.data[1] & (1 << i) == 0 { 1 } else { 0 }; + } + + // println!("{:?}", controller_state_handle.ground_state); + }, + move |buf| { + let mut led_state_handle = led_state.lock().unwrap(); + if led_state_handle.dirty { + buf.len = 31 * 2; + buf + .data + .chunks_mut(2) + .take(31) + .zip(led_state_handle.led_state.chunks(3).rev()) + .for_each(|(buf_chunk, state_chunk)| { + buf_chunk[0] = (state_chunk[0] << 3 & 0xe0) | (state_chunk[2] >> 3); + buf_chunk[1] = (state_chunk[1] & 0xf8) | (state_chunk[0] >> 5); + }); + led_state_handle.dirty = false; + } + }, + &stop_signal_clone, + ) + // .unwrap_or_else(|err| { + // println!("Device thread: {:?}", err); + // }); + .unwrap(); + }), + DeviceMode::Brokenithm { .. } => thread::spawn(|| {}), + }), + stop_signal: stop_signal, + } + } +} + +impl Drop for DeviceThread { + fn drop(&mut self) { + self.stop_signal.swap(true, Ordering::SeqCst); + if self.thread.is_some() { + self.thread.take().unwrap().join().ok(); + } + } +} diff --git a/src-tauri/src/slider_io/hid.rs b/src-tauri/src/slider_io/hid.rs new file mode 100644 index 0000000..a12f3b8 --- /dev/null +++ b/src-tauri/src/slider_io/hid.rs @@ -0,0 +1,124 @@ +use std::{ + error, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, + time::Duration, +}; + +use rusb::{self, UsbContext}; + +const timeout: Duration = Duration::from_millis(20); + +pub struct Buffer { + pub data: [u8; 128], + pub len: usize, +} + +impl Buffer { + pub fn new() -> Self { + Buffer { + data: [0; 128], + len: 0, + } + } + + fn slice(&self) -> &[u8] { + &self.data[0..self.len] + } +} + +pub fn poll_controller( + vid: u16, + pid: u16, + read_callback: impl Fn(&Buffer) -> (), + write_callback: impl Fn(&mut Buffer) -> (), + // write_buf: Arc>, + stop: &AtomicBool, +) -> Result<(), Box> { + // println!("Getting context"); + // let mut context = rusb::Context::new().unwrap(); + // println!("Getting devices"); + // let devices: Vec> = context + // .devices() + // .unwrap() + // .iter() + // .filter(|d| { + // d.device_descriptor() + // .map(|d| d.vendor_id() == vid && d.product_id() == pid) + // .unwrap_or(false) + // }) + // .collect(); + // println!("Found {:?}", devices); + // let mut handle = devices[0].open().unwrap(); + + let mut handle = rusb::open_device_with_vid_pid(vid, pid).unwrap(); + println!("Found device {:?}", handle); + // .ok_or("Cannot find usb device".to_string())?; + + // let device = handle.device(); + if handle.kernel_driver_active(0).unwrap_or(false) { + println!("Disabling kernel driver"); + handle.detach_kernel_driver(0)?; + } + + println!("Kernel driver OK"); + handle.set_active_configuration(1)?; + println!("Configuration OK"); + handle.claim_interface(0)?; + println!("Interface OK"); + + let mut in_buf = Buffer::new(); + let mut led_buf = Buffer::new(); + loop { + { + // Read loop + + let res = handle + .read_interrupt(0x81, &mut in_buf.data, timeout) + .unwrap_or(0); + + in_buf.len = res; + + // println!("Read {:?}", res); + // println!("Data {:?}", in_buf.data); + + if in_buf.len != 0 { + read_callback(&in_buf); + } + } + + { + // Write loop + write_callback(&mut led_buf); + if led_buf.len != 0 { + let res = handle + .write_interrupt(0x02, led_buf.slice(), timeout) + .unwrap_or(0); + + // println!( + // "Sent {:?} {:?} {:?}", + // led_buf.len, + // res, + // led_buf.slice().len() + // ); + if res == led_buf.len + 1 { + led_buf.len = 0; + } + } + } + + { + if stop.load(Ordering::SeqCst) { + break; + } + } + } + + println!("HID thread stopped"); + + handle.release_interface(0)?; + + Ok(()) +} diff --git a/src-tauri/src/slider_io/led.rs b/src-tauri/src/slider_io/led.rs new file mode 100644 index 0000000..258ccfe --- /dev/null +++ b/src-tauri/src/slider_io/led.rs @@ -0,0 +1,153 @@ +use std::{ + borrow::BorrowMut, + ops::{Deref, DerefMut}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread::{self, JoinHandle}, + time::{Duration, Instant}, +}; + +use palette::{FromColor, Hsv, Srgb}; + +use crate::slider_io::{ + config::{LedMode, ReactiveLayout}, + controller_state::{ControllerState, FullState, LedState}, +}; + +fn update_reactive( + controller_state: &ControllerState, + led_state: &mut LedState, + reactive_layout: &ReactiveLayout, + sensitivity: u8, +) { + let splits = match reactive_layout { + ReactiveLayout::Four => 4, + ReactiveLayout::Eight => 8, + ReactiveLayout::Sixteen => 16, + }; + let buttons_per_split = 32 / splits; + + let banks: Vec = controller_state + .ground_state + .chunks(buttons_per_split) + .map(|x| x.iter().max().unwrap() > &sensitivity) + .collect(); + + // (0..splits) + // .map(|i| { + // controller_state.ground_state[i * buttons_per_split..(i + 1) * buttons_per_split] + // .iter() + // .max() + // .unwrap() + // > &sensitivity + // }) + // .collect(); + + led_state + .led_state + .chunks_mut(3) + .enumerate() + .for_each(|(idx, chunk)| match (idx + 1) % buttons_per_split { + 0 => { + chunk[0] = 255; + chunk[1] = 0; + chunk[2] = 255; + } + _ => match banks[idx / buttons_per_split] { + true => { + chunk[0] = 255; + chunk[1] = 0; + chunk[2] = 255; + } + false => { + chunk[0] = 255; + chunk[1] = 255; + chunk[2] = 0; + } + }, + }); + + // println!("{:?}", controller_state.ground_state); + // println!("{:?}", banks); + // println!("{:?}", led_state.led_state); + + led_state.dirty = true; +} + +fn update_attract(led_state: &mut LedState) { + let now = Instant::now(); + let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0; + + led_state + .led_state + .chunks_mut(3) + .enumerate() + .for_each(|(idx, chunk)| { + let slice_theta = (&theta + (idx as f64) / 31.0) % 1.0; + let color = Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::(); + chunk[0] = color.red; + chunk[1] = color.green; + chunk[2] = color.blue; + }); + + // println!("{} {:?}", theta, led_state.led_state); + + led_state.dirty = true; +} + +pub struct LedThread { + thread: Option>, + stop_signal: Arc, +} + +impl LedThread { + pub fn new(state: &FullState, mode: LedMode) -> Self { + let stop_signal = Arc::new(AtomicBool::new(false)); + + let stop_signal_clone = Arc::clone(&stop_signal); + let controller_state = state.clone_controller(); + let led_state = state.clone_led(); + Self { + thread: Some(thread::spawn(move || loop { + // println!("Led thread: {:?}", mode); + match &mode { + LedMode::Reactive { layout } => { + let controller_state_handle = controller_state.lock().unwrap(); + let mut led_state_handle = led_state.lock().unwrap(); + update_reactive( + controller_state_handle.deref(), + led_state_handle.deref_mut(), + layout, + 20, + ) + } + LedMode::Attract => { + let mut led_state_handle = led_state.lock().unwrap(); + update_attract(led_state_handle.deref_mut()); + } + _ => {} + } + + { + if stop_signal_clone.load(Ordering::SeqCst) { + break; + } + } + + thread::sleep(Duration::from_millis(33)) + })), + stop_signal, + } + } +} + +impl Drop for LedThread { + fn drop(&mut self) { + self.stop_signal.swap(true, Ordering::SeqCst); + if self.thread.is_some() { + self.thread.take().unwrap().join().ok(); + } + } +} diff --git a/src-tauri/src/slider_io/manager.rs b/src-tauri/src/slider_io/manager.rs new file mode 100644 index 0000000..245b5c6 --- /dev/null +++ b/src-tauri/src/slider_io/manager.rs @@ -0,0 +1,27 @@ +use crate::slider_io::{ + config::Config, controller_state::FullState, device::DeviceThread, led::LedThread, +}; + +pub struct Manager { + state: FullState, + config: Config, + device_thread: DeviceThread, + led_thread: LedThread, +} + +impl Manager { + pub fn new(config: Config) -> Self { + let state = FullState::new(); + let device_thread = DeviceThread::new(&state, config.device_mode.clone()); + let led_thread = LedThread::new(&state, config.led_mode.clone()); + + println!("Starting manager with config: {:?}", config); + + Self { + state, + config, + device_thread, + led_thread, + } + } +} diff --git a/src-tauri/src/slider_io/mod.rs b/src-tauri/src/slider_io/mod.rs new file mode 100644 index 0000000..e22d6c5 --- /dev/null +++ b/src-tauri/src/slider_io/mod.rs @@ -0,0 +1,6 @@ +pub mod config; +pub mod controller_state; +pub mod device; +pub mod hid; +pub mod led; +pub mod manager; diff --git a/src/App.svelte b/src/App.svelte index 90a0242..7271bc1 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -6,21 +6,21 @@ let deviceMode = "none"; let outputMode = "none"; - let lightingMode = "none"; + let ledMode = "none"; let keyboardSensitivity = 20; - let websocketOutputUrl = "http://localhost:3000"; - let websocketLedUrl = "http://localhost:3001"; + let outputWebsocketUrl = "http://localhost:3000"; + let ledWebsocketUrl = "http://localhost:3001"; onMount(async () => { await listen("showConfig", (event) => { const payload: any = event.payload; deviceMode = payload.deviceMode; outputMode = payload.outputMode; - lightingMode = payload.lightingMode; + ledMode = payload.ledMode; keyboardSensitivity = payload.keyboardSensitivity; - websocketOutputUrl = payload.websocketOutputUrl; - websocketLedUrl = payload.websocketLedUrl; + outputWebsocketUrl = payload.outputWebsocketUrl; + ledWebsocketUrl = payload.ledWebsocketUrl; }); }); @@ -29,10 +29,10 @@ await emit("setConfig", JSON.stringify({ deviceMode, outputMode, - lightingMode, + ledMode, keyboardSensitivity, - websocketOutputUrl, - websocketLedUrl + outputWebsocketUrl, + ledWebsocketUrl })); console.log("Done"); } @@ -49,8 +49,8 @@
- slidershim + slidershim
@@ -61,7 +61,8 @@
+
{/if}
LED Mode
- @@ -129,11 +130,11 @@
- {#if lightingMode === "websocket"} + {#if ledMode === "websocket"}
LED URL
- +
{/if}