use super::*;
pub const RSSI_SENTINEL: f64 = -100.0;
pub(super) fn rf_unverified_field(status: &Map<String, Value>) -> Value {
if status.get("state").and_then(Value::as_str) == Some("stale") {
return Value::Null;
}
bool_or_null(status, "rf_unverified")
}
pub(super) fn bool_or_null(map: &Map<String, Value>, key: &str) -> Value {
match map.get(key) {
Some(Value::Bool(b)) => Value::Bool(*b),
_ => Value::Null,
}
}
pub fn build_radio_block(wfb_status: Option<&Map<String, Value>>) -> Value {
let Some(status) = wfb_status else {
return radio_absent_block();
};
let iface = status.get("interface").and_then(non_empty_string);
let driver = iface
.as_deref()
.and_then(detect_radio_driver_name)
.map(Value::from)
.unwrap_or(Value::Null);
let iface_value = iface.clone().map(Value::from).unwrap_or(Value::Null);
let channel = status
.get("actual_channel")
.filter(|v| !is_falsey(v))
.cloned()
.unwrap_or(Value::Null);
let freq_mhz = channel
.as_i64()
.and_then(|c| get_channel(c).map(|ch| ch.frequency_mhz))
.map(Value::from)
.unwrap_or(Value::Null);
let rssi = match status.get("rssi_dbm").and_then(Value::as_f64) {
Some(v) if v == RSSI_SENTINEL => Value::Null,
_ => status.get("rssi_dbm").cloned().unwrap_or(Value::Null),
};
let bitrate = status
.get("bitrate_kbps")
.filter(|v| !is_falsey(v))
.cloned()
.unwrap_or(Value::Null);
json!({
"state": status.get("state").cloned().unwrap_or(Value::Null),
"iface": iface_value,
"driver": driver,
"channel": channel,
"freq_mhz": freq_mhz,
"bandwidth_mhz": 20,
"tx_power_dbm": get_or_null(status, "tx_power_dbm"),
"tx_power_max_dbm": get_or_null(status, "tx_power_max_dbm"),
"topology": get_or_null(status, "topology"),
"rssi_dbm": rssi,
"snr_db": get_or_null(status, "snr_db"),
"noise_dbm": get_or_null(status, "noise_dbm"),
"bitrate_kbps": bitrate,
"fec_recovered": get_or_null(status, "fec_recovered"),
"fec_lost": get_or_null(status, "fec_failed"),
"packets_lost": get_or_null(status, "packets_lost"),
"loss_percent": get_or_null(status, "loss_percent"),
"mcs_index": get_or_null(status, "mcs_index"),
"rx_silent_seconds": get_or_null(status, "rx_silent_seconds"),
"paired": json_truthy(status.get("paired").unwrap_or(&Value::Null)),
"paired_with_device_id": get_or_null(status, "paired_with_device_id"),
"paired_at": get_or_null(status, "paired_at"),
"public_key_fingerprint": get_or_null(status, "public_key_fingerprint"),
"auto_pair_enabled": get_or_null(status, "auto_pair_enabled"),
"tx_video_stalled": get_or_null(status, "tx_video_stalled"),
"tx_video_stall_kills": get_or_null(status, "tx_video_stall_kills"),
"tx_video_recvq_bytes": get_or_null(status, "tx_video_recvq_bytes"),
"acquire_state": get_or_null(status, "acquire_state"),
"channel_locked": get_or_null(status, "channel_locked"),
"rf_unverified": rf_unverified_field(status),
"reacquire_kills": get_or_null(status, "reacquire_kills"),
"valid_rx_packets_per_s": get_or_null(status, "valid_rx_packets_per_s"),
"adapter_chipset": get_or_null(status, "adapter_chipset"),
"adapter_injection_ok": bool_or_null(status, "adapter_injection_ok"),
"adapter_usb_speed_mbps": get_or_null(status, "adapter_usb_speed_mbps"),
"adapter_usb_degraded": bool_or_null(status, "adapter_usb_degraded"),
"phy_muted": bool_or_null(status, "phy_muted"),
"tx_zombie_kills": get_or_null(status, "tx_zombie_kills"),
"tx_bytes_per_s": get_or_null(status, "tx_bytes_per_s"),
"restart_count": get_or_null(status, "restart_count"),
})
}
pub fn radio_absent_block() -> Value {
json!({
"state": "absent",
"iface": null,
"driver": null,
"channel": null,
"freq_mhz": null,
"bandwidth_mhz": null,
"tx_power_dbm": null,
"tx_power_max_dbm": null,
"topology": null,
"rssi_dbm": null,
"snr_db": null,
"noise_dbm": null,
"bitrate_kbps": null,
"fec_recovered": null,
"fec_lost": null,
"packets_lost": null,
"loss_percent": null,
"mcs_index": null,
"rx_silent_seconds": null,
"paired": false,
"paired_with_device_id": null,
"paired_at": null,
"public_key_fingerprint": null,
"auto_pair_enabled": null,
"tx_video_stalled": null,
"tx_video_stall_kills": null,
"tx_video_recvq_bytes": null,
"acquire_state": null,
"channel_locked": null,
"rf_unverified": null,
"reacquire_kills": null,
"valid_rx_packets_per_s": null,
"adapter_chipset": null,
"adapter_injection_ok": null,
"adapter_usb_speed_mbps": null,
"adapter_usb_degraded": null,
"phy_muted": null,
})
}
pub(super) fn detect_radio_driver_name(interface: &str) -> Option<String> {
if interface.is_empty() {
return None;
}
let path = Path::new("/sys/class/net")
.join(interface)
.join("device")
.join("uevent");
let text = std::fs::read_to_string(path).ok()?;
for line in text.lines() {
if let Some(rest) = line.strip_prefix("DRIVER=") {
let name = rest.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
}
None
}