matc/clusters/codec/
camera_av_settings_user_level_management.rs

1//! Matter TLV encoders and decoders for Camera AV Settings User Level Management Cluster
2//! Cluster ID: 0x0552
3//!
4//! This file is automatically generated from CameraAVSettingsUserLevelManagement.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Enum definitions
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum PhysicalMovement {
18    /// The camera is idle.
19    Idle = 0,
20    /// The camera is moving to a new value of Pan, Tilt, and/or Zoom.
21    Moving = 1,
22}
23
24impl PhysicalMovement {
25    /// Convert from u8 value
26    pub fn from_u8(value: u8) -> Option<Self> {
27        match value {
28            0 => Some(PhysicalMovement::Idle),
29            1 => Some(PhysicalMovement::Moving),
30            _ => None,
31        }
32    }
33
34    /// Convert to u8 value
35    pub fn to_u8(self) -> u8 {
36        self as u8
37    }
38}
39
40impl From<PhysicalMovement> for u8 {
41    fn from(val: PhysicalMovement) -> Self {
42        val as u8
43    }
44}
45
46// Struct definitions
47
48#[derive(Debug, serde::Serialize)]
49pub struct DPTZ {
50    pub video_stream_id: Option<u8>,
51}
52
53#[derive(Debug, serde::Serialize)]
54pub struct MPTZPreset {
55    pub preset_id: Option<u8>,
56    pub name: Option<String>,
57    pub settings: Option<MPTZ>,
58}
59
60#[derive(Debug, serde::Serialize)]
61pub struct MPTZ {
62    pub pan: Option<i16>,
63    pub tilt: Option<i16>,
64    pub zoom: Option<u8>,
65}
66
67// Command encoders
68
69/// Encode MPTZSetPosition command (0x00)
70pub fn encode_mptz_set_position(pan: i16, tilt: i16, zoom: u8) -> anyhow::Result<Vec<u8>> {
71    let tlv = tlv::TlvItemEnc {
72        tag: 0,
73        value: tlv::TlvItemValueEnc::StructInvisible(vec![
74        (0, tlv::TlvItemValueEnc::Int16(pan)).into(),
75        (1, tlv::TlvItemValueEnc::Int16(tilt)).into(),
76        (2, tlv::TlvItemValueEnc::UInt8(zoom)).into(),
77        ]),
78    };
79    Ok(tlv.encode()?)
80}
81
82/// Encode MPTZRelativeMove command (0x01)
83pub fn encode_mptz_relative_move(pan_delta: i16, tilt_delta: i16, zoom_delta: i8) -> anyhow::Result<Vec<u8>> {
84    let tlv = tlv::TlvItemEnc {
85        tag: 0,
86        value: tlv::TlvItemValueEnc::StructInvisible(vec![
87        (0, tlv::TlvItemValueEnc::Int16(pan_delta)).into(),
88        (1, tlv::TlvItemValueEnc::Int16(tilt_delta)).into(),
89        (2, tlv::TlvItemValueEnc::Int8(zoom_delta)).into(),
90        ]),
91    };
92    Ok(tlv.encode()?)
93}
94
95/// Encode MPTZMoveToPreset command (0x02)
96pub fn encode_mptz_move_to_preset(preset_id: u8) -> anyhow::Result<Vec<u8>> {
97    let tlv = tlv::TlvItemEnc {
98        tag: 0,
99        value: tlv::TlvItemValueEnc::StructInvisible(vec![
100        (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
101        ]),
102    };
103    Ok(tlv.encode()?)
104}
105
106/// Encode MPTZSavePreset command (0x03)
107pub fn encode_mptz_save_preset(preset_id: u8, name: String) -> anyhow::Result<Vec<u8>> {
108    let tlv = tlv::TlvItemEnc {
109        tag: 0,
110        value: tlv::TlvItemValueEnc::StructInvisible(vec![
111        (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
112        (1, tlv::TlvItemValueEnc::String(name)).into(),
113        ]),
114    };
115    Ok(tlv.encode()?)
116}
117
118/// Encode MPTZRemovePreset command (0x04)
119pub fn encode_mptz_remove_preset(preset_id: u8) -> anyhow::Result<Vec<u8>> {
120    let tlv = tlv::TlvItemEnc {
121        tag: 0,
122        value: tlv::TlvItemValueEnc::StructInvisible(vec![
123        (0, tlv::TlvItemValueEnc::UInt8(preset_id)).into(),
124        ]),
125    };
126    Ok(tlv.encode()?)
127}
128
129/// Encode DPTZSetViewport command (0x05)
130pub fn encode_dptz_set_viewport(video_stream_id: u8) -> anyhow::Result<Vec<u8>> {
131    let tlv = tlv::TlvItemEnc {
132        tag: 0,
133        value: tlv::TlvItemValueEnc::StructInvisible(vec![
134        (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
135        ]),
136    };
137    Ok(tlv.encode()?)
138}
139
140/// Encode DPTZRelativeMove command (0x06)
141pub fn encode_dptz_relative_move(video_stream_id: u8, delta_x: i16, delta_y: i16, zoom_delta: i8) -> anyhow::Result<Vec<u8>> {
142    let tlv = tlv::TlvItemEnc {
143        tag: 0,
144        value: tlv::TlvItemValueEnc::StructInvisible(vec![
145        (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
146        (1, tlv::TlvItemValueEnc::Int16(delta_x)).into(),
147        (2, tlv::TlvItemValueEnc::Int16(delta_y)).into(),
148        (3, tlv::TlvItemValueEnc::Int8(zoom_delta)).into(),
149        ]),
150    };
151    Ok(tlv.encode()?)
152}
153
154// Attribute decoders
155
156/// Decode MPTZPosition attribute (0x0000)
157pub fn decode_mptz_position(inp: &tlv::TlvItemValue) -> anyhow::Result<MPTZ> {
158    if let tlv::TlvItemValue::List(_fields) = inp {
159        // Struct with fields
160        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
161        Ok(MPTZ {
162                pan: item.get_int(&[0]).map(|v| v as i16),
163                tilt: item.get_int(&[1]).map(|v| v as i16),
164                zoom: item.get_int(&[2]).map(|v| v as u8),
165        })
166    } else {
167        Err(anyhow::anyhow!("Expected struct fields"))
168    }
169}
170
171/// Decode MaxPresets attribute (0x0001)
172pub fn decode_max_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
173    if let tlv::TlvItemValue::Int(v) = inp {
174        Ok(*v as u8)
175    } else {
176        Err(anyhow::anyhow!("Expected UInt8"))
177    }
178}
179
180/// Decode MPTZPresets attribute (0x0002)
181pub fn decode_mptz_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<MPTZPreset>> {
182    let mut res = Vec::new();
183    if let tlv::TlvItemValue::List(v) = inp {
184        for item in v {
185            res.push(MPTZPreset {
186                preset_id: item.get_int(&[0]).map(|v| v as u8),
187                name: item.get_string_owned(&[1]),
188                settings: {
189                    if let Some(nested_tlv) = item.get(&[2]) {
190                        if let tlv::TlvItemValue::List(_) = nested_tlv {
191                            let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
192                            Some(MPTZ {
193                pan: nested_item.get_int(&[0]).map(|v| v as i16),
194                tilt: nested_item.get_int(&[1]).map(|v| v as i16),
195                zoom: nested_item.get_int(&[2]).map(|v| v as u8),
196                            })
197                        } else {
198                            None
199                        }
200                    } else {
201                        None
202                    }
203                },
204            });
205        }
206    }
207    Ok(res)
208}
209
210/// Decode DPTZStreams attribute (0x0003)
211pub fn decode_dptz_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DPTZ>> {
212    let mut res = Vec::new();
213    if let tlv::TlvItemValue::List(v) = inp {
214        for item in v {
215            res.push(DPTZ {
216                video_stream_id: item.get_int(&[0]).map(|v| v as u8),
217            });
218        }
219    }
220    Ok(res)
221}
222
223/// Decode ZoomMax attribute (0x0004)
224pub fn decode_zoom_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
225    if let tlv::TlvItemValue::Int(v) = inp {
226        Ok(*v as u8)
227    } else {
228        Err(anyhow::anyhow!("Expected UInt8"))
229    }
230}
231
232/// Decode TiltMin attribute (0x0005)
233pub fn decode_tilt_min(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
234    if let tlv::TlvItemValue::Int(v) = inp {
235        Ok(*v as i16)
236    } else {
237        Err(anyhow::anyhow!("Expected Int16"))
238    }
239}
240
241/// Decode TiltMax attribute (0x0006)
242pub fn decode_tilt_max(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
243    if let tlv::TlvItemValue::Int(v) = inp {
244        Ok(*v as i16)
245    } else {
246        Err(anyhow::anyhow!("Expected Int16"))
247    }
248}
249
250/// Decode PanMin attribute (0x0007)
251pub fn decode_pan_min(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
252    if let tlv::TlvItemValue::Int(v) = inp {
253        Ok(*v as i16)
254    } else {
255        Err(anyhow::anyhow!("Expected Int16"))
256    }
257}
258
259/// Decode PanMax attribute (0x0008)
260pub fn decode_pan_max(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
261    if let tlv::TlvItemValue::Int(v) = inp {
262        Ok(*v as i16)
263    } else {
264        Err(anyhow::anyhow!("Expected Int16"))
265    }
266}
267
268/// Decode MovementState attribute (0x0009)
269pub fn decode_movement_state(inp: &tlv::TlvItemValue) -> anyhow::Result<PhysicalMovement> {
270    if let tlv::TlvItemValue::Int(v) = inp {
271        PhysicalMovement::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
272    } else {
273        Err(anyhow::anyhow!("Expected Integer"))
274    }
275}
276
277
278// JSON dispatcher function
279
280/// Decode attribute value and return as JSON string
281///
282/// # Parameters
283/// * `cluster_id` - The cluster identifier
284/// * `attribute_id` - The attribute identifier
285/// * `tlv_value` - The TLV value to decode
286///
287/// # Returns
288/// JSON string representation of the decoded value or error
289pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
290    // Verify this is the correct cluster
291    if cluster_id != 0x0552 {
292        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0552, got {}\"}}", cluster_id);
293    }
294
295    match attribute_id {
296        0x0000 => {
297            match decode_mptz_position(tlv_value) {
298                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
299                Err(e) => format!("{{\"error\": \"{}\"}}", e),
300            }
301        }
302        0x0001 => {
303            match decode_max_presets(tlv_value) {
304                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
305                Err(e) => format!("{{\"error\": \"{}\"}}", e),
306            }
307        }
308        0x0002 => {
309            match decode_mptz_presets(tlv_value) {
310                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
311                Err(e) => format!("{{\"error\": \"{}\"}}", e),
312            }
313        }
314        0x0003 => {
315            match decode_dptz_streams(tlv_value) {
316                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
317                Err(e) => format!("{{\"error\": \"{}\"}}", e),
318            }
319        }
320        0x0004 => {
321            match decode_zoom_max(tlv_value) {
322                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
323                Err(e) => format!("{{\"error\": \"{}\"}}", e),
324            }
325        }
326        0x0005 => {
327            match decode_tilt_min(tlv_value) {
328                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
329                Err(e) => format!("{{\"error\": \"{}\"}}", e),
330            }
331        }
332        0x0006 => {
333            match decode_tilt_max(tlv_value) {
334                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
335                Err(e) => format!("{{\"error\": \"{}\"}}", e),
336            }
337        }
338        0x0007 => {
339            match decode_pan_min(tlv_value) {
340                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
341                Err(e) => format!("{{\"error\": \"{}\"}}", e),
342            }
343        }
344        0x0008 => {
345            match decode_pan_max(tlv_value) {
346                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
347                Err(e) => format!("{{\"error\": \"{}\"}}", e),
348            }
349        }
350        0x0009 => {
351            match decode_movement_state(tlv_value) {
352                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
353                Err(e) => format!("{{\"error\": \"{}\"}}", e),
354            }
355        }
356        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
357    }
358}
359
360/// Get list of all attributes supported by this cluster
361///
362/// # Returns
363/// Vector of tuples containing (attribute_id, attribute_name)
364pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
365    vec![
366        (0x0000, "MPTZPosition"),
367        (0x0001, "MaxPresets"),
368        (0x0002, "MPTZPresets"),
369        (0x0003, "DPTZStreams"),
370        (0x0004, "ZoomMax"),
371        (0x0005, "TiltMin"),
372        (0x0006, "TiltMax"),
373        (0x0007, "PanMin"),
374        (0x0008, "PanMax"),
375        (0x0009, "MovementState"),
376    ]
377}
378
379// Command listing
380
381pub fn get_command_list() -> Vec<(u32, &'static str)> {
382    vec![
383        (0x00, "MPTZSetPosition"),
384        (0x01, "MPTZRelativeMove"),
385        (0x02, "MPTZMoveToPreset"),
386        (0x03, "MPTZSavePreset"),
387        (0x04, "MPTZRemovePreset"),
388        (0x05, "DPTZSetViewport"),
389        (0x06, "DPTZRelativeMove"),
390    ]
391}
392
393pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
394    match cmd_id {
395        0x00 => Some("MPTZSetPosition"),
396        0x01 => Some("MPTZRelativeMove"),
397        0x02 => Some("MPTZMoveToPreset"),
398        0x03 => Some("MPTZSavePreset"),
399        0x04 => Some("MPTZRemovePreset"),
400        0x05 => Some("DPTZSetViewport"),
401        0x06 => Some("DPTZRelativeMove"),
402        _ => None,
403    }
404}
405
406pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
407    match cmd_id {
408        0x00 => Some(vec![
409            crate::clusters::codec::CommandField { tag: 0, name: "pan", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
410            crate::clusters::codec::CommandField { tag: 1, name: "tilt", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
411            crate::clusters::codec::CommandField { tag: 2, name: "zoom", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
412        ]),
413        0x01 => Some(vec![
414            crate::clusters::codec::CommandField { tag: 0, name: "pan_delta", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
415            crate::clusters::codec::CommandField { tag: 1, name: "tilt_delta", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
416            crate::clusters::codec::CommandField { tag: 2, name: "zoom_delta", kind: crate::clusters::codec::FieldKind::I8, optional: true, nullable: false },
417        ]),
418        0x02 => Some(vec![
419            crate::clusters::codec::CommandField { tag: 0, name: "preset_id", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
420        ]),
421        0x03 => Some(vec![
422            crate::clusters::codec::CommandField { tag: 0, name: "preset_id", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
423            crate::clusters::codec::CommandField { tag: 1, name: "name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
424        ]),
425        0x04 => Some(vec![
426            crate::clusters::codec::CommandField { tag: 0, name: "preset_id", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
427        ]),
428        0x05 => Some(vec![
429            crate::clusters::codec::CommandField { tag: 0, name: "video_stream_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
430        ]),
431        0x06 => Some(vec![
432            crate::clusters::codec::CommandField { tag: 0, name: "video_stream_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
433            crate::clusters::codec::CommandField { tag: 1, name: "delta_x", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
434            crate::clusters::codec::CommandField { tag: 2, name: "delta_y", kind: crate::clusters::codec::FieldKind::I16, optional: true, nullable: false },
435            crate::clusters::codec::CommandField { tag: 3, name: "zoom_delta", kind: crate::clusters::codec::FieldKind::I8, optional: true, nullable: false },
436        ]),
437        _ => None,
438    }
439}
440
441pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
442    match cmd_id {
443        0x00 => {
444        let pan = crate::clusters::codec::json_util::get_i16(args, "pan")?;
445        let tilt = crate::clusters::codec::json_util::get_i16(args, "tilt")?;
446        let zoom = crate::clusters::codec::json_util::get_u8(args, "zoom")?;
447        encode_mptz_set_position(pan, tilt, zoom)
448        }
449        0x01 => {
450        let pan_delta = crate::clusters::codec::json_util::get_i16(args, "pan_delta")?;
451        let tilt_delta = crate::clusters::codec::json_util::get_i16(args, "tilt_delta")?;
452        let zoom_delta = crate::clusters::codec::json_util::get_i8(args, "zoom_delta")?;
453        encode_mptz_relative_move(pan_delta, tilt_delta, zoom_delta)
454        }
455        0x02 => {
456        let preset_id = crate::clusters::codec::json_util::get_u8(args, "preset_id")?;
457        encode_mptz_move_to_preset(preset_id)
458        }
459        0x03 => {
460        let preset_id = crate::clusters::codec::json_util::get_u8(args, "preset_id")?;
461        let name = crate::clusters::codec::json_util::get_string(args, "name")?;
462        encode_mptz_save_preset(preset_id, name)
463        }
464        0x04 => {
465        let preset_id = crate::clusters::codec::json_util::get_u8(args, "preset_id")?;
466        encode_mptz_remove_preset(preset_id)
467        }
468        0x05 => {
469        let video_stream_id = crate::clusters::codec::json_util::get_u8(args, "video_stream_id")?;
470        encode_dptz_set_viewport(video_stream_id)
471        }
472        0x06 => {
473        let video_stream_id = crate::clusters::codec::json_util::get_u8(args, "video_stream_id")?;
474        let delta_x = crate::clusters::codec::json_util::get_i16(args, "delta_x")?;
475        let delta_y = crate::clusters::codec::json_util::get_i16(args, "delta_y")?;
476        let zoom_delta = crate::clusters::codec::json_util::get_i8(args, "zoom_delta")?;
477        encode_dptz_relative_move(video_stream_id, delta_x, delta_y, zoom_delta)
478        }
479        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
480    }
481}
482
483// Typed facade (invokes + reads)
484
485/// Invoke `MPTZSetPosition` command on cluster `Camera AV Settings User Level Management`.
486pub async fn mptz_set_position(conn: &crate::controller::Connection, endpoint: u16, pan: i16, tilt: i16, zoom: u8) -> anyhow::Result<()> {
487    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_MPTZSETPOSITION, &encode_mptz_set_position(pan, tilt, zoom)?).await?;
488    Ok(())
489}
490
491/// Invoke `MPTZRelativeMove` command on cluster `Camera AV Settings User Level Management`.
492pub async fn mptz_relative_move(conn: &crate::controller::Connection, endpoint: u16, pan_delta: i16, tilt_delta: i16, zoom_delta: i8) -> anyhow::Result<()> {
493    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_MPTZRELATIVEMOVE, &encode_mptz_relative_move(pan_delta, tilt_delta, zoom_delta)?).await?;
494    Ok(())
495}
496
497/// Invoke `MPTZMoveToPreset` command on cluster `Camera AV Settings User Level Management`.
498pub async fn mptz_move_to_preset(conn: &crate::controller::Connection, endpoint: u16, preset_id: u8) -> anyhow::Result<()> {
499    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_MPTZMOVETOPRESET, &encode_mptz_move_to_preset(preset_id)?).await?;
500    Ok(())
501}
502
503/// Invoke `MPTZSavePreset` command on cluster `Camera AV Settings User Level Management`.
504pub async fn mptz_save_preset(conn: &crate::controller::Connection, endpoint: u16, preset_id: u8, name: String) -> anyhow::Result<()> {
505    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_MPTZSAVEPRESET, &encode_mptz_save_preset(preset_id, name)?).await?;
506    Ok(())
507}
508
509/// Invoke `MPTZRemovePreset` command on cluster `Camera AV Settings User Level Management`.
510pub async fn mptz_remove_preset(conn: &crate::controller::Connection, endpoint: u16, preset_id: u8) -> anyhow::Result<()> {
511    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_MPTZREMOVEPRESET, &encode_mptz_remove_preset(preset_id)?).await?;
512    Ok(())
513}
514
515/// Invoke `DPTZSetViewport` command on cluster `Camera AV Settings User Level Management`.
516pub async fn dptz_set_viewport(conn: &crate::controller::Connection, endpoint: u16, video_stream_id: u8) -> anyhow::Result<()> {
517    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_DPTZSETVIEWPORT, &encode_dptz_set_viewport(video_stream_id)?).await?;
518    Ok(())
519}
520
521/// Invoke `DPTZRelativeMove` command on cluster `Camera AV Settings User Level Management`.
522pub async fn dptz_relative_move(conn: &crate::controller::Connection, endpoint: u16, video_stream_id: u8, delta_x: i16, delta_y: i16, zoom_delta: i8) -> anyhow::Result<()> {
523    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_CMD_ID_DPTZRELATIVEMOVE, &encode_dptz_relative_move(video_stream_id, delta_x, delta_y, zoom_delta)?).await?;
524    Ok(())
525}
526
527/// Read `MPTZPosition` attribute from cluster `Camera AV Settings User Level Management`.
528pub async fn read_mptz_position(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<MPTZ> {
529    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_MPTZPOSITION).await?;
530    decode_mptz_position(&tlv)
531}
532
533/// Read `MaxPresets` attribute from cluster `Camera AV Settings User Level Management`.
534pub async fn read_max_presets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
535    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_MAXPRESETS).await?;
536    decode_max_presets(&tlv)
537}
538
539/// Read `MPTZPresets` attribute from cluster `Camera AV Settings User Level Management`.
540pub async fn read_mptz_presets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<MPTZPreset>> {
541    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_MPTZPRESETS).await?;
542    decode_mptz_presets(&tlv)
543}
544
545/// Read `DPTZStreams` attribute from cluster `Camera AV Settings User Level Management`.
546pub async fn read_dptz_streams(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DPTZ>> {
547    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_DPTZSTREAMS).await?;
548    decode_dptz_streams(&tlv)
549}
550
551/// Read `ZoomMax` attribute from cluster `Camera AV Settings User Level Management`.
552pub async fn read_zoom_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
553    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_ZOOMMAX).await?;
554    decode_zoom_max(&tlv)
555}
556
557/// Read `TiltMin` attribute from cluster `Camera AV Settings User Level Management`.
558pub async fn read_tilt_min(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
559    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_TILTMIN).await?;
560    decode_tilt_min(&tlv)
561}
562
563/// Read `TiltMax` attribute from cluster `Camera AV Settings User Level Management`.
564pub async fn read_tilt_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
565    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_TILTMAX).await?;
566    decode_tilt_max(&tlv)
567}
568
569/// Read `PanMin` attribute from cluster `Camera AV Settings User Level Management`.
570pub async fn read_pan_min(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
571    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_PANMIN).await?;
572    decode_pan_min(&tlv)
573}
574
575/// Read `PanMax` attribute from cluster `Camera AV Settings User Level Management`.
576pub async fn read_pan_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
577    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_PANMAX).await?;
578    decode_pan_max(&tlv)
579}
580
581/// Read `MovementState` attribute from cluster `Camera AV Settings User Level Management`.
582pub async fn read_movement_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<PhysicalMovement> {
583    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT, crate::clusters::defs::CLUSTER_CAMERA_AV_SETTINGS_USER_LEVEL_MANAGEMENT_ATTR_ID_MOVEMENTSTATE).await?;
584    decode_movement_state(&tlv)
585}
586