1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct AppInfo {
15 pub catalog_vendor_id: Option<u16>,
16 pub application_id: Option<String>,
17}
18
19#[derive(Debug, serde::Serialize)]
20pub struct BlockChannel {
21 pub block_channel_index: Option<u16>,
22 pub major_number: Option<u16>,
23 pub minor_number: Option<u16>,
24 pub identifier: Option<String>,
25}
26
27#[derive(Debug, serde::Serialize)]
28pub struct RatingName {
29 pub rating_name: Option<String>,
30 pub rating_name_desc: Option<String>,
31}
32
33#[derive(Debug, serde::Serialize)]
34pub struct TimePeriod {
35 pub start_hour: Option<u8>,
36 pub start_minute: Option<u8>,
37 pub end_hour: Option<u8>,
38 pub end_minute: Option<u8>,
39}
40
41#[derive(Debug, serde::Serialize)]
42pub struct TimeWindow {
43 pub time_window_index: Option<u16>,
44 pub day_of_week: Option<u8>,
45 pub time_period: Option<Vec<TimePeriod>>,
46}
47
48pub fn encode_update_pin(old_pin: String, new_pin: String) -> anyhow::Result<Vec<u8>> {
52 let tlv = tlv::TlvItemEnc {
53 tag: 0,
54 value: tlv::TlvItemValueEnc::StructInvisible(vec![
55 (0, tlv::TlvItemValueEnc::String(old_pin)).into(),
56 (1, tlv::TlvItemValueEnc::String(new_pin)).into(),
57 ]),
58 };
59 Ok(tlv.encode()?)
60}
61
62pub fn encode_add_bonus_time(pin_code: String, bonus_time: u8) -> anyhow::Result<Vec<u8>> {
64 let tlv = tlv::TlvItemEnc {
65 tag: 0,
66 value: tlv::TlvItemValueEnc::StructInvisible(vec![
67 (0, tlv::TlvItemValueEnc::String(pin_code)).into(),
68 (1, tlv::TlvItemValueEnc::UInt8(bonus_time)).into(),
69 ]),
70 };
71 Ok(tlv.encode()?)
72}
73
74pub fn encode_set_screen_daily_time(screen_time: u8) -> anyhow::Result<Vec<u8>> {
76 let tlv = tlv::TlvItemEnc {
77 tag: 0,
78 value: tlv::TlvItemValueEnc::StructInvisible(vec![
79 (0, tlv::TlvItemValueEnc::UInt8(screen_time)).into(),
80 ]),
81 };
82 Ok(tlv.encode()?)
83}
84
85pub fn encode_set_on_demand_rating_threshold(rating: String) -> anyhow::Result<Vec<u8>> {
87 let tlv = tlv::TlvItemEnc {
88 tag: 0,
89 value: tlv::TlvItemValueEnc::StructInvisible(vec![
90 (0, tlv::TlvItemValueEnc::String(rating)).into(),
91 ]),
92 };
93 Ok(tlv.encode()?)
94}
95
96pub fn encode_set_scheduled_content_rating_threshold(rating: String) -> anyhow::Result<Vec<u8>> {
98 let tlv = tlv::TlvItemEnc {
99 tag: 0,
100 value: tlv::TlvItemValueEnc::StructInvisible(vec![
101 (0, tlv::TlvItemValueEnc::String(rating)).into(),
102 ]),
103 };
104 Ok(tlv.encode()?)
105}
106
107pub fn encode_add_block_channels(channels: Vec<u8>) -> anyhow::Result<Vec<u8>> {
109 let tlv = tlv::TlvItemEnc {
110 tag: 0,
111 value: tlv::TlvItemValueEnc::StructInvisible(vec![
112 (0, tlv::TlvItemValueEnc::StructAnon(channels.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
113 ]),
114 };
115 Ok(tlv.encode()?)
116}
117
118pub fn encode_remove_block_channels(channel_indexes: Vec<u8>) -> anyhow::Result<Vec<u8>> {
120 let tlv = tlv::TlvItemEnc {
121 tag: 0,
122 value: tlv::TlvItemValueEnc::StructInvisible(vec![
123 (0, tlv::TlvItemValueEnc::StructAnon(channel_indexes.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
124 ]),
125 };
126 Ok(tlv.encode()?)
127}
128
129pub fn encode_add_block_applications(applications: Vec<u8>) -> anyhow::Result<Vec<u8>> {
131 let tlv = tlv::TlvItemEnc {
132 tag: 0,
133 value: tlv::TlvItemValueEnc::StructInvisible(vec![
134 (0, tlv::TlvItemValueEnc::StructAnon(applications.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
135 ]),
136 };
137 Ok(tlv.encode()?)
138}
139
140pub fn encode_remove_block_applications(applications: Vec<u8>) -> anyhow::Result<Vec<u8>> {
142 let tlv = tlv::TlvItemEnc {
143 tag: 0,
144 value: tlv::TlvItemValueEnc::StructInvisible(vec![
145 (0, tlv::TlvItemValueEnc::StructAnon(applications.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
146 ]),
147 };
148 Ok(tlv.encode()?)
149}
150
151pub fn encode_set_block_content_time_window(time_window: u8) -> anyhow::Result<Vec<u8>> {
153 let tlv = tlv::TlvItemEnc {
154 tag: 0,
155 value: tlv::TlvItemValueEnc::StructInvisible(vec![
156 (0, tlv::TlvItemValueEnc::UInt8(time_window)).into(),
157 ]),
158 };
159 Ok(tlv.encode()?)
160}
161
162pub fn encode_remove_block_content_time_window(time_window_indexes: Vec<u8>) -> anyhow::Result<Vec<u8>> {
164 let tlv = tlv::TlvItemEnc {
165 tag: 0,
166 value: tlv::TlvItemValueEnc::StructInvisible(vec![
167 (0, tlv::TlvItemValueEnc::StructAnon(time_window_indexes.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
168 ]),
169 };
170 Ok(tlv.encode()?)
171}
172
173pub fn decode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
177 if let tlv::TlvItemValue::Bool(v) = inp {
178 Ok(*v)
179 } else {
180 Err(anyhow::anyhow!("Expected Bool"))
181 }
182}
183
184pub fn decode_on_demand_ratings(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RatingName>> {
186 let mut res = Vec::new();
187 if let tlv::TlvItemValue::List(v) = inp {
188 for item in v {
189 res.push(RatingName {
190 rating_name: item.get_string_owned(&[0]),
191 rating_name_desc: item.get_string_owned(&[1]),
192 });
193 }
194 }
195 Ok(res)
196}
197
198pub fn decode_on_demand_rating_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
200 if let tlv::TlvItemValue::String(v) = inp {
201 Ok(v.clone())
202 } else {
203 Err(anyhow::anyhow!("Expected String"))
204 }
205}
206
207pub fn decode_scheduled_content_ratings(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RatingName>> {
209 let mut res = Vec::new();
210 if let tlv::TlvItemValue::List(v) = inp {
211 for item in v {
212 res.push(RatingName {
213 rating_name: item.get_string_owned(&[0]),
214 rating_name_desc: item.get_string_owned(&[1]),
215 });
216 }
217 }
218 Ok(res)
219}
220
221pub fn decode_scheduled_content_rating_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
223 if let tlv::TlvItemValue::String(v) = inp {
224 Ok(v.clone())
225 } else {
226 Err(anyhow::anyhow!("Expected String"))
227 }
228}
229
230pub fn decode_screen_daily_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
232 if let tlv::TlvItemValue::Int(v) = inp {
233 Ok(*v as u8)
234 } else {
235 Err(anyhow::anyhow!("Expected Integer"))
236 }
237}
238
239pub fn decode_remaining_screen_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
241 if let tlv::TlvItemValue::Int(v) = inp {
242 Ok(*v as u8)
243 } else {
244 Err(anyhow::anyhow!("Expected Integer"))
245 }
246}
247
248pub fn decode_block_unrated(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
250 if let tlv::TlvItemValue::Bool(v) = inp {
251 Ok(*v)
252 } else {
253 Err(anyhow::anyhow!("Expected Bool"))
254 }
255}
256
257pub fn decode_block_channel_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<BlockChannel>> {
259 let mut res = Vec::new();
260 if let tlv::TlvItemValue::List(v) = inp {
261 for item in v {
262 res.push(BlockChannel {
263 block_channel_index: item.get_int(&[0]).map(|v| v as u16),
264 major_number: item.get_int(&[1]).map(|v| v as u16),
265 minor_number: item.get_int(&[2]).map(|v| v as u16),
266 identifier: item.get_string_owned(&[3]),
267 });
268 }
269 }
270 Ok(res)
271}
272
273pub fn decode_block_application_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AppInfo>> {
275 let mut res = Vec::new();
276 if let tlv::TlvItemValue::List(v) = inp {
277 for item in v {
278 res.push(AppInfo {
279 catalog_vendor_id: item.get_int(&[0]).map(|v| v as u16),
280 application_id: item.get_string_owned(&[1]),
281 });
282 }
283 }
284 Ok(res)
285}
286
287pub fn decode_block_content_time_window(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TimeWindow>> {
289 let mut res = Vec::new();
290 if let tlv::TlvItemValue::List(v) = inp {
291 for item in v {
292 res.push(TimeWindow {
293 time_window_index: item.get_int(&[0]).map(|v| v as u16),
294 day_of_week: item.get_int(&[1]).map(|v| v as u8),
295 time_period: {
296 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
297 let mut items = Vec::new();
298 for list_item in l {
299 items.push(TimePeriod {
300 start_hour: list_item.get_int(&[0]).map(|v| v as u8),
301 start_minute: list_item.get_int(&[1]).map(|v| v as u8),
302 end_hour: list_item.get_int(&[2]).map(|v| v as u8),
303 end_minute: list_item.get_int(&[3]).map(|v| v as u8),
304 });
305 }
306 Some(items)
307 } else {
308 None
309 }
310 },
311 });
312 }
313 }
314 Ok(res)
315}
316
317
318pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
330 if cluster_id != 0x050F {
332 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x050F, got {}\"}}", cluster_id);
333 }
334
335 match attribute_id {
336 0x0000 => {
337 match decode_enabled(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 0x0001 => {
343 match decode_on_demand_ratings(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 0x0002 => {
349 match decode_on_demand_rating_threshold(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 0x0003 => {
355 match decode_scheduled_content_ratings(tlv_value) {
356 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
357 Err(e) => format!("{{\"error\": \"{}\"}}", e),
358 }
359 }
360 0x0004 => {
361 match decode_scheduled_content_rating_threshold(tlv_value) {
362 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
363 Err(e) => format!("{{\"error\": \"{}\"}}", e),
364 }
365 }
366 0x0005 => {
367 match decode_screen_daily_time(tlv_value) {
368 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
369 Err(e) => format!("{{\"error\": \"{}\"}}", e),
370 }
371 }
372 0x0006 => {
373 match decode_remaining_screen_time(tlv_value) {
374 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
375 Err(e) => format!("{{\"error\": \"{}\"}}", e),
376 }
377 }
378 0x0007 => {
379 match decode_block_unrated(tlv_value) {
380 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
381 Err(e) => format!("{{\"error\": \"{}\"}}", e),
382 }
383 }
384 0x0008 => {
385 match decode_block_channel_list(tlv_value) {
386 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
387 Err(e) => format!("{{\"error\": \"{}\"}}", e),
388 }
389 }
390 0x0009 => {
391 match decode_block_application_list(tlv_value) {
392 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
393 Err(e) => format!("{{\"error\": \"{}\"}}", e),
394 }
395 }
396 0x000A => {
397 match decode_block_content_time_window(tlv_value) {
398 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
399 Err(e) => format!("{{\"error\": \"{}\"}}", e),
400 }
401 }
402 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
403 }
404}
405
406pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
411 vec![
412 (0x0000, "Enabled"),
413 (0x0001, "OnDemandRatings"),
414 (0x0002, "OnDemandRatingThreshold"),
415 (0x0003, "ScheduledContentRatings"),
416 (0x0004, "ScheduledContentRatingThreshold"),
417 (0x0005, "ScreenDailyTime"),
418 (0x0006, "RemainingScreenTime"),
419 (0x0007, "BlockUnrated"),
420 (0x0008, "BlockChannelList"),
421 (0x0009, "BlockApplicationList"),
422 (0x000A, "BlockContentTimeWindow"),
423 ]
424}
425