1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum InputType {
18 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 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 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#[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
75pub 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
88pub 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
100pub 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
118pub 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
128pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
140 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
162pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
167 vec![
168 (0x0000, "InputList"),
169 (0x0001, "CurrentInput"),
170 ]
171}
172
173pub 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
226pub 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
234pub 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
240pub 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
246pub 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
252pub 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
258pub 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