From 36fde2527037407fc694a2d7075984b73da9a844 Mon Sep 17 00:00:00 2001
From: Daniel Schaefer <dhs@frame.work>
Date: Fri, 25 Aug 2023 17:08:35 +0800
Subject: [PATCH 1/8] release: Update udev rules path

Signed-off-by: Daniel Schaefer <dhs@frame.work>
---
 release/postinstall.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/release/postinstall.sh b/release/postinstall.sh
index 4df223d1..6c98c813 100755
--- a/release/postinstall.sh
+++ b/release/postinstall.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
 
-cp -u ./ledmatrix/50-framework.rules /etc/udev/rules.d/
+cp -u ./release/50-framework-inputmodule.rules /etc/udev/rules.d/

From 9840ad3fe7b315d3553a49b5ee32e75faafffaa7 Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Sun, 27 Aug 2023 10:06:39 -0400
Subject: [PATCH 2/8] Initial commit with working implementation

---
 Cargo.lock                             |  31 +++++++
 Cargo.toml                             |   1 +
 dbus-monitor/Cargo.toml                |  16 ++++
 dbus-monitor/src/dbus_monitor.rs       | 120 +++++++++++++++++++++++++
 dbus-monitor/src/main.rs               |   6 ++
 dbus-monitor/src/utils.rs              |  27 ++++++
 inputmodule-control/src/commands.rs    |  50 +++++++++++
 inputmodule-control/src/inputmodule.rs |  11 +--
 inputmodule-control/src/lib.rs         |   9 ++
 inputmodule-control/src/main.rs        |  49 +---------
 10 files changed, 270 insertions(+), 50 deletions(-)
 create mode 100644 dbus-monitor/Cargo.toml
 create mode 100644 dbus-monitor/src/dbus_monitor.rs
 create mode 100644 dbus-monitor/src/main.rs
 create mode 100644 dbus-monitor/src/utils.rs
 create mode 100644 inputmodule-control/src/commands.rs
 create mode 100644 inputmodule-control/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 00663417..8b8327b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -606,6 +606,17 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
 
+[[package]]
+name = "dbus"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
+dependencies = [
+ "libc",
+ "libdbus-sys",
+ "winapi",
+]
+
 [[package]]
 name = "debug-helper"
 version = "0.3.13"
@@ -820,6 +831,16 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "framework-inputmodule-dbus-monitor"
+version = "0.0.1"
+dependencies = [
+ "clap",
+ "dbus",
+ "inputmodule-control",
+ "libdbus-sys",
+]
+
 [[package]]
 name = "fuchsia-cprng"
 version = "0.1.1"
@@ -1128,6 +1149,16 @@ version = "0.2.146"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
 
+[[package]]
+name = "libdbus-sys"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
 [[package]]
 name = "libloading"
 version = "0.7.4"
diff --git a/Cargo.toml b/Cargo.toml
index e86f3e3b..f4eada3c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ members = [
     "ledmatrix",
     "fl16-inputmodules",
     "inputmodule-control",
+    "dbus-monitor"
 ]
 # Don't build all of them by default.
 # Because that'll lead to all features enabled in `fl16-inputmodules` and it
diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml
new file mode 100644
index 00000000..4849a800
--- /dev/null
+++ b/dbus-monitor/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+edition = "2021"
+name = "framework-inputmodule-dbus-monitor"
+version = "0.0.1"
+
+[dependencies]
+dbus = { version = "0.9.7", features = ["vendored"] }
+clap = { version = "4.0", features = ["derive"] }
+
+[dependencies.libdbus-sys]
+default-features = false
+features = ["vendored"]
+version = "0.2.5"
+
+[dependencies.inputmodule-control]
+path = "../inputmodule-control"
\ No newline at end of file
diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
new file mode 100644
index 00000000..811cb1ee
--- /dev/null
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -0,0 +1,120 @@
+// Mostly taken from https://github.com/diwic/dbus-rs/blob/366a6dca3d20745f5dcfa006b1b1311c376d420e/dbus/examples/monitor.rs
+
+// This programs implements the equivalent of running the "dbus-monitor" tool
+// modified to only search for messages in the org.freedesktop.Notifications interface
+use dbus::blocking::Connection;
+use dbus::channel::MatchingReceiver;
+use dbus::message::MatchRule;
+
+use dbus::Message;
+use dbus::MessageType;
+
+use std::process::Command;
+use std::time::Duration;
+
+use crate::utils;
+
+use inputmodule_control::inputmodule::find_serialdevs;
+use inputmodule_control::commands::ClapCli;
+use inputmodule_control::inputmodule::{serial_commands};
+use clap::{Parser, Subcommand};
+
+fn handle_message(msg: &Message) {
+    println!("Got message from DBus: {:?}", msg);
+
+    run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blinking"]);
+    // Can't seem to get to this second command
+    run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
+
+    println!("Message handled");
+}
+
+pub fn run_inputmodule_command(args: Vec<&str>){
+    let bin_placeholder = vec!["bin-placeholder"];
+    let full_args = [&bin_placeholder[..], &args[..]].concat();
+    let args = ClapCli::parse_from(full_args);
+    
+    serial_commands(&args);
+}
+
+pub fn run_dbus_monitor() {
+    // First open up a connection to the desired bus.
+    let conn = Connection::new_session().expect("D-Bus connection failed");
+    println!("Connection to DBus session monitor opened");
+
+    // Second create a rule to match messages we want to receive; in this example we add no
+    // further requirements, so all messages will match
+    let rule = MatchRule::new()
+        .with_type(MessageType::MethodCall)
+        .with_interface("org.freedesktop.Notifications")
+        .with_member("Notify");
+
+    // Try matching using new scheme
+    let proxy = conn.with_proxy(
+        "org.freedesktop.DBus",
+        "/org/freedesktop/DBus",
+        Duration::from_millis(5000),
+    );
+    let result: Result<(), dbus::Error> = proxy.method_call(
+        "org.freedesktop.DBus.Monitoring",
+        "BecomeMonitor",
+        (vec![rule.match_str()], 0u32),
+    );
+    println!("Monitoring DBus channel...");
+
+    match result {
+        // BecomeMonitor was successful, start listening for messages
+        Ok(_) => {
+            conn.start_receive(
+                rule,
+                Box::new(|msg, _| {
+                    println!("Start listening");
+                    handle_message(&msg);
+                    true
+                }),
+            );
+        }
+        // BecomeMonitor failed, fallback to using the old scheme
+        Err(e) => {
+            eprintln!(
+                "Failed to BecomeMonitor: '{}', falling back to eavesdrop",
+                e
+            );
+
+            // First, we'll try "eavesdrop", which as the name implies lets us receive
+            // *all* messages, not just ours.
+            let rule_with_eavesdrop = {
+                let mut rule = rule.clone();
+                rule.eavesdrop = true;
+                rule
+            };
+
+            let result = conn.add_match(rule_with_eavesdrop, |_: (), _, msg| {
+                handle_message(&msg);
+                true
+            });
+
+            match result {
+                Ok(_) => {
+                    // success, we're now listening
+                }
+                // This can sometimes fail, for example when listening to the system bus as a non-root user.
+                // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`:
+                Err(e) => {
+                    eprintln!("Failed to eavesdrop: '{}', trying without it", e);
+                    conn.add_match(rule, |_: (), _, msg| {
+                        handle_message(&msg);
+                        true
+                    })
+                    .expect("add_match failed");
+                }
+            }
+        }
+    }
+
+    // Loop and print out all messages received (using handle_message()) as they come.
+    // Some can be quite large, e.g. if they contain embedded images..
+    loop {
+        conn.process(Duration::from_millis(1000)).unwrap();
+    }
+}
diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs
new file mode 100644
index 00000000..ddcfa2ae
--- /dev/null
+++ b/dbus-monitor/src/main.rs
@@ -0,0 +1,6 @@
+mod dbus_monitor;
+mod utils;
+
+fn main() {
+    dbus_monitor::run_dbus_monitor();
+}
diff --git a/dbus-monitor/src/utils.rs b/dbus-monitor/src/utils.rs
new file mode 100644
index 00000000..93d4914d
--- /dev/null
+++ b/dbus-monitor/src/utils.rs
@@ -0,0 +1,27 @@
+use std::process::Child;
+use std::process::Command;
+use std::time::Duration;
+use std::time::Instant;
+
+pub fn run_command_with_timeout(
+    command: &str,
+    timeout_seconds: u64,
+) -> Result<Child, Box<dyn std::error::Error>> {
+    let mut child_process = Command::new("bash").arg("-c").arg(command).spawn()?;
+
+    let start_time = Instant::now();
+    while start_time.elapsed() < Duration::from_secs(timeout_seconds) {
+        if let Some(exit_status) = child_process.try_wait()? {
+            println!(
+                "Command finished before the specified duration. Exit status: {:?}",
+                exit_status
+            );
+            return Ok(child_process);
+        }
+    }
+
+    child_process.kill()?; // Attempt to kill the process
+
+    println!("Command terminated after {} seconds", timeout_seconds);
+    Ok(child_process)
+}
diff --git a/inputmodule-control/src/commands.rs b/inputmodule-control/src/commands.rs
new file mode 100644
index 00000000..fa122aab
--- /dev/null
+++ b/inputmodule-control/src/commands.rs
@@ -0,0 +1,50 @@
+#![allow(clippy::needless_range_loop)]
+#![allow(clippy::single_match)]
+
+use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID};
+use crate::b1display::B1DisplaySubcommand;
+use crate::c1minimal::C1MinimalSubcommand;
+use crate::ledmatrix::LedMatrixSubcommand;
+
+use clap::{Parser, Subcommand};
+
+#[derive(Subcommand, Debug)]
+pub enum Commands {
+    LedMatrix(LedMatrixSubcommand),
+    B1Display(B1DisplaySubcommand),
+    C1Minimal(C1MinimalSubcommand),
+}
+
+impl Commands {
+    pub fn to_pid(&self) -> u16 {
+        match self {
+            Self::LedMatrix(_) => LED_MATRIX_PID,
+            Self::B1Display(_) => B1_LCD_PID,
+            Self::C1Minimal(_) => 0x22,
+        }
+    }
+}
+
+/// RAW HID and VIA commandline for QMK devices
+#[derive(Parser, Debug)]
+#[command(version, arg_required_else_help = true)]
+pub struct ClapCli {
+    #[command(subcommand)]
+    pub command: Option<Commands>,
+
+    /// List connected HID devices
+    #[arg(short, long)]
+    pub list: bool,
+
+    /// Verbose outputs to the console
+    #[arg(short, long)]
+    pub verbose: bool,
+
+    /// Serial device, like /dev/ttyACM0 or COM0
+    #[arg(long)]
+    pub serial_dev: Option<String>,
+
+    /// Retry connecting to the device until it works
+    #[arg(long)]
+    pub wait_for_device: bool,
+}
diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs
index 25baa57e..81457fc2 100644
--- a/inputmodule-control/src/inputmodule.rs
+++ b/inputmodule-control/src/inputmodule.rs
@@ -12,6 +12,7 @@ use crate::b1display::{B1Pattern, Fps, PowerMode};
 use crate::c1minimal::Color;
 use crate::font::{convert_font, convert_symbol};
 use crate::ledmatrix::{Game, GameOfLifeStartParam, Pattern};
