hap_model/
characteristics.rs1use crate::error::{ModelError, Result};
4use crate::format::{CharFormat, CharValue};
5use serde::Deserialize;
6use std::fmt::Write as _;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct CharRead {
11 pub aid: u64,
13 pub iid: u64,
15 pub value: Option<CharValue>,
17 pub status: Option<i64>,
19}
20
21pub fn build_read_request(ids: &[(u64, u64)]) -> String {
26 let mut path = String::from("/characteristics?id=");
27 for (i, (aid, iid)) in ids.iter().enumerate() {
28 if i > 0 {
29 path.push(',');
30 }
31 let _ = write!(path, "{aid}.{iid}");
33 }
34 path.push_str("&meta=1");
35 path
36}
37
38#[derive(Debug, Deserialize)]
41struct WireReadResponse {
42 characteristics: Vec<WireReadEntry>,
43}
44
45#[derive(Debug, Deserialize)]
46struct WireReadEntry {
47 aid: u64,
48 iid: u64,
49 #[serde(default)]
50 value: Option<serde_json::Value>,
51 #[serde(default)]
52 status: Option<i64>,
53 #[serde(default)]
54 format: Option<CharFormat>,
55}
56
57pub fn parse_read_response(json: &[u8]) -> Result<Vec<((u64, u64), CharValue)>> {
71 let wire: WireReadResponse = serde_json::from_slice(json)?;
72 let mut out = Vec::with_capacity(wire.characteristics.len());
73 for e in wire.characteristics {
74 if let Some(s) = e.status {
75 if s != 0 {
76 return Err(ModelError::CharacteristicStatus {
77 aid: e.aid,
78 iid: e.iid,
79 status: s,
80 });
81 }
82 }
83 let Some(raw) = e.value else { continue };
84 let value = match e.format {
85 Some(fmt) => fmt.value_from_json(&raw)?,
86 None => infer_value(&raw)?,
87 };
88 out.push(((e.aid, e.iid), value));
89 }
90 Ok(out)
91}
92
93fn infer_value(v: &serde_json::Value) -> Result<CharValue> {
95 use serde_json::Value as J;
96 match v {
97 J::Bool(b) => Ok(CharValue::Bool(*b)),
98 J::String(s) => Ok(CharValue::Str(s.clone())),
99 J::Number(n) => {
100 if let Some(u) = n.as_u64() {
101 Ok(CharValue::Uint(u))
102 } else if let Some(i) = n.as_i64() {
103 Ok(CharValue::Int(i))
104 } else if let Some(f) = n.as_f64() {
105 Ok(CharValue::Float(f))
106 } else {
107 Err(ModelError::ValueType {
108 format: "unknown",
109 detail: format!("uninterpretable number {n}"),
110 })
111 }
112 }
113 other => Err(ModelError::ValueType {
114 format: "unknown",
115 detail: format!("got JSON {other}"),
116 }),
117 }
118}
119
120pub fn build_write_request(writes: &[((u64, u64), CharValue)]) -> Vec<u8> {
125 let entries: Vec<serde_json::Value> = writes
126 .iter()
127 .map(|((aid, iid), v)| serde_json::json!({ "aid": aid, "iid": iid, "value": v.to_json() }))
128 .collect();
129 let body = serde_json::json!({ "characteristics": entries });
130 serde_json::to_vec(&body).unwrap_or_default()
132}
133
134pub fn build_subscribe_request(ids: &[(u64, u64)], enable: bool) -> Vec<u8> {
137 let entries: Vec<serde_json::Value> = ids
138 .iter()
139 .map(|(aid, iid)| serde_json::json!({ "aid": aid, "iid": iid, "ev": enable }))
140 .collect();
141 let body = serde_json::json!({ "characteristics": entries });
142 serde_json::to_vec(&body).unwrap_or_default()
143}