use anyhow::Result;
use brainwires_hardware::homeauto::matter::{MatterDeviceConfig, MatterDeviceServer};
use std::sync::Arc;
use tracing::info;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt().with_env_filter("info").init();
let config = MatterDeviceConfig::builder()
.device_name("Brainwires Agent Light")
.vendor_id(0xFFF1) .product_id(0x8001)
.discriminator(3840)
.passcode(20202021) .storage_path("/tmp/brainwires-matter-server")
.port(5540) .build();
let server = MatterDeviceServer::new(config).await?;
server.set_on_off_handler(|on| {
let state = if on { "ON" } else { "OFF" };
println!("[Matter] On/Off → {state}");
});
server.set_level_handler(|level| {
let pct = level as f32 / 254.0 * 100.0;
println!("[Matter] Level → {level}/254 ({pct:.0}%)");
});
server.set_color_temp_handler(|mireds| {
let kelvin = 1_000_000u32.checked_div(mireds as u32).unwrap_or(0);
println!("[Matter] Color temperature → {mireds} mireds (~{kelvin} K)");
});
server.set_thermostat_handler(|celsius| {
println!("[Matter] Thermostat setpoint → {celsius:.1}°C");
});
println!();
println!("==========================================================");
println!(" Matter 1.3 device server");
println!("==========================================================");
println!(" QR code: {}", server.qr_code());
println!(" Pairing code: {}", server.pairing_code());
println!(
" QR URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data={}",
urlencoded(server.qr_code())
);
println!();
println!(" Scan the QR code (or enter the pairing code) in:");
println!(" - Apple Home (iOS 16.2+ / macOS 13+)");
println!(" - Google Home");
println!(" - Home Assistant (Settings → Devices → Add integration → Matter)");
println!(
" - chip-tool: chip-tool pairing qrcode 1 \"{}\"",
server.qr_code()
);
println!("==========================================================");
println!();
info!("Advertising as '_matterc._udp' (discriminator=3840) on UDP port 5540");
info!("Waiting for commissioner… Press Ctrl+C to stop.");
let server = Arc::new(server);
let server_clone = Arc::clone(&server);
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
let mut tx_opt = Some(tx);
ctrlc::set_handler(move || {
if let Some(tx) = tx_opt.take() {
let _ = tx.send(());
}
})?;
let server_task = tokio::spawn(async move {
if let Err(e) = server_clone.start().await {
eprintln!("Matter server error: {e}");
}
});
let _ = rx.await;
info!("Shutting down…");
server.stop().await?;
server_task.abort();
Ok(())
}
fn urlencoded(s: &str) -> String {
s.chars()
.flat_map(|c| match c {
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' => {
vec![c]
}
c => format!("%{:02X}", c as u32).chars().collect::<Vec<_>>(),
})
.collect()
}