1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct BasicCommissioningInfo {
15 pub fail_safe_expiry_length_seconds: Option<u16>,
16 pub max_cumulative_failsafe_seconds: Option<u16>,
17}
18
19pub fn encode_arm_fail_safe(expiry_length_seconds: u16, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
23 let tlv = tlv::TlvItemEnc {
24 tag: 0,
25 value: tlv::TlvItemValueEnc::StructInvisible(vec![
26 (0, tlv::TlvItemValueEnc::UInt16(expiry_length_seconds)).into(),
27 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
28 ]),
29 };
30 Ok(tlv.encode()?)
31}
32
33pub fn encode_set_regulatory_config(new_regulatory_config: u8, country_code: String, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
35 let tlv = tlv::TlvItemEnc {
36 tag: 0,
37 value: tlv::TlvItemValueEnc::StructInvisible(vec![
38 (0, tlv::TlvItemValueEnc::UInt8(new_regulatory_config)).into(),
39 (1, tlv::TlvItemValueEnc::String(country_code)).into(),
40 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
41 ]),
42 };
43 Ok(tlv.encode()?)
44}
45
46pub fn encode_set_tc_acknowledgements(tc_version: u16, tc_user_response: u8) -> anyhow::Result<Vec<u8>> {
48 let tlv = tlv::TlvItemEnc {
49 tag: 0,
50 value: tlv::TlvItemValueEnc::StructInvisible(vec![
51 (0, tlv::TlvItemValueEnc::UInt16(tc_version)).into(),
52 (1, tlv::TlvItemValueEnc::UInt8(tc_user_response)).into(),
53 ]),
54 };
55 Ok(tlv.encode()?)
56}
57
58pub fn decode_breadcrumb(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
62 if let tlv::TlvItemValue::Int(v) = inp {
63 Ok(*v)
64 } else {
65 Err(anyhow::anyhow!("Expected Integer"))
66 }
67}
68
69pub fn decode_basic_commissioning_info(inp: &tlv::TlvItemValue) -> anyhow::Result<BasicCommissioningInfo> {
71 if let tlv::TlvItemValue::List(_fields) = inp {
72 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
74 Ok(BasicCommissioningInfo {
75 fail_safe_expiry_length_seconds: item.get_int(&[0]).map(|v| v as u16),
76 max_cumulative_failsafe_seconds: item.get_int(&[1]).map(|v| v as u16),
77 })
78 } else {
79 Err(anyhow::anyhow!("Expected struct fields"))
80 }
81}
82
83pub fn decode_regulatory_config(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
85 if let tlv::TlvItemValue::Int(v) = inp {
86 Ok(*v as u8)
87 } else {
88 Err(anyhow::anyhow!("Expected Integer"))
89 }
90}
91
92pub fn decode_location_capability(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
94 if let tlv::TlvItemValue::Int(v) = inp {
95 Ok(*v as u8)
96 } else {
97 Err(anyhow::anyhow!("Expected Integer"))
98 }
99}
100
101pub fn decode_supports_concurrent_connection(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
103 if let tlv::TlvItemValue::Bool(v) = inp {
104 Ok(*v)
105 } else {
106 Err(anyhow::anyhow!("Expected Bool"))
107 }
108}
109
110pub fn decode_tc_accepted_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
112 if let tlv::TlvItemValue::Int(v) = inp {
113 Ok(*v as u16)
114 } else {
115 Err(anyhow::anyhow!("Expected Integer"))
116 }
117}
118
119pub fn decode_tc_min_required_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
121 if let tlv::TlvItemValue::Int(v) = inp {
122 Ok(*v as u16)
123 } else {
124 Err(anyhow::anyhow!("Expected Integer"))
125 }
126}
127
128pub fn decode_tc_acknowledgements(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
130 if let tlv::TlvItemValue::Int(v) = inp {
131 Ok(*v as u8)
132 } else {
133 Err(anyhow::anyhow!("Expected Integer"))
134 }
135}
136
137pub fn decode_tc_acknowledgements_required(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
139 if let tlv::TlvItemValue::Bool(v) = inp {
140 Ok(*v)
141 } else {
142 Err(anyhow::anyhow!("Expected Bool"))
143 }
144}
145
146pub fn decode_tc_update_deadline(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
148 if let tlv::TlvItemValue::Int(v) = inp {
149 Ok(Some(*v as u32))
150 } else {
151 Ok(None)
152 }
153}
154
155
156pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
168 if cluster_id != 0x0030 {
170 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0030, got {}\"}}", cluster_id);
171 }
172
173 match attribute_id {
174 0x0000 => {
175 match decode_breadcrumb(tlv_value) {
176 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
177 Err(e) => format!("{{\"error\": \"{}\"}}", e),
178 }
179 }
180 0x0001 => {
181 match decode_basic_commissioning_info(tlv_value) {
182 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
183 Err(e) => format!("{{\"error\": \"{}\"}}", e),
184 }
185 }
186 0x0002 => {
187 match decode_regulatory_config(tlv_value) {
188 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
189 Err(e) => format!("{{\"error\": \"{}\"}}", e),
190 }
191 }
192 0x0003 => {
193 match decode_location_capability(tlv_value) {
194 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
195 Err(e) => format!("{{\"error\": \"{}\"}}", e),
196 }
197 }
198 0x0004 => {
199 match decode_supports_concurrent_connection(tlv_value) {
200 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
201 Err(e) => format!("{{\"error\": \"{}\"}}", e),
202 }
203 }
204 0x0005 => {
205 match decode_tc_accepted_version(tlv_value) {
206 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
207 Err(e) => format!("{{\"error\": \"{}\"}}", e),
208 }
209 }
210 0x0006 => {
211 match decode_tc_min_required_version(tlv_value) {
212 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
213 Err(e) => format!("{{\"error\": \"{}\"}}", e),
214 }
215 }
216 0x0007 => {
217 match decode_tc_acknowledgements(tlv_value) {
218 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
219 Err(e) => format!("{{\"error\": \"{}\"}}", e),
220 }
221 }
222 0x0008 => {
223 match decode_tc_acknowledgements_required(tlv_value) {
224 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
225 Err(e) => format!("{{\"error\": \"{}\"}}", e),
226 }
227 }
228 0x0009 => {
229 match decode_tc_update_deadline(tlv_value) {
230 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
231 Err(e) => format!("{{\"error\": \"{}\"}}", e),
232 }
233 }
234 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
235 }
236}
237
238pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
243 vec![
244 (0x0000, "Breadcrumb"),
245 (0x0001, "BasicCommissioningInfo"),
246 (0x0002, "RegulatoryConfig"),
247 (0x0003, "LocationCapability"),
248 (0x0004, "SupportsConcurrentConnection"),
249 (0x0005, "TCAcceptedVersion"),
250 (0x0006, "TCMinRequiredVersion"),
251 (0x0007, "TCAcknowledgements"),
252 (0x0008, "TCAcknowledgementsRequired"),
253 (0x0009, "TCUpdateDeadline"),
254 ]
255}
256