v0.12.0-preety_code

This commit is contained in:
Mateusz Gruszczyński
2026-09-03 15:33:37 +02:00
parent be67932b47
commit 07be4b85d9
87 changed files with 11691 additions and 3172 deletions
+90 -24
View File
@@ -1,8 +1,17 @@
impl GreeClient {
/// protocol_filter: 0=auto/both, 1=ECB only, 2=GCM only.
pub async fn discover(&self, broadcast: &str, duration: Duration, protocol_filter: u8, passes: u8) -> Result<Vec<Device>> {
pub async fn discover(
&self,
broadcast: &str,
duration: Duration,
protocol_filter: u8,
passes: u8,
) -> Result<Vec<Device>> {
let target = self.discovery_target(broadcast)?;
let target_hint = match target { SocketAddr::V4(addr) => Some(*addr.ip()), SocketAddr::V6(_) => None };
let target_hint = match target {
SocketAddr::V4(addr) => Some(*addr.ip()),
SocketAddr::V6(_) => None,
};
let socket = self.udp_socket(true, target_hint).await?;
let local = socket.local_addr()?;
let passes = passes.clamp(1, 10);
@@ -17,7 +26,11 @@ impl GreeClient {
);
let deadline = Instant::now() + duration;
let interval = if passes > 1 { duration / passes as u32 } else { duration };
let interval = if passes > 1 {
duration / passes as u32
} else {
duration
};
let mut next_scan = Instant::now();
let mut sent = 0_u8;
let mut result = Vec::new();
@@ -36,10 +49,14 @@ impl GreeClient {
let wait = remaining.min(Duration::from_millis(250));
match timeout(wait, socket.recv_from(&mut buffer)).await {
Ok(Ok((size, source))) => {
let Ok(value) = serde_json::from_slice::<Value>(&buffer[..size]) else { continue; };
let Ok(value) = serde_json::from_slice::<Value>(&buffer[..size]) else {
continue;
};
match self.parse_discovery(value, source) {
Ok(Some(mut device)) => {
if protocol_filter != 0 && device.protocol_version != protocol_filter { continue; }
if protocol_filter != 0 && device.protocol_version != protocol_filter {
continue;
}
let key = device.mac.to_ascii_lowercase();
if seen.insert(key) {
device.last_seen = Some(Utc::now());
@@ -48,7 +65,9 @@ impl GreeClient {
}
}
Ok(None) => {}
Err(err) => tracing::debug!(source=%source, error=?err, "Ignoring undecodable discovery response"),
Err(err) => {
tracing::debug!(source=%source, error=?err, "Ignoring undecodable discovery response")
}
}
}
Ok(Err(err)) => return Err(err.into()),
@@ -69,46 +88,90 @@ impl GreeClient {
} else {
decrypt_v1(GENERIC_GREE_V1_KEY, pack)?
};
value = serde_json::from_slice::<Value>(&clear).context("invalid decrypted discovery JSON")?;
value = serde_json::from_slice::<Value>(&clear)
.context("invalid decrypted discovery JSON")?;
} else if pack_value.is_object() {
value = pack_value.clone();
}
}
}
let kind = value.get("t").and_then(Value::as_str).unwrap_or_default().to_ascii_lowercase();
if kind != "dev" && kind != "scan" && value.get("mac").is_none() && value.get("cid").is_none() {
let kind = value
.get("t")
.and_then(Value::as_str)
.unwrap_or_default()
.to_ascii_lowercase();
if kind != "dev"
&& kind != "scan"
&& value.get("mac").is_none()
&& value.get("cid").is_none()
{
return Ok(None);
}
let mac = value.get("mac")
let mac = value
.get("mac")
.or_else(|| value.get("cid"))
.and_then(Value::as_str)
.unwrap_or_default()
.replace([':', '-'], "").to_ascii_uppercase();
if mac.is_empty() { return Ok(None); }
.replace([':', '-'], "")
.to_ascii_uppercase();
if mac.is_empty() {
return Ok(None);
}
let raw_model = value.get("model").or_else(|| value.get("series"))
.and_then(Value::as_str).unwrap_or_default().trim().to_string();
let model_type = value.get("ModelType")
.and_then(|v| v.as_str().map(str::to_string).or_else(|| v.as_i64().map(|n| n.to_string())))
let raw_model = value
.get("model")
.or_else(|| value.get("series"))
.and_then(Value::as_str)
.unwrap_or_default()
.trim()
.to_string();
let model_type = value
.get("ModelType")
.and_then(|v| {
v.as_str()
.map(str::to_string)
.or_else(|| v.as_i64().map(|n| n.to_string()))
})
.unwrap_or_default();
let model = if !model_type.is_empty() && (raw_model.is_empty() || raw_model.eq_ignore_ascii_case("gree")) {
let model = if !model_type.is_empty()
&& (raw_model.is_empty() || raw_model.eq_ignore_ascii_case("gree"))
{
format!("GREE {model_type}")
} else if raw_model.is_empty() {
"GREE".to_string()
} else {
raw_model
};
let ver = value.get("ver").and_then(Value::as_str).unwrap_or_default().trim();
let hid = value.get("hid").and_then(Value::as_str).unwrap_or_default().trim();
let ver = value
.get("ver")
.and_then(Value::as_str)
.unwrap_or_default()
.trim();
let hid = value
.get("hid")
.and_then(Value::as_str)
.unwrap_or_default()
.trim();
let firmware = match (ver.is_empty(), hid.is_empty()) {
(false, false) => format!("{ver} · {hid}"),
(false, true) => ver.to_string(),
(true, false) => hid.to_string(),
(true, true) => String::new(),
};
let suffix = mac.chars().rev().take(4).collect::<String>().chars().rev().collect::<String>().to_ascii_uppercase();
let name = value.get("name").and_then(Value::as_str)
.map(str::trim).filter(|v| !v.is_empty())
let suffix = mac
.chars()
.rev()
.take(4)
.collect::<String>()
.chars()
.rev()
.collect::<String>()
.to_ascii_uppercase();
let name = value
.get("name")
.and_then(Value::as_str)
.map(str::trim)
.filter(|v| !v.is_empty())
.map(str::to_string)
.unwrap_or_else(|| format!("{model} {suffix}"));
let now = Utc::now();
@@ -117,7 +180,11 @@ impl GreeClient {
mac,
name,
ip: source.ip().to_string(),
port: if source.port() == 0 { 7000 } else { source.port() },
port: if source.port() == 0 {
7000
} else {
source.port()
},
protocol_version: detected_protocol,
model,
firmware,
@@ -157,5 +224,4 @@ impl GreeClient {
updated_at: now,
}))
}
}