+use crate::commands::ClapCli;
 
 const FWK_MAGIC: &[u8] = &[0x32, 0xAC];
 pub const FRAMEWORK_VID: u16 = 0x32AC;
@@ -99,7 +100,7 @@ fn match_serialdevs(
     }
 }
 
-pub fn find_serialdevs(args: &crate::ClapCli, wait_for_device: bool) -> (Vec<String>, bool) {
+pub fn find_serialdevs(args: &ClapCli, wait_for_device: bool) -> (Vec<String>, bool) {
     let mut serialdevs: Vec<String>;
     let mut waited = false;
     loop {
@@ -151,7 +152,7 @@ pub fn find_serialdevs(args: &crate::ClapCli, wait_for_device: bool) -> (Vec<Str
 }
 
 /// Commands that interact with serial devices
-pub fn serial_commands(args: &crate::ClapCli) {
+pub fn serial_commands(args: &ClapCli) {
     let (serialdevs, waited): (Vec<String>, bool) = find_serialdevs(args, args.wait_for_device);
     if serialdevs.is_empty() {
         println!("Failed to find serial devivce. Please manually specify with --serial-dev");
@@ -164,7 +165,7 @@ pub fn serial_commands(args: &crate::ClapCli) {
 
     match &args.command {
         // TODO: Handle generic commands without code deduplication
-        Some(crate::Commands::LedMatrix(ledmatrix_args)) => {
+        Some(crate::commands::Commands::LedMatrix(ledmatrix_args)) => {
             for serialdev in &serialdevs {
                 if args.verbose {
                     println!("Selected serialdev: {:?}", serialdev);
@@ -262,7 +263,7 @@ pub fn serial_commands(args: &crate::ClapCli) {
                 clock_cmd(&serialdevs);
             }
         }
-        Some(crate::Commands::B1Display(b1display_args)) => {
+        Some(crate::commands::Commands::B1Display(b1display_args)) => {
             for serialdev in &serialdevs {
                 if args.verbose {
                     println!("Selected serialdev: {:?}", serialdev);
@@ -312,7 +313,7 @@ pub fn serial_commands(args: &crate::ClapCli) {
                 }
             }
         }
-        Some(crate::Commands::C1Minimal(c1minimal_args)) => {
+        Some(crate::commands::Commands::C1Minimal(c1minimal_args)) => {
             for serialdev in &serialdevs {
                 if args.verbose {
                     println!("Selected serialdev: {:?}", serialdev);
diff --git a/inputmodule-control/src/lib.rs b/inputmodule-control/src/lib.rs
new file mode 100644
index 00000000..a421c538
--- /dev/null
+++ b/inputmodule-control/src/lib.rs
@@ -0,0 +1,9 @@
+#![allow(clippy::needless_range_loop)]
+
+mod b1display;
+mod c1minimal;
+mod font;
+pub mod inputmodule;
+mod ledmatrix;
+pub mod commands;
+
diff --git a/inputmodule-control/src/main.rs b/inputmodule-control/src/main.rs
index 8348ad74..561233d9 100644
--- a/inputmodule-control/src/main.rs
+++ b/inputmodule-control/src/main.rs
@@ -5,55 +5,14 @@ mod c1minimal;
 mod font;
 mod inputmodule;
 mod ledmatrix;
+mod commands;
 
-use clap::{Parser, Subcommand};
+use clap::{Parser};
 use inputmodule::find_serialdevs;
 
-use crate::b1display::B1DisplaySubcommand;
-use crate::c1minimal::C1MinimalSubcommand;
-use crate::inputmodule::{serial_commands, B1_LCD_PID, LED_MATRIX_PID};
-use crate::ledmatrix::LedMatrixSubcommand;
+use crate::inputmodule::serial_commands;
 
-#[derive(Subcommand, Debug)]
-enum Commands {
-    LedMatrix(LedMatrixSubcommand),
-    B1Display(B1DisplaySubcommand),
-    C1Minimal(C1MinimalSubcommand),
-}
-
-impl Commands {
-    pub fn to_pid(&self) -> u16 {
-        match self {
-            Self::LedMatrix(_) => LED_MATRIX_PID,
-            Self::B1Display(_) => B1_LCD_PID,
-            Self::C1Minimal(_) => 0x22,
-        }
-    }
-}
-
-/// RAW HID and VIA commandline for QMK devices
-#[derive(Parser, Debug)]
-#[command(version, arg_required_else_help = true)]
-pub struct ClapCli {
-    #[command(subcommand)]
-    command: Option<Commands>,
-
-    /// List connected HID devices
-    #[arg(short, long)]
-    list: bool,
-
-    /// Verbose outputs to the console
-    #[arg(short, long)]
-    verbose: bool,
-
-    /// Serial device, like /dev/ttyACM0 or COM0
-    #[arg(long)]
-    pub serial_dev: Option<String>,
-
-    /// Retry connecting to the device until it works
-    #[arg(long)]
-    wait_for_device: bool,
-}
+use crate::commands::ClapCli;
 
 fn main() {
     let args: Vec<String> = std::env::args().collect();

From 357946c8e0c9cb5a9a73b89dbdd00c64ed3de1cb Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 12:50:16 -0400
Subject: [PATCH 3/8] Bring up to date with main

---
 Cargo.lock                           |  4 +-
 Cargo.toml                           |  2 +-
 inputmodule-dbus-monitor/Cargo.toml  |  7 +++
 inputmodule-dbus-monitor/src/main.rs | 82 ++++++++++++++++++++++++++++
 4 files changed, 92 insertions(+), 3 deletions(-)
 create mode 100644 inputmodule-dbus-monitor/Cargo.toml
 create mode 100644 inputmodule-dbus-monitor/src/main.rs

diff --git a/Cargo.lock b/Cargo.lock
index 8b8327b0..de8676a5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1042,8 +1042,8 @@ dependencies = [
 
 [[package]]
 name = "is31fl3741"
-version = "0.2.1"
-source = "git+https://github.com/FrameworkComputer/is31fl3741-rs?branch=sw-enablement#fb88ad1baf28aa2a61bc85ff5db83c6e2b661ed5"
+version = "0.2.2"
+source = "git+https://github.com/FrameworkComputer/is31fl3741-rs?branch=is31fl3743a#1814f6a838560c7553cda34cdad4692dc5da2d5f"
 dependencies = [
  "embedded-graphics-core",
  "embedded-hal",
diff --git a/Cargo.toml b/Cargo.toml
index f4eada3c..840ff910 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,7 @@ usbd-serial = "0.1.1"
 usbd-hid = "0.6.1"
 fugit = "0.3.7"
 # LED Matrix
-is31fl3741 = { git = "https://github.com/FrameworkComputer/is31fl3741-rs", branch = "sw-enablement" }
+is31fl3741 = { git = "https://github.com/FrameworkComputer/is31fl3741-rs", branch = "is31fl3743a" }
 # B1 Display
 st7306 = { git = "https://github.com/FrameworkComputer/st7306-rs", branch = "update-deps" }
 embedded-graphics = "0.8"
diff --git a/inputmodule-dbus-monitor/Cargo.toml b/inputmodule-dbus-monitor/Cargo.toml
new file mode 100644
index 00000000..541bb31c
--- /dev/null
+++ b/inputmodule-dbus-monitor/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+edition = "2021"
+name = "inputmodule-dbus-monitor"
+version = "0.0.1"
+
+[dependencies]
+dbus = { version = "0.9.7", features = ["vendored"] }
diff --git a/inputmodule-dbus-monitor/src/main.rs b/inputmodule-dbus-monitor/src/main.rs
new file mode 100644
index 00000000..882c6126
--- /dev/null
+++ b/inputmodule-dbus-monitor/src/main.rs
@@ -0,0 +1,82 @@
+// Source: https://github.com/diwic/dbus-rs/blob/master/dbus/examples/monitor.rs
+
+use std::time::Duration;
+
+use dbus::blocking::Connection;
+use dbus::channel::MatchingReceiver;
+use dbus::message::MatchRule;
+use dbus::Message;
+
+// This programs implements the equivalent of running the "dbus-monitor" tool
+fn main() {
+    // Very simple argument parsing.
+    let use_system_bus = std::env::args().into_iter().any(|a| a == "--system");
+
+    // First open up a connection to the desired bus.
+    let conn = (if use_system_bus { Connection::new_system() } else { Connection::new_session() }).expect("D-Bus connection failed");
+
+    // Second create a rule to match messages we want to receive; in this example we add no
+    // further requirements, so all messages will match
+    let rule = MatchRule::new();
+
+    // Try matching using new scheme
+    let proxy = conn.with_proxy("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_millis(5000));
+    let result: Result<(), dbus::Error> =
+        proxy.method_call("org.freedesktop.DBus.Monitoring", "BecomeMonitor", (vec![rule.match_str()], 0u32));
+
+    match result {
+        // BecomeMonitor was successful, start listening for messages
+        Ok(_) => {
+            conn.start_receive(
+                rule,
+                Box::new(|msg, _| {
+                    handle_message(&msg);
+                    true
+                }),
+            );
+        }
+        // BecomeMonitor failed, fallback to using the old scheme
+        Err(e) => {
+            eprintln!("Failed to BecomeMonitor: '{}', falling back to eavesdrop", e);
+
+            // First, we'll try "eavesdrop", which as the name implies lets us receive
+            // *all* messages, not just ours.
+            let rule_with_eavesdrop = {
+                let mut rule = rule.clone();
+                rule.eavesdrop = true;
+                rule
+            };
+
+            let result = conn.add_match(rule_with_eavesdrop, |_: (), _, msg| {
+                handle_message(&msg);
+                true
+            });
+
+            match result {
+                Ok(_) => {
+                    // success, we're now listening
+                }
+                // This can sometimes fail, for example when listening to the system bus as a non-root user.
+                // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`:
+                Err(e) => {
+                    eprintln!("Failed to eavesdrop: '{}', trying without it", e);
+                    conn.add_match(rule, |_: (), _, msg| {
+                        handle_message(&msg);
+                        true
+                    })
+                    .expect("add_match failed");
+                }
+            }
+        }
+    }
+
+    // Loop and print out all messages received (using handle_message()) as they come.
+    // Some can be quite large, e.g. if they contain embedded images..
+    loop {
+        conn.process(Duration::from_millis(1000)).unwrap();
+    }
+}
+
+fn handle_message(msg: &Message) {
+    println!("Got message: {:?}", msg);
+}
\ No newline at end of file

From a81697f701c4546cb268672eee83fb9b1e41973b Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 13:22:02 -0400
Subject: [PATCH 4/8] Add new blink n times command and use it

---
 dbus-monitor/src/dbus_monitor.rs       |  3 +--
 inputmodule-control/src/inputmodule.rs | 13 +++++++++++++
 inputmodule-control/src/ledmatrix.rs   |  4 ++++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
index 811cb1ee..14bae2fa 100644
--- a/dbus-monitor/src/dbus_monitor.rs
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -22,8 +22,7 @@ use clap::{Parser, Subcommand};
 fn handle_message(msg: &Message) {
     println!("Got message from DBus: {:?}", msg);
 
-    run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blinking"]);
-    // Can't seem to get to this second command
+    run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]);
     run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
 
     println!("Message handled");
diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs
index 81457fc2..0c2e0036 100644
--- a/inputmodule-control/src/inputmodule.rs
+++ b/inputmodule-control/src/inputmodule.rs
@@ -190,6 +190,9 @@ pub fn serial_commands(args: &ClapCli) {
                 if let Some(pattern) = ledmatrix_args.pattern {
                     pattern_cmd(serialdev, pattern);
                 }
+                if let Some(blink_n_times_arg) = ledmatrix_args.blink_n_times {
+                    blink_n_cmd(&serialdevs, blink_n_times_arg);
+                }
                 if ledmatrix_args.all_brightnesses {
                     all_brightnesses_cmd(serialdev);
                 }
@@ -556,6 +559,16 @@ fn blinking_cmd(serialdevs: &Vec<String>) {
     }
 }
 
+fn blink_n_cmd(serialdevs: &Vec<String>, blink_n_times: u8) {
+    let duration = Duration::from_millis(500);
+    for _ in 0..blink_n_times {
+        simple_cmd_multiple(serialdevs, Command::Brightness, &[0]);
+        thread::sleep(duration);
+        simple_cmd_multiple(serialdevs, Command::Brightness, &[200]);
+        thread::sleep(duration);
+    }
+}
+
 fn breathing_cmd(serialdevs: &Vec<String>) {
     loop {
         // Go quickly from 250 to 50
diff --git a/inputmodule-control/src/ledmatrix.rs b/inputmodule-control/src/ledmatrix.rs
index b5f5ac1c..01e0afb9 100644
--- a/inputmodule-control/src/ledmatrix.rs
+++ b/inputmodule-control/src/ledmatrix.rs
@@ -70,6 +70,10 @@ pub struct LedMatrixSubcommand {
     #[arg(long)]
     pub blinking: bool,
 
+    /// Blink the current pattern once a second, n times
+    #[arg(long)]
+    pub blink_n_times: Option<u8>,
+
     /// Breathing brightness of the current pattern
     #[arg(long)]
     pub breathing: bool,

From fe52e15301f83d9329533cef0f31a03a6361770e Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 15:04:12 -0400
Subject: [PATCH 5/8] Basic string scanning functionality

---
 dbus-monitor/src/dbus_monitor.rs | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
index 14bae2fa..dc41d21d 100644
--- a/dbus-monitor/src/dbus_monitor.rs
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -21,9 +21,22 @@ use clap::{Parser, Subcommand};
 
 fn handle_message(msg: &Message) {
     println!("Got message from DBus: {:?}", msg);
-
-    run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]);
-    run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
+    let mut iter = msg.iter_init();
+    while let Some(arg) = iter.get_refarg() {
+        // Extract the inner value as a string and convert it to a String
+        if let Some(string_ref) = arg.as_str() {
+            let string_value: String = string_ref.to_string();
+            println!("String value: {}", string_value);
+
+            if string_value.contains("calendar.google.com"){
+                run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]);
+                run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
+            }
+        } else {
+            println!("Not a string.");
+        }
+        iter.next();
+    }
 
     println!("Message handled");
 }

From 978fa837f559fa2e12adf7875c5c47964c63602a Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 15:04:31 -0400
Subject: [PATCH 6/8] Switch to debug! macro for quieter logs by default

---
 Cargo.lock                       |  2 ++
 dbus-monitor/Cargo.toml          |  2 ++
 dbus-monitor/src/dbus_monitor.rs | 22 +++++++++++-----------
 dbus-monitor/src/main.rs         |  5 +++++
 4 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index de8676a5..6a3a0e54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -837,8 +837,10 @@ version = "0.0.1"
 dependencies = [
  "clap",
  "dbus",
+ "env_logger",
  "inputmodule-control",
  "libdbus-sys",
+ "log",
 ]
 
 [[package]]
diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml
index 4849a800..9f09a53e 100644
--- a/dbus-monitor/Cargo.toml
+++ b/dbus-monitor/Cargo.toml
@@ -6,6 +6,8 @@ version = "0.0.1"
 [dependencies]
 dbus = { version = "0.9.7", features = ["vendored"] }
 clap = { version = "4.0", features = ["derive"] }
+log = "0.4"
+env_logger = "0.10.0"
 
 [dependencies.libdbus-sys]
 default-features = false
diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
index dc41d21d..072fb5b0 100644
--- a/dbus-monitor/src/dbus_monitor.rs
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -19,26 +19,26 @@ use inputmodule_control::commands::ClapCli;
 use inputmodule_control::inputmodule::{serial_commands};
 use clap::{Parser, Subcommand};
 
+use log::debug;
+
 fn handle_message(msg: &Message) {
-    println!("Got message from DBus: {:?}", msg);
+    debug!("Got message from DBus: {:?}", msg);
+
     let mut iter = msg.iter_init();
     while let Some(arg) = iter.get_refarg() {
-        // Extract the inner value as a string and convert it to a String
         if let Some(string_ref) = arg.as_str() {
             let string_value: String = string_ref.to_string();
-            println!("String value: {}", string_value);
+            debug!("String value: {}", string_value);
 
             if string_value.contains("calendar.google.com"){
                 run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]);
                 run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
             }
-        } else {
-            println!("Not a string.");
         }
         iter.next();
     }
 
-    println!("Message handled");
+    debug!("DBus Message handled");
 }
 
 pub fn run_inputmodule_command(args: Vec<&str>){
@@ -52,7 +52,7 @@ pub fn run_inputmodule_command(args: Vec<&str>){
 pub fn run_dbus_monitor() {
     // First open up a connection to the desired bus.
     let conn = Connection::new_session().expect("D-Bus connection failed");
-    println!("Connection to DBus session monitor opened");
+    debug!("Connection to DBus session monitor opened");
 
     // Second create a rule to match messages we want to receive; in this example we add no
     // further requirements, so all messages will match
@@ -72,7 +72,7 @@ pub fn run_dbus_monitor() {
         "BecomeMonitor",
         (vec![rule.match_str()], 0u32),
     );
-    println!("Monitoring DBus channel...");
+    debug!("Monitoring DBus channel...");
 
     match result {
         // BecomeMonitor was successful, start listening for messages
@@ -80,7 +80,7 @@ pub fn run_dbus_monitor() {
             conn.start_receive(
                 rule,
                 Box::new(|msg, _| {
-                    println!("Start listening");
+                    debug!("Start listening");
                     handle_message(&msg);
                     true
                 }),
@@ -88,7 +88,7 @@ pub fn run_dbus_monitor() {
         }
         // BecomeMonitor failed, fallback to using the old scheme
         Err(e) => {
-            eprintln!(
+            debug!(
                 "Failed to BecomeMonitor: '{}', falling back to eavesdrop",
                 e
             );
@@ -113,7 +113,7 @@ pub fn run_dbus_monitor() {
                 // This can sometimes fail, for example when listening to the system bus as a non-root user.
                 // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`:
                 Err(e) => {
-                    eprintln!("Failed to eavesdrop: '{}', trying without it", e);
+                    debug!("Failed to eavesdrop: '{}', trying without it", e);
                     conn.add_match(rule, |_: (), _, msg| {
                         handle_message(&msg);
                         true
diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs
index ddcfa2ae..64068857 100644
--- a/dbus-monitor/src/main.rs
+++ b/dbus-monitor/src/main.rs
@@ -1,6 +1,11 @@
 mod dbus_monitor;
 mod utils;
 
+extern crate log;
+use log::{debug};
+use env_logger;
+
 fn main() {
+    env_logger::init();
     dbus_monitor::run_dbus_monitor();
 }

From 82bf7d407be3fe4c00b4b8fa8b843442472ab4d6 Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 15:06:43 -0400
Subject: [PATCH 7/8] RM unused modules, cargo clippy, cargo fmt

---
 dbus-monitor/src/dbus_monitor.rs       | 22 +++++++++++----------
 dbus-monitor/src/main.rs               |  2 --
 dbus-monitor/src/utils.rs              | 27 --------------------------
 inputmodule-control/src/commands.rs    |  2 +-
 inputmodule-control/src/inputmodule.rs |  2 +-
 inputmodule-control/src/lib.rs         |  3 +--
 inputmodule-control/src/main.rs        |  4 ++--
 7 files changed, 17 insertions(+), 45 deletions(-)
 delete mode 100644 dbus-monitor/src/utils.rs

diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
index 072fb5b0..271bcacb 100644
--- a/dbus-monitor/src/dbus_monitor.rs
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -9,15 +9,11 @@ use dbus::message::MatchRule;
 use dbus::Message;
 use dbus::MessageType;
 
-use std::process::Command;
 use std::time::Duration;
 
-use crate::utils;
-
-use inputmodule_control::inputmodule::find_serialdevs;
+use clap::Parser;
 use inputmodule_control::commands::ClapCli;
-use inputmodule_control::inputmodule::{serial_commands};
-use clap::{Parser, Subcommand};
+use inputmodule_control::inputmodule::serial_commands;
 
 use log::debug;
 
@@ -30,8 +26,14 @@ fn handle_message(msg: &Message) {
             let string_value: String = string_ref.to_string();
             debug!("String value: {}", string_value);
 
-            if string_value.contains("calendar.google.com"){
-                run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]);
+            if string_value.contains("calendar.google.com") {
+                run_inputmodule_command(vec![
+                    "led-matrix",
+                    "--pattern",
+                    "all-on",
+                    "--blink-n-times",
+                    "3",
+                ]);
                 run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
             }
         }
@@ -41,11 +43,11 @@ fn handle_message(msg: &Message) {
     debug!("DBus Message handled");
 }
 
-pub fn run_inputmodule_command(args: Vec<&str>){
+pub fn run_inputmodule_command(args: Vec<&str>) {
     let bin_placeholder = vec!["bin-placeholder"];
     let full_args = [&bin_placeholder[..], &args[..]].concat();
     let args = ClapCli::parse_from(full_args);
-    
+
     serial_commands(&args);
 }
 
diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs
index 64068857..aa4bcc66 100644
--- a/dbus-monitor/src/main.rs
+++ b/dbus-monitor/src/main.rs
@@ -1,8 +1,6 @@
 mod dbus_monitor;
-mod utils;
 
 extern crate log;
-use log::{debug};
 use env_logger;
 
 fn main() {
diff --git a/dbus-monitor/src/utils.rs b/dbus-monitor/src/utils.rs
deleted file mode 100644
index 93d4914d..00000000
--- a/dbus-monitor/src/utils.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use std::process::Child;
-use std::process::Command;
-use std::time::Duration;
-use std::time::Instant;
-
-pub fn run_command_with_timeout(
-    command: &str,
-    timeout_seconds: u64,
-) -> Result<Child, Box<dyn std::error::Error>> {
-    let mut child_process = Command::new("bash").arg("-c").arg(command).spawn()?;
-
-    let start_time = Instant::now();
-    while start_time.elapsed() < Duration::from_secs(timeout_seconds) {
-        if let Some(exit_status) = child_process.try_wait()? {
-            println!(
-                "Command finished before the specified duration. Exit status: {:?}",
-                exit_status
-            );
-            return Ok(child_process);
-        }
-    }
-
-    child_process.kill()?; // Attempt to kill the process
-
-    println!("Command terminated after {} seconds", timeout_seconds);
-    Ok(child_process)
-}
diff --git a/inputmodule-control/src/commands.rs b/inputmodule-control/src/commands.rs
index fa122aab..c959c5db 100644
--- a/inputmodule-control/src/commands.rs
+++ b/inputmodule-control/src/commands.rs
@@ -1,9 +1,9 @@
 #![allow(clippy::needless_range_loop)]
 #![allow(clippy::single_match)]
 
-use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID};
 use crate::b1display::B1DisplaySubcommand;
 use crate::c1minimal::C1MinimalSubcommand;
+use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID};
 use crate::ledmatrix::LedMatrixSubcommand;
 
 use clap::{Parser, Subcommand};
diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs
index 0c2e0036..2ca8c8e8 100644
--- a/inputmodule-control/src/inputmodule.rs
+++ b/inputmodule-control/src/inputmodule.rs
@@ -10,9 +10,9 @@ use serialport::{SerialPort, SerialPortInfo, SerialPortType};
 
 use crate::b1display::{B1Pattern, Fps, PowerMode};
 use crate::c1minimal::Color;
+use crate::commands::ClapCli;
 use crate::font::{convert_font, convert_symbol};
 use crate::ledmatrix::{Game, GameOfLifeStartParam, Pattern};
-use crate::commands::ClapCli;
 
 const FWK_MAGIC: &[u8] = &[0x32, 0xAC];
 pub const FRAMEWORK_VID: u16 = 0x32AC;
diff --git a/inputmodule-control/src/lib.rs b/inputmodule-control/src/lib.rs
index a421c538..698e610b 100644
--- a/inputmodule-control/src/lib.rs
+++ b/inputmodule-control/src/lib.rs
@@ -2,8 +2,7 @@
 
 mod b1display;
 mod c1minimal;
+pub mod commands;
 mod font;
 pub mod inputmodule;
 mod ledmatrix;
-pub mod commands;
-
diff --git a/inputmodule-control/src/main.rs b/inputmodule-control/src/main.rs
index 561233d9..d8d9dc62 100644
--- a/inputmodule-control/src/main.rs
+++ b/inputmodule-control/src/main.rs
@@ -2,12 +2,12 @@
 #![allow(clippy::single_match)]
 mod b1display;
 mod c1minimal;
+mod commands;
 mod font;
 mod inputmodule;
 mod ledmatrix;
-mod commands;
 
-use clap::{Parser};
+use clap::Parser;
 use inputmodule::find_serialdevs;
 
 use crate::inputmodule::serial_commands;

From 8d04dff630eacaf0317290e0c32e4bb21bdcb671 Mon Sep 17 00:00:00 2001
From: Zach Feldman <zmf@frame.work>
Date: Mon, 9 Oct 2023 15:46:58 -0400
Subject: [PATCH 8/8] Add configuration file

---
 Cargo.lock                       | 63 +++++++++++++++++++++++++-------
 dbus-monitor/Cargo.toml          |  5 ++-
 dbus-monitor/src/config.json     | 10 +++++
 dbus-monitor/src/dbus_monitor.rs | 59 +++++++++++++++++++++++-------
 4 files changed, 109 insertions(+), 28 deletions(-)
 create mode 100644 dbus-monitor/src/config.json

diff --git a/Cargo.lock b/Cargo.lock
index 6a3a0e54..5dbaf955 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -95,16 +95,15 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.3.2"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
  "anstyle-wincon",
  "colorchoice",
- "is-terminal",
  "utf8parse",
 ]
 
@@ -134,9 +133,9 @@ dependencies = [
 
 [[package]]
 name = "anstyle-wincon"
-version = "1.0.1"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
 dependencies = [
  "anstyle",
  "windows-sys",
@@ -390,33 +389,31 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.3.3"
+version = "4.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0"
+checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
 dependencies = [
  "clap_builder",
  "clap_derive",
- "once_cell 1.18.0",
 ]
 
 [[package]]
 name = "clap_builder"
-version = "4.3.3"
+version = "4.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab"
+checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
 dependencies = [
  "anstream",
  "anstyle",
- "bitflags 1.3.2",
  "clap_lex",
  "strsim",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.3.2"
+version = "4.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
+checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -839,8 +836,11 @@ dependencies = [
  "dbus",
  "env_logger",
  "inputmodule-control",
+ "lazy_static",
  "libdbus-sys",
  "log",
+ "serde",
+ "serde_json",
 ]
 
 [[package]]
@@ -1060,6 +1060,12 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
 [[package]]
 name = "jni"
 version = "0.19.0"
@@ -1989,6 +1995,12 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
 [[package]]
 name = "same-file"
 version = "1.0.6"
@@ -2036,6 +2048,31 @@ name = "serde"
 version = "1.0.164"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.18",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
 
 [[package]]
 name = "serialport"
diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml
index 9f09a53e..ae43a175 100644
--- a/dbus-monitor/Cargo.toml
+++ b/dbus-monitor/Cargo.toml
@@ -5,9 +5,12 @@ version = "0.0.1"
 
 [dependencies]
 dbus = { version = "0.9.7", features = ["vendored"] }
-clap = { version = "4.0", features = ["derive"] }
+clap = { version = "4.4.6", features = ["derive"] }
 log = "0.4"
 env_logger = "0.10.0"
+serde = { version="1.0", features = ["derive"] }
+serde_json = "1.0"
+lazy_static = "1.4"
 
 [dependencies.libdbus-sys]
 default-features = false
diff --git a/dbus-monitor/src/config.json b/dbus-monitor/src/config.json
new file mode 100644
index 00000000..f8381116
--- /dev/null
+++ b/dbus-monitor/src/config.json
@@ -0,0 +1,10 @@
+{
+    "dbus_interface": "org.freedesktop.Notifications",
+    "dbus_member": "Notify",
+    "scan_args_for": "calendar.google.com",
+    "run_inputmodule_commands": [
+        "led-matrix --pattern all-on --blink-n-times 3",
+        "led-matrix --brightness 0"
+    ]
+}
+
diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs
index 271bcacb..05212a47 100644
--- a/dbus-monitor/src/dbus_monitor.rs
+++ b/dbus-monitor/src/dbus_monitor.rs
@@ -1,7 +1,9 @@
 // Mostly taken from https://github.com/diwic/dbus-rs/blob/366a6dca3d20745f5dcfa006b1b1311c376d420e/dbus/examples/monitor.rs
 
 // This programs implements the equivalent of running the "dbus-monitor" tool
-// modified to only search for messages in the org.freedesktop.Notifications interface
+// modified to only search for messages in the interface specificied in config.json,
+// and then run arbitary inputmodule-rs commands to react to them
+
 use dbus::blocking::Connection;
 use dbus::channel::MatchingReceiver;
 use dbus::message::MatchRule;
@@ -17,6 +19,40 @@ use inputmodule_control::inputmodule::serial_commands;
 
 use log::debug;
 
+use serde::{Deserialize, Serialize};
+use serde_json;
+use std::fs::File;
+use std::io::Read;
+
+use lazy_static::lazy_static;
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct Config {
+    dbus_interface: String,
+    dbus_member: String,
+    scan_args_for: String,
+    run_inputmodule_commands: Vec<String>,
+}
+
+fn read_config(file_path: &str) -> Result<Config, Box<dyn std::error::Error>> {
+    let mut file = File::open(file_path)?;
+    let mut config_data = String::new();
+    file.read_to_string(&mut config_data)?;
+
+    let config: Config = serde_json::from_str(&config_data)?;
+
+    Ok(config)
+}
+
+lazy_static! {
+    pub static ref CONFIG: Config = {
+        // Read and deserialize the JSON configuration
+        let config_file = "dbus-monitor/src/config.json";
+        let config = read_config(config_file).expect("Failed to read config");
+        config
+    };
+}
+
 fn handle_message(msg: &Message) {
     debug!("Got message from DBus: {:?}", msg);
 
@@ -26,15 +62,11 @@ fn handle_message(msg: &Message) {
             let string_value: String = string_ref.to_string();
             debug!("String value: {}", string_value);
 
-            if string_value.contains("calendar.google.com") {
-                run_inputmodule_command(vec![
-                    "led-matrix",
-                    "--pattern",
-                    "all-on",
-                    "--blink-n-times",
-                    "3",
-                ]);
-                run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]);
+            if string_value.contains(&CONFIG.scan_args_for) {
+                for command in &CONFIG.run_inputmodule_commands {
+                    let command_vec: Vec<&str> = command.split_whitespace().collect();
+                    run_inputmodule_command(command_vec);
+                }
             }
         }
         iter.next();
@@ -56,12 +88,11 @@ pub fn run_dbus_monitor() {
     let conn = Connection::new_session().expect("D-Bus connection failed");
     debug!("Connection to DBus session monitor opened");
 
-    // Second create a rule to match messages we want to receive; in this example we add no
-    // further requirements, so all messages will match
+    // Second create a rule to match messages we want to receive
     let rule = MatchRule::new()
         .with_type(MessageType::MethodCall)
-        .with_interface("org.freedesktop.Notifications")
-        .with_member("Notify");
+        .with_interface(&CONFIG.dbus_interface)
+        .with_member(&CONFIG.dbus_member);
 
     // Try matching using new scheme
     let proxy = conn.with_proxy(