#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum StatusCode {
Invalidpincode = 2,
Invalidrating = 3,
Invalidchannel = 4,
Channelalreadyexist = 5,
Channelnotexist = 6,
Unidentifiableapplication = 7,
Applicationalreadyexist = 8,
Applicationnotexist = 9,
Timewindowalreadyexist = 10,
Timewindownotexist = 11,
}
impl StatusCode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
2 => Some(StatusCode::Invalidpincode),
3 => Some(StatusCode::Invalidrating),
4 => Some(StatusCode::Invalidchannel),
5 => Some(StatusCode::Channelalreadyexist),
6 => Some(StatusCode::Channelnotexist),
7 => Some(StatusCode::Unidentifiableapplication),
8 => Some(StatusCode::Applicationalreadyexist),
9 => Some(StatusCode::Applicationnotexist),
10 => Some(StatusCode::Timewindowalreadyexist),
11 => Some(StatusCode::Timewindownotexist),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<StatusCode> for u8 {
fn from(val: StatusCode) -> Self {
val as u8
}
}
pub type DayOfWeek = u8;
pub mod dayofweek {
pub const SUNDAY: u8 = 0x01;
pub const MONDAY: u8 = 0x02;
pub const TUESDAY: u8 = 0x04;
pub const WEDNESDAY: u8 = 0x08;
pub const THURSDAY: u8 = 0x10;
pub const FRIDAY: u8 = 0x20;
pub const SATURDAY: u8 = 0x40;
}
#[derive(Debug, serde::Serialize)]
pub struct AppInfo {
pub catalog_vendor_id: Option<u16>,
pub application_id: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct BlockChannel {
pub block_channel_index: Option<u16>,
pub major_number: Option<u16>,
pub minor_number: Option<u16>,
pub identifier: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct RatingName {
pub rating_name: Option<String>,
pub rating_name_desc: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct TimePeriod {
pub start_hour: Option<u8>,
pub start_minute: Option<u8>,
pub end_hour: Option<u8>,
pub end_minute: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct TimeWindow {
pub time_window_index: Option<u16>,
pub day_of_week: Option<DayOfWeek>,
pub time_period: Option<Vec<TimePeriod>>,
}
pub fn encode_update_pin(old_pin: String, new_pin: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::String(old_pin)).into(),
(1, tlv::TlvItemValueEnc::String(new_pin)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_bonus_time(pin_code: Option<String>, bonus_time: u32) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
if let Some(x) = pin_code { tlv_fields.push((0, tlv::TlvItemValueEnc::String(x)).into()); }
tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(bonus_time)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_set_screen_daily_time(screen_time: u32) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt32(screen_time)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_on_demand_rating_threshold(rating: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::String(rating)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_scheduled_content_rating_threshold(rating: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::String(rating)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_block_channels(channels: Vec<BlockChannel>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::Array(channels.into_iter().map(|v| {
let mut fields = Vec::new();
if let Some(x) = v.block_channel_index { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.major_number { fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.minor_number { fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.identifier { fields.push((3, tlv::TlvItemValueEnc::String(x.clone())).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
}).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_block_channels(channel_indexes: Vec<u16>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::StructAnon(channel_indexes.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt16(v)).into()).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_block_applications(applications: Vec<AppInfo>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::Array(applications.into_iter().map(|v| {
let mut fields = Vec::new();
if let Some(x) = v.catalog_vendor_id { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.application_id { fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
}).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_block_applications(applications: Vec<AppInfo>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::Array(applications.into_iter().map(|v| {
let mut fields = Vec::new();
if let Some(x) = v.catalog_vendor_id { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = v.application_id { fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
}).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_block_content_time_window(time_window: TimeWindow) -> anyhow::Result<Vec<u8>> {
let mut time_window_fields = Vec::new();
if let Some(x) = time_window.time_window_index { time_window_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = time_window.day_of_week { time_window_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
if let Some(listv) = time_window.time_period {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.start_hour { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
if let Some(x) = inner.start_minute { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
if let Some(x) = inner.end_hour { nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
if let Some(x) = inner.end_minute { nested_fields.push((3, tlv::TlvItemValueEnc::UInt8(x)).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
time_window_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::StructInvisible(time_window_fields)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_block_content_time_window(time_window_indexes: Vec<u16>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::StructAnon(time_window_indexes.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt16(v)).into()).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_on_demand_ratings(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RatingName>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(RatingName {
rating_name: item.get_string_owned(&[0]),
rating_name_desc: item.get_string_owned(&[1]),
});
}
}
Ok(res)
}
pub fn decode_on_demand_rating_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
if let tlv::TlvItemValue::String(v) = inp {
Ok(v.clone())
} else {
Err(anyhow::anyhow!("Expected String"))
}
}
pub fn decode_scheduled_content_ratings(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RatingName>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(RatingName {
rating_name: item.get_string_owned(&[0]),
rating_name_desc: item.get_string_owned(&[1]),
});
}
}
Ok(res)
}
pub fn decode_scheduled_content_rating_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
if let tlv::TlvItemValue::String(v) = inp {
Ok(v.clone())
} else {
Err(anyhow::anyhow!("Expected String"))
}
}
pub fn decode_screen_daily_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u32)
} else {
Err(anyhow::anyhow!("Expected UInt32"))
}
}
pub fn decode_remaining_screen_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u32)
} else {
Err(anyhow::anyhow!("Expected UInt32"))
}
}
pub fn decode_block_unrated(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_block_channel_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<BlockChannel>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(BlockChannel {
block_channel_index: item.get_int(&[0]).map(|v| v as u16),
major_number: item.get_int(&[1]).map(|v| v as u16),
minor_number: item.get_int(&[2]).map(|v| v as u16),
identifier: item.get_string_owned(&[3]),
});
}
}
Ok(res)
}
pub fn decode_block_application_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AppInfo>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(AppInfo {
catalog_vendor_id: item.get_int(&[0]).map(|v| v as u16),
application_id: item.get_string_owned(&[1]),
});
}
}
Ok(res)
}
pub fn decode_block_content_time_window(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TimeWindow>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(TimeWindow {
time_window_index: item.get_int(&[0]).map(|v| v as u16),
day_of_week: item.get_int(&[1]).map(|v| v as u8),
time_period: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let mut items = Vec::new();
for list_item in l {
items.push(TimePeriod {
start_hour: list_item.get_int(&[0]).map(|v| v as u8),
start_minute: list_item.get_int(&[1]).map(|v| v as u8),
end_hour: list_item.get_int(&[2]).map(|v| v as u8),
end_minute: list_item.get_int(&[3]).map(|v| v as u8),
});
}
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x050F {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x050F, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_on_demand_ratings(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_on_demand_rating_threshold(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_scheduled_content_ratings(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_scheduled_content_rating_threshold(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_screen_daily_time(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_remaining_screen_time(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_block_unrated(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_block_channel_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_block_application_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_block_content_time_window(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "Enabled"),
(0x0001, "OnDemandRatings"),
(0x0002, "OnDemandRatingThreshold"),
(0x0003, "ScheduledContentRatings"),
(0x0004, "ScheduledContentRatingThreshold"),
(0x0005, "ScreenDailyTime"),
(0x0006, "RemainingScreenTime"),
(0x0007, "BlockUnrated"),
(0x0008, "BlockChannelList"),
(0x0009, "BlockApplicationList"),
(0x000A, "BlockContentTimeWindow"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "UpdatePIN"),
(0x01, "ResetPIN"),
(0x03, "Enable"),
(0x04, "Disable"),
(0x05, "AddBonusTime"),
(0x06, "SetScreenDailyTime"),
(0x07, "BlockUnratedContent"),
(0x08, "UnblockUnratedContent"),
(0x09, "SetOnDemandRatingThreshold"),
(0x0A, "SetScheduledContentRatingThreshold"),
(0x0B, "AddBlockChannels"),
(0x0C, "RemoveBlockChannels"),
(0x0D, "AddBlockApplications"),
(0x0E, "RemoveBlockApplications"),
(0x0F, "SetBlockContentTimeWindow"),
(0x10, "RemoveBlockContentTimeWindow"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("UpdatePIN"),
0x01 => Some("ResetPIN"),
0x03 => Some("Enable"),
0x04 => Some("Disable"),
0x05 => Some("AddBonusTime"),
0x06 => Some("SetScreenDailyTime"),
0x07 => Some("BlockUnratedContent"),
0x08 => Some("UnblockUnratedContent"),
0x09 => Some("SetOnDemandRatingThreshold"),
0x0A => Some("SetScheduledContentRatingThreshold"),
0x0B => Some("AddBlockChannels"),
0x0C => Some("RemoveBlockChannels"),
0x0D => Some("AddBlockApplications"),
0x0E => Some("RemoveBlockApplications"),
0x0F => Some("SetBlockContentTimeWindow"),
0x10 => Some("RemoveBlockContentTimeWindow"),
_ => None,
}
}
pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
match cmd_id {
0x00 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "old_pin", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "new_pin", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x01 => Some(vec![]),
0x03 => Some(vec![]),
0x04 => Some(vec![]),
0x05 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "pin_code", kind: crate::clusters::codec::FieldKind::String, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "bonus_time", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x06 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "screen_time", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x07 => Some(vec![]),
0x08 => Some(vec![]),
0x09 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "rating", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x0A => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "rating", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x0B => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "channels", kind: crate::clusters::codec::FieldKind::List { entry_type: "BlockChannelStruct" }, optional: false, nullable: false },
]),
0x0C => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "channel_indexes", kind: crate::clusters::codec::FieldKind::List { entry_type: "uint16" }, optional: false, nullable: false },
]),
0x0D => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "applications", kind: crate::clusters::codec::FieldKind::List { entry_type: "AppInfoStruct" }, optional: false, nullable: false },
]),
0x0E => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "applications", kind: crate::clusters::codec::FieldKind::List { entry_type: "AppInfoStruct" }, optional: false, nullable: false },
]),
0x0F => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "time_window", kind: crate::clusters::codec::FieldKind::Struct { name: "TimeWindowStruct" }, optional: false, nullable: false },
]),
0x10 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "time_window_indexes", kind: crate::clusters::codec::FieldKind::List { entry_type: "uint16" }, optional: false, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => {
let old_pin = crate::clusters::codec::json_util::get_string(args, "old_pin")?;
let new_pin = crate::clusters::codec::json_util::get_string(args, "new_pin")?;
encode_update_pin(old_pin, new_pin)
}
0x01 => Ok(vec![]),
0x03 => Ok(vec![]),
0x04 => Ok(vec![]),
0x05 => {
let pin_code = crate::clusters::codec::json_util::get_opt_string(args, "pin_code")?;
let bonus_time = crate::clusters::codec::json_util::get_u32(args, "bonus_time")?;
encode_add_bonus_time(pin_code, bonus_time)
}
0x06 => {
let screen_time = crate::clusters::codec::json_util::get_u32(args, "screen_time")?;
encode_set_screen_daily_time(screen_time)
}
0x07 => Ok(vec![]),
0x08 => Ok(vec![]),
0x09 => {
let rating = crate::clusters::codec::json_util::get_string(args, "rating")?;
encode_set_on_demand_rating_threshold(rating)
}
0x0A => {
let rating = crate::clusters::codec::json_util::get_string(args, "rating")?;
encode_set_scheduled_content_rating_threshold(rating)
}
0x0B => Err(anyhow::anyhow!("command \"AddBlockChannels\" has complex args: use raw mode")),
0x0C => Err(anyhow::anyhow!("command \"RemoveBlockChannels\" has complex args: use raw mode")),
0x0D => Err(anyhow::anyhow!("command \"AddBlockApplications\" has complex args: use raw mode")),
0x0E => Err(anyhow::anyhow!("command \"RemoveBlockApplications\" has complex args: use raw mode")),
0x0F => Err(anyhow::anyhow!("command \"SetBlockContentTimeWindow\" has complex args: use raw mode")),
0x10 => Err(anyhow::anyhow!("command \"RemoveBlockContentTimeWindow\" has complex args: use raw mode")),
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct ResetPINResponse {
pub pin_code: Option<String>,
}
pub fn decode_reset_pin_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ResetPINResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ResetPINResponse {
pin_code: item.get_string_owned(&[0]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub async fn update_pin(conn: &crate::controller::Connection, endpoint: u16, old_pin: String, new_pin: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_UPDATEPIN, &encode_update_pin(old_pin, new_pin)?).await?;
Ok(())
}
pub async fn reset_pin(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ResetPINResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_RESETPIN, &[]).await?;
decode_reset_pin_response(&tlv)
}
pub async fn enable(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_ENABLE, &[]).await?;
Ok(())
}
pub async fn disable(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_DISABLE, &[]).await?;
Ok(())
}
pub async fn add_bonus_time(conn: &crate::controller::Connection, endpoint: u16, pin_code: Option<String>, bonus_time: u32) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_ADDBONUSTIME, &encode_add_bonus_time(pin_code, bonus_time)?).await?;
Ok(())
}
pub async fn set_screen_daily_time(conn: &crate::controller::Connection, endpoint: u16, screen_time: u32) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_SETSCREENDAILYTIME, &encode_set_screen_daily_time(screen_time)?).await?;
Ok(())
}
pub async fn block_unrated_content(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_BLOCKUNRATEDCONTENT, &[]).await?;
Ok(())
}
pub async fn unblock_unrated_content(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_UNBLOCKUNRATEDCONTENT, &[]).await?;
Ok(())
}
pub async fn set_on_demand_rating_threshold(conn: &crate::controller::Connection, endpoint: u16, rating: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_SETONDEMANDRATINGTHRESHOLD, &encode_set_on_demand_rating_threshold(rating)?).await?;
Ok(())
}
pub async fn set_scheduled_content_rating_threshold(conn: &crate::controller::Connection, endpoint: u16, rating: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_SETSCHEDULEDCONTENTRATINGTHRESHOLD, &encode_set_scheduled_content_rating_threshold(rating)?).await?;
Ok(())
}
pub async fn add_block_channels(conn: &crate::controller::Connection, endpoint: u16, channels: Vec<BlockChannel>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_ADDBLOCKCHANNELS, &encode_add_block_channels(channels)?).await?;
Ok(())
}
pub async fn remove_block_channels(conn: &crate::controller::Connection, endpoint: u16, channel_indexes: Vec<u16>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_REMOVEBLOCKCHANNELS, &encode_remove_block_channels(channel_indexes)?).await?;
Ok(())
}
pub async fn add_block_applications(conn: &crate::controller::Connection, endpoint: u16, applications: Vec<AppInfo>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_ADDBLOCKAPPLICATIONS, &encode_add_block_applications(applications)?).await?;
Ok(())
}
pub async fn remove_block_applications(conn: &crate::controller::Connection, endpoint: u16, applications: Vec<AppInfo>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_REMOVEBLOCKAPPLICATIONS, &encode_remove_block_applications(applications)?).await?;
Ok(())
}
pub async fn set_block_content_time_window(conn: &crate::controller::Connection, endpoint: u16, time_window: TimeWindow) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_SETBLOCKCONTENTTIMEWINDOW, &encode_set_block_content_time_window(time_window)?).await?;
Ok(())
}
pub async fn remove_block_content_time_window(conn: &crate::controller::Connection, endpoint: u16, time_window_indexes: Vec<u16>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_CMD_ID_REMOVEBLOCKCONTENTTIMEWINDOW, &encode_remove_block_content_time_window(time_window_indexes)?).await?;
Ok(())
}
pub async fn read_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_ENABLED).await?;
decode_enabled(&tlv)
}
pub async fn read_on_demand_ratings(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<RatingName>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_ONDEMANDRATINGS).await?;
decode_on_demand_ratings(&tlv)
}
pub async fn read_on_demand_rating_threshold(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_ONDEMANDRATINGTHRESHOLD).await?;
decode_on_demand_rating_threshold(&tlv)
}
pub async fn read_scheduled_content_ratings(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<RatingName>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_SCHEDULEDCONTENTRATINGS).await?;
decode_scheduled_content_ratings(&tlv)
}
pub async fn read_scheduled_content_rating_threshold(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_SCHEDULEDCONTENTRATINGTHRESHOLD).await?;
decode_scheduled_content_rating_threshold(&tlv)
}
pub async fn read_screen_daily_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_SCREENDAILYTIME).await?;
decode_screen_daily_time(&tlv)
}
pub async fn read_remaining_screen_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_REMAININGSCREENTIME).await?;
decode_remaining_screen_time(&tlv)
}
pub async fn read_block_unrated(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_BLOCKUNRATED).await?;
decode_block_unrated(&tlv)
}
pub async fn read_block_channel_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<BlockChannel>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_BLOCKCHANNELLIST).await?;
decode_block_channel_list(&tlv)
}
pub async fn read_block_application_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AppInfo>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_BLOCKAPPLICATIONLIST).await?;
decode_block_application_list(&tlv)
}
pub async fn read_block_content_time_window(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TimeWindow>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_CONTROL, crate::clusters::defs::CLUSTER_CONTENT_CONTROL_ATTR_ID_BLOCKCONTENTTIMEWINDOW).await?;
decode_block_content_time_window(&tlv)
}