use std::sync::Arc;
use hidpp::channel::HidppChannel;
use openlogi_core::device::{DeviceInventory, DeviceKind, PairedDevice, ReceiverInfo};
use tracing::debug;
use super::{NodeProbe, PassContext, ProbeVerdict};
use crate::backend::NodeInfo;
use crate::channel::route::DIRECT_DEVICE_INDEX;
use crate::inventory::cache::{CacheKey, CacheOutcome, probe_or_reuse, seen};
use crate::inventory::mappings::resolve_device_kind;
pub(in crate::inventory) fn preferred_direct_codename(
marketing_name: Option<&str>,
os_name: &str,
) -> String {
marketing_name
.filter(|name| !name.trim().is_empty())
.unwrap_or(os_name)
.to_string()
}
pub(super) async fn probe_direct(
channel: Arc<HidppChannel>,
info: &NodeInfo,
pass: PassContext<'_>,
) -> NodeProbe {
let id = CacheKey::Direct(info.id.clone());
let cached = pass.cache.get(&id);
let (probe, outcome) = probe_or_reuse(
&channel,
DIRECT_DEVICE_INDEX,
Some(id),
cached,
true,
pass.now,
pass.subscriptions,
)
.await;
let capabilities = probe.capabilities;
let walk_succeeded = capabilities.is_some();
let caps = capabilities.unwrap_or_default();
let is_peripheral = probe.battery.is_some() || caps.buttons || caps.pointer || caps.lighting;
if !walk_succeeded {
debug!(
vid = format_args!("{:04x}", info.vendor_id),
pid = format_args!("{:04x}", info.product_id),
"feature walk did not complete — transient probe failure, keeping last-known identity"
);
return NodeProbe {
inventory: None,
verdict: ProbeVerdict::Failed,
outcomes: vec![seen(Some(CacheKey::Direct(info.id.clone())))],
};
}
if !is_peripheral {
debug!(
vid = format_args!("{:04x}", info.vendor_id),
pid = format_args!("{:04x}", info.product_id),
has_model = probe.model_info.is_some(),
"slot 0xff exposes no battery or config feature — likely a receiver \
secondary interface; skipping"
);
return NodeProbe {
inventory: None,
verdict: ProbeVerdict::healthy_when(walk_succeeded),
outcomes: vec![CacheOutcome::Unkeyed],
};
}
let codename = preferred_direct_codename(probe.marketing_name.as_deref(), &info.name);
debug!(os_name = %info.name, name = %codename, "BT-direct / wired device recognised");
let inventory = DeviceInventory {
receiver: ReceiverInfo {
name: info.name.clone(),
vendor_id: info.vendor_id,
product_id: info.product_id,
unique_id: None,
},
paired: vec![PairedDevice {
slot: DIRECT_DEVICE_INDEX,
codename: Some(codename),
wpid: None,
kind: resolve_device_kind(probe.kind, DeviceKind::Unknown),
online: true,
battery: probe.battery,
model_info: probe.model_info,
capabilities,
}],
};
NodeProbe {
inventory: Some(inventory),
verdict: ProbeVerdict::Healthy { complete: true },
outcomes: vec![outcome],
}
}