matc/clusters/codec/
media_input.rs

1//! Matter TLV encoders and decoders for Media Input Cluster
2//! Cluster ID: 0x0507
3//!
4//! This file is automatically generated from MediaInput.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 InputType {
18    /// Indicates content not coming from a physical input.
19    Internal = 0,
20    Aux = 1,
21    Coax = 2,
22    Composite = 3,
23    Hdmi = 4,
24    Input = 5,
25    Line = 6,
26    Optical = 7,
27    Video = 8,
28    Scart = 9,
29    Usb = 10,
30    Other = 11,
31}
32
33impl InputType {
34    /// Convert from u8 value
35    pub fn from_u8(value: u8) -> Option<Self> {
36        match value {
37            0 => Some(InputType::Internal),
38            1 => Some(InputType::Aux),
39            2 => Some(InputType::Coax),
40            3 => Some(InputType::Composite),
41            4 => Some(InputType::Hdmi),
42            5 => Some(InputType::Input),
43            6 => Some(InputType::Line),
44            7 => Some(InputType::Optical),
45            8 => Some(InputType::Video),
46            9 => Some(InputType::Scart),
47            10 => Some(InputType::Usb),
48            11 => Some(InputType::Other),
49            _ => None,
50        }
51    }
52
53    /// Convert to u8 value
54    pub fn to_u8(self) -> u8 {
55        self as u8
56    }
57}
58
59impl From<InputType> for u8 {
60    fn from(val: InputType) -> Self {
61        val as u8
62    }
63}
64
65// Struct definitions
66
67#[derive(Debug, serde::Serialize)]
68pub struct InputInfo {
69    pub index: Option<u8>,
70    pub input_type: Option<InputType>,
71    pub name: Option<String>,
72    pub description: Option<String>,
73}
74
75// Command encoders
76
77/// Encode SelectInput command (0x00)
78pub fn encode_select_input(index: u8) -> anyhow::Result<Vec<u8>> {
79    let tlv = tlv::TlvItemEnc {
80        tag: 0,
81        value: tlv::TlvItemValueEnc::StructInvisible(vec![
82        (0, tlv::TlvItemValueEnc::UInt8(index)).into(),
83        ]),
84    };
85    Ok(tlv.encode()?)
86}
87
88/// Encode RenameInput command (0x03)
89pub fn encode_rename_input(index: u8, name: String) -> anyhow::Result<Vec<u8>> {
90    let tlv = tlv::TlvItemEnc {
91        tag: 0,
92        value: tlv::TlvItemValueEnc::StructInvisible(vec![
93        (0, tlv::TlvItemValueEnc::UInt8(index)).into(),
94        (1, tlv::TlvItemValueEnc::String(name)).into(),
95        ]),
96    };
97    Ok(tlv.encode()?)
98}
99
100// Attribute decoders
101
102/// Decode InputList attribute (0x0000)
103pub fn decode_input_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<InputInfo>> {
104    let mut res = Vec::new();
105    if let tlv::TlvItemValue::List(v) = inp {
106        for item in v {
107            res.push(InputInfo {
108                index: item.get_int(&[0]).map(|v| v as u8),
109                input_type: item.get_int(&[1]).and_then(|v| InputType::from_u8(v as u8)),
110                name: item.get_string_owned(&[2]),
111                description: item.get_string_owned(&[3]),
112            });
113        }
114    }
115    Ok(res)
116}
117
118/// Decode CurrentInput attribute (0x0001)
119pub fn decode_current_input(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
120    if let tlv::TlvItemValue::Int(v) = inp {
121        Ok(*v as u8)
122    } else {
123        Err(anyhow::anyhow!("Expected UInt8"))
124    }
125}
126
127
128// JSON dispatcher function
129
130/// Decode attribute value and return as JSON string
131///
132/// # Parameters
133/// * `cluster_id` - The cluster identifier
134/// * `attribute_id` - The attribute identifier
135/// * `tlv_value` - The TLV value to decode
136///
137/// # Returns
138/// JSON string representation of the decoded value or error
139pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
140    // Verify this is the correct cluster
141    if cluster_id != 0x0507 {
142        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0507, got {}\"}}", cluster_id);
143    }
144
145    match attribute_id {
146        0x0000 => {
147            match decode_input_list(tlv_value) {
148                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
149                Err(e) => format!("{{\"error\": \"{}\"}}", e),
150            }
151        }
152        0x0001 => {
153            match decode_current_input(tlv_value) {
154                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
155                Err(e) => format!("{{\"error\": \"{}\"}}", e),
156            }
157        }
158        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
159    }
160}
161
162/// Get list of all attributes supported by this cluster
163///
164/// # Returns
165/// Vector of tuples containing (attribute_id, attribute_name)
166pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
167    vec![
168        (0x0000, "InputList"),
169        (0x0001, "CurrentInput"),
170    ]
171}
172
173// Command listing
174
175pub fn get_command_list() -> Vec<(u32, &'static str)> {
176    vec![
177        (0x00, "SelectInput"),
178        (0x01, "ShowInputStatus"),
179        (0x02, "HideInputStatus"),
180        (0x03, "RenameInput"),
181    ]
182}
183
184pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
185    match cmd_id {
186        0x00 => Some("SelectInput"),
187        0x01 => Some("ShowInputStatus"),
188        0x02 => Some("HideInputStatus"),
189        0x03 => Some("RenameInput"),
190        _ => None,
191    }
192}
193
194pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
195    match cmd_id {
196        0x00 => Some(vec![
197            crate::clusters::codec::CommandField { tag: 0, name: "index", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
198        ]),
199        0x01 => Some(vec![]),
200        0x02 => Some(vec![]),
201        0x03 => Some(vec![
202            crate::clusters::codec::CommandField { tag: 0, name: "index", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
203            crate::clusters::codec::CommandField { tag: 1, name: "name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
204        ]),
205        _ => None,
206    }
207}
208
209pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
210    match cmd_id {
211        0x00 => {
212        let index = crate::clusters::codec::json_util::get_u8(args, "index")?;
213        encode_select_input(index)
214        }
215        0x01 => Ok(vec![]),
216        0x02 => Ok(vec![]),
217        0x03 => {
218        let index = crate::clusters::codec::json_util::get_u8(args, "index")?;
219        let name = crate::clusters::codec::json_util::get_string(args, "name")?;
220        encode_rename_input(index, name)
221        }
222        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
223    }
224}
225
226// Typed facade (invokes + reads)
227
228/// Invoke `SelectInput` command on cluster `Media Input`.
229pub async fn select_input(conn: &crate::controller::Connection, endpoint: u16, index: u8) -> anyhow::Result<()> {
230    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_CMD_ID_SELECTINPUT, &encode_select_input(index)?).await?;
231    Ok(())
232}
233
234/// Invoke `ShowInputStatus` command on cluster `Media Input`.
235pub async fn show_input_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
236    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_CMD_ID_SHOWINPUTSTATUS, &[]).await?;
237    Ok(())
238}
239
240/// Invoke `HideInputStatus` command on cluster `Media Input`.
241pub async fn hide_input_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
242    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_CMD_ID_HIDEINPUTSTATUS, &[]).await?;
243    Ok(())
244}
245
246/// Invoke `RenameInput` command on cluster `Media Input`.
247pub async fn rename_input(conn: &crate::controller::Connection, endpoint: u16, index: u8, name: String) -> anyhow::Result<()> {
248    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_CMD_ID_RENAMEINPUT, &encode_rename_input(index, name)?).await?;
249    Ok(())
250}
251
252/// Read `InputList` attribute from cluster `Media Input`.
253pub async fn read_input_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<InputInfo>> {
254    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_ATTR_ID_INPUTLIST).await?;
255    decode_input_list(&tlv)
256}
257
258/// Read `CurrentInput` attribute from cluster `Media Input`.
259pub async fn read_current_input(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
260    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_MEDIA_INPUT, crate::clusters::defs::CLUSTER_MEDIA_INPUT_ATTR_ID_CURRENTINPUT).await?;
261    decode_current_input(&tlv)
262}
263