1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum PhysicalMovement {
16 Idle = 0,
18 Moving = 1,
20}
21
22impl PhysicalMovement {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 0 => Some(PhysicalMovement::Idle),
27 1 => Some(PhysicalMovement::Moving),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<PhysicalMovement> for u8 {
39 fn from(val: PhysicalMovement) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, serde::Serialize)]
47pub struct DPTZ {
48 pub video_stream_id: Option<u8>,
49}
50
51#[derive(Debug, serde::Serialize)]
52pub struct MPTZPreset {
53 pub preset_id: Option<u8>,
54 pub name: Option<String>,
55 pub settings: Option<MPTZ>,
56}
57
58#[derive(Debug, serde::Serialize)]
59pub struct MPTZ {
60 pub pan: Option<i16>,
61 pub tilt: Option<i16>,
62 pub zoom: Option<u8>,
63}
64
65pub fn encode_mptz_set_position(pan: i16, tilt: i16, zoom: u8) -> anyhow::Result<Vec<u8>> {
69 let tlv = tlv::TlvItemEnc {
70 tag: 0,
71 value: tlv::TlvItemValueEnc::StructInvisible(vec![
72 (0, tlv::TlvItemValueEnc::Int16(pan)).into(),
73 (1, tlv::TlvItemValueEnc::Int16(tilt)).into(),
74 (2, tlv::TlvItemValueEnc::UInt8(zoom)).into(),
75 ]),
76 };
77 Ok(tlv.encode()?)
78}
79
80pub fn encode_mptz_relative_move(pan_delta: i16, tilt_delta: i16, zoom_delta: i8) -> anyhow::Result<Vec<u8>> {
82 let tlv = tlv::TlvItemEnc {
83 tag: 0,
84 value: tlv::TlvItemValueEnc::StructInvisible(vec![
85 (0, tlv::TlvItemValueEnc::Int16(pan_delta)).into(),
86 (1, tlv::TlvItemValueEnc::Int16(tilt_delta)).into(),
87 (2, tlv::TlvItemValueEnc::Int8(zoom_delta)).into(),
88 ]),
89 };
90 Ok(tlv.encode()?)
91}
92
93pub fn encode_mptz_move_to_preset(preset_id: u8) -> anyhow::Result<Vec<u8>> {
95 let tlv = tlv::TlvItemEnc {
96 tag: 0,
97 value: tlv::TlvItemValueEnc::StructInvisible(vec![
98 (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
99 ]),
100 };
101 Ok(tlv.encode()?)
102}
103
104pub fn encode_mptz_save_preset(preset_id: u8, name: String) -> anyhow::Result<Vec<u8>> {
106 let tlv = tlv::TlvItemEnc {
107 tag: 0,
108 value: tlv::TlvItemValueEnc::StructInvisible(vec![
109 (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
110 (1, tlv::TlvItemValueEnc::String(name)).into(),
111 ]),
112 };
113 Ok(tlv.encode()?)
114}
115
116pub fn encode_mptz_remove_preset(preset_id: u8) -> anyhow::Result<Vec<u8>> {
118 let tlv = tlv::TlvItemEnc {
119 tag: 0,
120 value: tlv::TlvItemValueEnc::StructInvisible(vec![
121 (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
122 ]),
123 };
124 Ok(tlv.encode()?)
125}
126
127pub fn encode_dptz_set_viewport(video_stream_id: u8) -> anyhow::Result<Vec<u8>> {
129 let tlv = tlv::TlvItemEnc {
130 tag: 0,
131 value: tlv::TlvItemValueEnc::StructInvisible(vec![
132 (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
133 ]),
134 };
135 Ok(tlv.encode()?)
136}
137
138pub fn encode_dptz_relative_move(video_stream_id: u8, delta_x: i16, delta_y: i16, zoom_delta: i8) -> anyhow::Result<Vec<u8>> {
140 let tlv = tlv::TlvItemEnc {
141 tag: 0,
142 value: tlv::TlvItemValueEnc::StructInvisible(vec![
143 (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
144 (1, tlv::TlvItemValueEnc::Int16(delta_x)).into(),
145 (2, tlv::TlvItemValueEnc::Int16(delta_y)).into(),
146 (3, tlv::TlvItemValueEnc::Int8(zoom_delta)).into(),
147 ]),
148 };
149 Ok(tlv.encode()?)
150}
151
152pub fn decode_mptz_position(inp: &tlv::TlvItemValue) -> anyhow::Result<MPTZ> {
156 if let tlv::TlvItemValue::List(_fields) = inp {
157 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
159 Ok(MPTZ {
160 pan: item.get_int(&[0]).map(|v| v as i16),
161 tilt: item.get_int(&[1]).map(|v| v as i16),
162 zoom: item.get_int(&[2]).map(|v| v as u8),
163 })
164 } else {
165 Err(anyhow::anyhow!("Expected struct fields"))
166 }
167}
168
169pub fn decode_max_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
171 if let tlv::TlvItemValue::Int(v) = inp {
172 Ok(*v as u8)
173 } else {
174 Err(anyhow::anyhow!("Expected UInt8"))
175 }
176}
177
178pub fn decode_mptz_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<MPTZPreset>> {
180 let mut res = Vec::new();
181 if let tlv::TlvItemValue::List(v) = inp {
182 for item in v {
183 res.push(MPTZPreset {
184 preset_id: item.get_int(&[0]).map(|v| v as u8),
185 name: item.get_string_owned(&[1]),
186 settings: {
187 if let Some(nested_tlv) = item.get(&[2]) {
188 if let tlv::TlvItemValue::List(_) = nested_tlv {
189 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
190 Some(MPTZ {
191 pan: nested_item.get_int(&[0]).map(|v| v as i16),
192 tilt: nested_item.get_int(&[1]).map(|v| v as i16),
193 zoom: nested_item.get_int(&[2]).map(|v| v as u8),
194 })
195 } else {
196 None
197 }
198 } else {
199 None
200 }
201 },
202 });
203 }
204 }
205 Ok(res)
206}
207
208pub fn decode_dptz_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DPTZ>> {
210 let mut res = Vec::new();
211 if let tlv::TlvItemValue::List(v) = inp {
212 for item in v {
213 res.push(DPTZ {
214 video_stream_id: item.get_int(&[0]).map(|v| v as u8),
215 });
216 }
217 }
218 Ok(res)
219}
220
221pub fn decode_zoom_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
223 if let tlv::TlvItemValue::Int(v) = inp {
224 Ok(*v as u8)
225 } else {
226 Err(anyhow::anyhow!("Expected UInt8"))
227 }
228}
229
230pub fn decode_tilt_min(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
232 if let tlv::TlvItemValue::Int(v) = inp {
233 Ok(*v as i16)
234 } else {
235 Err(anyhow::anyhow!("Expected Int16"))
236 }
237}
238
239pub fn decode_tilt_max(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
241 if let tlv::TlvItemValue::Int(v) = inp {
242 Ok(*v as i16)
243 } else {
244 Err(anyhow::anyhow!("Expected Int16"))
245 }
246}
247
248pub fn decode_pan_min(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
250 if let tlv::TlvItemValue::Int(v) = inp {
251 Ok(*v as i16)
252 } else {
253 Err(anyhow::anyhow!("Expected Int16"))
254 }
255}
256
257pub fn decode_pan_max(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
259 if let tlv::TlvItemValue::Int(v) = inp {
260 Ok(*v as i16)
261 } else {
262 Err(anyhow::anyhow!("Expected Int16"))
263 }
264}
265
266pub fn decode_movement_state(inp: &tlv::TlvItemValue) -> anyhow::Result<PhysicalMovement> {
268 if let tlv::TlvItemValue::Int(v) = inp {
269 PhysicalMovement::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
270 } else {
271 Err(anyhow::anyhow!("Expected Integer"))
272 }
273}
274
275
276pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
288 if cluster_id != 0x0552 {
290 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0552, got {}\"}}", cluster_id);
291 }
292
293 match attribute_id {
294 0x0000 => {
295 match decode_mptz_position(tlv_value) {
296 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
297 Err(e) => format!("{{\"error\": \"{}\"}}", e),
298 }
299 }
300 0x0001 => {
301 match decode_max_presets(tlv_value) {
302 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
303 Err(e) => format!("{{\"error\": \"{}\"}}", e),
304 }
305 }
306 0x0002 => {
307 match decode_mptz_presets(tlv_value) {
308 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
309 Err(e) => format!("{{\"error\": \"{}\"}}", e),
310 }
311 }
312 0x0003 => {
313 match decode_dptz_streams(tlv_value) {
314 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
315 Err(e) => format!("{{\"error\": \"{}\"}}", e),
316 }
317 }
318 0x0004 => {
319 match decode_zoom_max(tlv_value) {
320 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
321 Err(e) => format!("{{\"error\": \"{}\"}}", e),
322 }
323 }
324 0x0005 => {
325 match decode_tilt_min(tlv_value) {
326 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
327 Err(e) => format!("{{\"error\": \"{}\"}}", e),
328 }
329 }
330 0x0006 => {
331 match decode_tilt_max(tlv_value) {
332 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
333 Err(e) => format!("{{\"error\": \"{}\"}}", e),
334 }
335 }
336 0x0007 => {
337 match decode_pan_min(tlv_value) {
338 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
339 Err(e) => format!("{{\"error\": \"{}\"}}", e),
340 }
341 }
342 0x0008 => {
343 match decode_pan_max(tlv_value) {
344 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
345 Err(e) => format!("{{\"error\": \"{}\"}}", e),
346 }
347 }
348 0x0009 => {
349 match decode_movement_state(tlv_value) {
350 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
351 Err(e) => format!("{{\"error\": \"{}\"}}", e),
352 }
353 }
354 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
355 }
356}
357
358pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
363 vec![
364 (0x0000, "MPTZPosition"),
365 (0x0001, "MaxPresets"),
366 (0x0002, "MPTZPresets"),
367 (0x0003, "DPTZStreams"),
368 (0x0004, "ZoomMax"),
369 (0x0005, "TiltMin"),
370 (0x0006, "TiltMax"),
371 (0x0007, "PanMin"),
372 (0x0008, "PanMax"),
373 (0x0009, "MovementState"),
374 ]
375}
376