Skip to content

Commit c2a94e8

Browse files
committed
Add MQTT connection callback config.
This can be used for example to control a gateway LED.
1 parent e42d47b commit c2a94e8

File tree

5 files changed

+77
-2
lines changed

5 files changed

+77
-2
lines changed

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[toolchain]
2-
channel = "1.80.1"
2+
channel = "1.83.0"
33
components = ["rustfmt", "clippy"]
44
profile = "default"

src/cmd/configfile.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,22 @@ pub fn run(config: &Configuration) {
222222
{{/each}}
223223
]
224224
{{/each}}
225+
226+
227+
# Callback commands.
228+
#
229+
# These are commands that are triggered by certain events (e.g. MQTT connected
230+
# or error). These commands are intended to e.g. trigger a LED of a gateway.
231+
# Commands are configured as an array, where the first item is the path to the
232+
# command, and the (optional) remaining elements are the arguments. An empty
233+
# array disables the callback.
234+
[callbacks]
235+
236+
# On MQTT connected.
237+
on_mqtt_connected=[]
238+
239+
# On MQTT connection error.
240+
on_mqtt_connection_error=[]
225241
"#;
226242

227243
let reg = Handlebars::new();

src/commands.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::process::Stdio;
33

44
use anyhow::Result;
55
use chirpstack_api::gw;
6-
use log::info;
6+
use log::{error, info};
77
use once_cell::sync::OnceCell;
88
use tokio::io::AsyncWriteExt;
99
use tokio::process::Command;
@@ -74,9 +74,53 @@ pub async fn exec(pl: &gw::GatewayCommandExecRequest) -> Result<gw::GatewayComma
7474
})
7575
}
7676

77+
pub async fn exec_callback(cmd_args: &[String]) {
78+
tokio::spawn({
79+
let cmd_args = cmd_args.to_vec();
80+
81+
async move {
82+
if cmd_args.is_empty() {
83+
return;
84+
}
85+
86+
info!("Executing callback, callback: {:?}", cmd_args);
87+
88+
let mut cmd = Command::new(&cmd_args[0]);
89+
if cmd_args.len() > 1 {
90+
cmd.args(&cmd_args[1..]);
91+
}
92+
93+
if let Err(e) = cmd.output().await {
94+
error!(
95+
"Execute callback error, callback: {:?}, error: {}",
96+
cmd_args, e
97+
);
98+
}
99+
}
100+
});
101+
}
102+
77103
#[cfg(test)]
78104
mod test {
79105
use super::*;
106+
use std::{env, fs};
107+
108+
#[tokio::test]
109+
async fn test_exec_callback() {
110+
let temp_file = env::temp_dir().join("test.txt");
111+
fs::write(&temp_file, vec![]).unwrap();
112+
assert!(fs::exists(&temp_file).unwrap());
113+
114+
exec_callback(&[
115+
"rm".into(),
116+
temp_file.clone().into_os_string().into_string().unwrap(),
117+
])
118+
.await;
119+
120+
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
121+
122+
assert_eq!(false, fs::exists(&temp_file).unwrap());
123+
}
80124

81125
#[tokio::test]
82126
async fn test_commands() {

src/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub struct Configuration {
1313
pub backend: Backend,
1414
pub metadata: Metadata,
1515
pub commands: HashMap<String, Vec<String>>,
16+
pub callbacks: Callbacks,
1617
}
1718

1819
impl Configuration {
@@ -168,3 +169,10 @@ pub struct Metadata {
168169
pub r#static: HashMap<String, String>,
169170
pub commands: HashMap<String, Vec<String>>,
170171
}
172+
173+
#[derive(Serialize, Deserialize, Default)]
174+
#[serde(default)]
175+
pub struct Callbacks {
176+
pub on_mqtt_connected: Vec<String>,
177+
pub on_mqtt_connection_error: Vec<String>,
178+
}

src/mqtt.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ pub async fn setup(conf: &Configuration) -> Result<()> {
208208

209209
// Eventloop
210210
tokio::spawn({
211+
let on_mqtt_connected = conf.callbacks.on_mqtt_connected.clone();
212+
let on_mqtt_connection_error = conf.callbacks.on_mqtt_connection_error.clone();
213+
211214
async move {
212215
info!("Starting MQTT event loop");
213216

@@ -228,6 +231,8 @@ pub async fn setup(conf: &Configuration) -> Result<()> {
228231
}
229232
Event::Incoming(Incoming::ConnAck(v)) => {
230233
if v.code == ConnectReturnCode::Success {
234+
commands::exec_callback(&on_mqtt_connected).await;
235+
231236
if let Err(e) = connect_tx.try_send(()) {
232237
error!("Send to subscribe channel error, error: {}", e);
233238
}
@@ -240,6 +245,8 @@ pub async fn setup(conf: &Configuration) -> Result<()> {
240245
}
241246
}
242247
Err(e) => {
248+
commands::exec_callback(&on_mqtt_connection_error).await;
249+
243250
error!("MQTT error, error: {}", e);
244251
sleep(Duration::from_secs(1)).await
245252
}

0 commit comments

Comments
 (0)