//! Matter TLV encoders and decoders for Content Launcher Cluster
//! Cluster ID: 0x050A
//!
//! This file is automatically generated from ContentLauncher.xml
#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
// Enum definitions
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum MetricType {
/// Dimensions defined in a number of Pixels
Pixels = 0,
/// Dimensions defined as a percentage
Percentage = 1,
}
impl MetricType {
/// Convert from u8 value
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(MetricType::Pixels),
1 => Some(MetricType::Percentage),
_ => None,
}
}
/// Convert to u8 value
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<MetricType> for u8 {
fn from(val: MetricType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ParameterEnum {
/// Actor represents an actor credited in video media content; for example, “Gaby Hoffman”
Actor = 0,
/// Channel represents the identifying data for a television channel; for example, "PBS"
Channel = 1,
/// A character represented in video media content; for example, “Snow White”
Character = 2,
/// A director of the video media content; for example, “Spike Lee”
Director = 3,
/// An event is a reference to a type of event; examples would include sports, music, or other types of events. For example, searching for "Football games" would search for a 'game' event entity and a 'football' sport entity.
Event = 4,
/// A franchise is a video entity which can represent a number of video entities, like movies or TV shows. For example, take the fictional franchise "Intergalactic Wars" which represents a collection of movie trilogies, as well as animated and live action TV shows. This entity type was introduced to account for requests by customers such as "Find Intergalactic Wars movies", which would search for all 'Intergalactic Wars' programs of the MOVIE MediaType, rather than attempting to match to a single title.
Franchise = 5,
/// Genre represents the genre of video media content such as action, drama or comedy.
Genre = 6,
/// League represents the categorical information for a sporting league; for example, "NCAA"
League = 7,
/// Popularity indicates whether the user asks for popular content.
Popularity = 8,
/// The provider (MSP) the user wants this media to be played on; for example, "Netflix".
Provider = 9,
/// Sport represents the categorical information of a sport; for example, football
Sport = 10,
/// SportsTeam represents the categorical information of a professional sports team; for example, "University of Washington Huskies"
Sportsteam = 11,
/// The type of content requested. Supported types are "Movie", "MovieSeries", "TVSeries", "TVSeason", "TVEpisode", "Trailer", "SportsEvent", "LiveEvent", and "Video"
Type = 12,
/// Video represents the identifying data for a specific piece of video content; for example, "Manchester by the Sea".
Video = 13,
/// Season represents the specific season number within a TV series.
Season = 14,
/// Episode represents a specific episode number within a Season in a TV series.
Episode = 15,
/// Represents a search text input across many parameter types or even outside of the defined param types.
Any = 16,
}
impl ParameterEnum {
/// Convert from u8 value
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ParameterEnum::Actor),
1 => Some(ParameterEnum::Channel),
2 => Some(ParameterEnum::Character),
3 => Some(ParameterEnum::Director),
4 => Some(ParameterEnum::Event),
5 => Some(ParameterEnum::Franchise),
6 => Some(ParameterEnum::Genre),
7 => Some(ParameterEnum::League),
8 => Some(ParameterEnum::Popularity),
9 => Some(ParameterEnum::Provider),
10 => Some(ParameterEnum::Sport),
11 => Some(ParameterEnum::Sportsteam),
12 => Some(ParameterEnum::Type),
13 => Some(ParameterEnum::Video),
14 => Some(ParameterEnum::Season),
15 => Some(ParameterEnum::Episode),
16 => Some(ParameterEnum::Any),
_ => None,
}
}
/// Convert to u8 value
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ParameterEnum> for u8 {
fn from(val: ParameterEnum) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum Status {
/// Command succeeded
Success = 0,
/// Requested URL could not be reached by device.
Urlnotavailable = 1,
/// Requested URL returned 401 error code.
Authfailed = 2,
/// Requested Text Track (in PlaybackPreferences) not available
Texttracknotavailable = 3,
/// Requested Audio Track (in PlaybackPreferences) not available
Audiotracknotavailable = 4,
}
impl Status {
/// Convert from u8 value
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Status::Success),
1 => Some(Status::Urlnotavailable),
2 => Some(Status::Authfailed),
3 => Some(Status::Texttracknotavailable),
4 => Some(Status::Audiotracknotavailable),
_ => None,
}
}
/// Convert to u8 value
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<Status> for u8 {
fn from(val: Status) -> Self {
val as u8
}
}
// Bitmap definitions
/// SupportedProtocols bitmap type
pub type SupportedProtocols = u8;
/// Constants for SupportedProtocols
pub mod supportedprotocols {
/// Device supports Dynamic Adaptive Streaming over HTTP (DASH)
pub const DASH: u8 = 0x01;
/// Device supports HTTP Live Streaming (HLS)
pub const HLS: u8 = 0x02;
}
// Struct definitions
#[derive(Debug, serde::Serialize)]
pub struct AdditionalInfo {
pub name: Option<String>,
pub value: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct BrandingInformation {
pub provider_name: Option<String>,
pub background: Option<StyleInformation>,
pub logo: Option<StyleInformation>,
pub progress_bar: Option<StyleInformation>,
pub splash: Option<StyleInformation>,
pub water_mark: Option<StyleInformation>,
}
#[derive(Debug, serde::Serialize)]
pub struct ContentSearch {
pub parameter_list: Option<Vec<Parameter>>,
}
#[derive(Debug, serde::Serialize)]
pub struct Dimension {
pub width: Option<u8>,
pub height: Option<u8>,
pub metric: Option<MetricType>,
}
#[derive(Debug, serde::Serialize)]
pub struct Parameter {
pub type_: Option<ParameterEnum>,
pub value: Option<String>,
pub external_id_list: Option<Vec<AdditionalInfo>>,
}
#[derive(Debug, serde::Serialize)]
pub struct PlaybackPreferences {
pub playback_position: Option<u64>,
pub text_track: Option<TrackPreference>,
pub audio_tracks: Option<Vec<TrackPreference>>,
}
#[derive(Debug, serde::Serialize)]
pub struct StyleInformation {
pub image_url: Option<String>,
pub color: Option<String>,
pub size: Option<Dimension>,
}
#[derive(Debug, serde::Serialize)]
pub struct TrackPreference {
pub language_code: Option<String>,
pub characteristics: Option<Vec<u8>>,
pub audio_output_index: Option<u8>,
}
// Command encoders
/// Encode LaunchContent command (0x00)
pub fn encode_launch_content(search: ContentSearch, auto_play: bool, data: Option<String>, playback_preferences: Option<PlaybackPreferences>, use_current_context: Option<bool>) -> anyhow::Result<Vec<u8>> {
// Encode struct ContentSearchStruct
let mut search_fields = Vec::new();
if let Some(listv) = search.parameter_list {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.type_ { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
if let Some(x) = inner.value { nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(listv) = inner.external_id_list {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.value { nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
nested_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
search_fields.push((0, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::StructInvisible(search_fields)).into());
tlv_fields.push((1, tlv::TlvItemValueEnc::Bool(auto_play)).into());
if let Some(x) = data { tlv_fields.push((2, tlv::TlvItemValueEnc::String(x)).into()); }
if let Some(playback_preferences) = playback_preferences {
// Encode struct PlaybackPreferencesStruct
let mut playback_preferences_fields = Vec::new();
if let Some(x) = playback_preferences.playback_position { playback_preferences_fields.push((0, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(inner) = playback_preferences.text_track {
let mut text_track_nested_fields = Vec::new();
if let Some(x) = inner.language_code { text_track_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(listv) = inner.characteristics { text_track_nested_fields.push((1, tlv::TlvItemValueEnc::StructAnon(listv.into_iter().map(|x| (0, tlv::TlvItemValueEnc::UInt8(x)).into()).collect())).into()); }
if let Some(x) = inner.audio_output_index { text_track_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
playback_preferences_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(text_track_nested_fields)).into());
}
if let Some(listv) = playback_preferences.audio_tracks {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.language_code { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(listv) = inner.characteristics { nested_fields.push((1, tlv::TlvItemValueEnc::StructAnon(listv.into_iter().map(|x| (0, tlv::TlvItemValueEnc::UInt8(x)).into()).collect())).into()); }
if let Some(x) = inner.audio_output_index { nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
playback_preferences_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
tlv_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(playback_preferences_fields)).into());
}
if let Some(x) = use_current_context { tlv_fields.push((4, tlv::TlvItemValueEnc::Bool(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
/// Encode LaunchURL command (0x01)
pub fn encode_launch_url(content_url: String, display_string: Option<String>, branding_information: Option<BrandingInformation>, playback_preferences: Option<PlaybackPreferences>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::String(content_url)).into());
if let Some(x) = display_string { tlv_fields.push((1, tlv::TlvItemValueEnc::String(x)).into()); }
if let Some(branding_information) = branding_information {
// Encode struct BrandingInformationStruct
let mut branding_information_fields = Vec::new();
if let Some(x) = branding_information.provider_name { branding_information_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = branding_information.background {
let mut background_nested_fields = Vec::new();
if let Some(x) = inner.image_url { background_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.color { background_nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = inner.size {
let mut size_nested_fields = Vec::new();
// TODO: encoding for field width (double) not implemented
// TODO: encoding for field height (double) not implemented
if let Some(x) = inner.metric { size_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
background_nested_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(size_nested_fields)).into());
}
branding_information_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(background_nested_fields)).into());
}
if let Some(inner) = branding_information.logo {
let mut logo_nested_fields = Vec::new();
if let Some(x) = inner.image_url { logo_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.color { logo_nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = inner.size {
let mut size_nested_fields = Vec::new();
// TODO: encoding for field width (double) not implemented
// TODO: encoding for field height (double) not implemented
if let Some(x) = inner.metric { size_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
logo_nested_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(size_nested_fields)).into());
}
branding_information_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(logo_nested_fields)).into());
}
if let Some(inner) = branding_information.progress_bar {
let mut progress_bar_nested_fields = Vec::new();
if let Some(x) = inner.image_url { progress_bar_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.color { progress_bar_nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = inner.size {
let mut size_nested_fields = Vec::new();
// TODO: encoding for field width (double) not implemented
// TODO: encoding for field height (double) not implemented
if let Some(x) = inner.metric { size_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
progress_bar_nested_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(size_nested_fields)).into());
}
branding_information_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(progress_bar_nested_fields)).into());
}
if let Some(inner) = branding_information.splash {
let mut splash_nested_fields = Vec::new();
if let Some(x) = inner.image_url { splash_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.color { splash_nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = inner.size {
let mut size_nested_fields = Vec::new();
// TODO: encoding for field width (double) not implemented
// TODO: encoding for field height (double) not implemented
if let Some(x) = inner.metric { size_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
splash_nested_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(size_nested_fields)).into());
}
branding_information_fields.push((4, tlv::TlvItemValueEnc::StructInvisible(splash_nested_fields)).into());
}
if let Some(inner) = branding_information.water_mark {
let mut water_mark_nested_fields = Vec::new();
if let Some(x) = inner.image_url { water_mark_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(x) = inner.color { water_mark_nested_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(inner) = inner.size {
let mut size_nested_fields = Vec::new();
// TODO: encoding for field width (double) not implemented
// TODO: encoding for field height (double) not implemented
if let Some(x) = inner.metric { size_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
water_mark_nested_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(size_nested_fields)).into());
}
branding_information_fields.push((5, tlv::TlvItemValueEnc::StructInvisible(water_mark_nested_fields)).into());
}
tlv_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(branding_information_fields)).into());
}
if let Some(playback_preferences) = playback_preferences {
// Encode struct PlaybackPreferencesStruct
let mut playback_preferences_fields = Vec::new();
if let Some(x) = playback_preferences.playback_position { playback_preferences_fields.push((0, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(inner) = playback_preferences.text_track {
let mut text_track_nested_fields = Vec::new();
if let Some(x) = inner.language_code { text_track_nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(listv) = inner.characteristics { text_track_nested_fields.push((1, tlv::TlvItemValueEnc::StructAnon(listv.into_iter().map(|x| (0, tlv::TlvItemValueEnc::UInt8(x)).into()).collect())).into()); }
if let Some(x) = inner.audio_output_index { text_track_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
playback_preferences_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(text_track_nested_fields)).into());
}
if let Some(listv) = playback_preferences.audio_tracks {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.language_code { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
if let Some(listv) = inner.characteristics { nested_fields.push((1, tlv::TlvItemValueEnc::StructAnon(listv.into_iter().map(|x| (0, tlv::TlvItemValueEnc::UInt8(x)).into()).collect())).into()); }
if let Some(x) = inner.audio_output_index { nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
playback_preferences_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
tlv_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(playback_preferences_fields)).into());
}
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
// Attribute decoders
/// Decode AcceptHeader attribute (0x0000)
pub fn decode_accept_header(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<String>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
if let tlv::TlvItemValue::String(s) = &item.value {
res.push(s.clone());
}
}
}
Ok(res)
}
/// Decode SupportedStreamingProtocols attribute (0x0001)
pub fn decode_supported_streaming_protocols(inp: &tlv::TlvItemValue) -> anyhow::Result<SupportedProtocols> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
// JSON dispatcher function
/// Decode attribute value and return as JSON string
///
/// # Parameters
/// * `cluster_id` - The cluster identifier
/// * `attribute_id` - The attribute identifier
/// * `tlv_value` - The TLV value to decode
///
/// # Returns
/// JSON string representation of the decoded value or error
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
// Verify this is the correct cluster
if cluster_id != 0x050A {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x050A, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_accept_header(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_supported_streaming_protocols(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),
}
}
/// Get list of all attributes supported by this cluster
///
/// # Returns
/// Vector of tuples containing (attribute_id, attribute_name)
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "AcceptHeader"),
(0x0001, "SupportedStreamingProtocols"),
]
}
// Command listing
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "LaunchContent"),
(0x01, "LaunchURL"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("LaunchContent"),
0x01 => Some("LaunchURL"),
_ => 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: "search", kind: crate::clusters::codec::FieldKind::Struct { name: "ContentSearchStruct" }, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "auto_play", kind: crate::clusters::codec::FieldKind::Bool, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "data", kind: crate::clusters::codec::FieldKind::String, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 3, name: "playback_preferences", kind: crate::clusters::codec::FieldKind::Struct { name: "PlaybackPreferencesStruct" }, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 4, name: "use_current_context", kind: crate::clusters::codec::FieldKind::Bool, optional: true, nullable: false },
]),
0x01 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "content_url", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "display_string", kind: crate::clusters::codec::FieldKind::String, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "branding_information", kind: crate::clusters::codec::FieldKind::Struct { name: "BrandingInformationStruct" }, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 3, name: "playback_preferences", kind: crate::clusters::codec::FieldKind::Struct { name: "PlaybackPreferencesStruct" }, optional: true, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => Err(anyhow::anyhow!("command \"LaunchContent\" has complex args: use raw mode")),
0x01 => Err(anyhow::anyhow!("command \"LaunchURL\" has complex args: use raw mode")),
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct LauncherResponse {
pub status: Option<Status>,
pub data: Option<String>,
}
// Command response decoders
/// Decode LauncherResponse command response (02)
pub fn decode_launcher_response(inp: &tlv::TlvItemValue) -> anyhow::Result<LauncherResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(LauncherResponse {
status: item.get_int(&[0]).and_then(|v| Status::from_u8(v as u8)),
data: item.get_string_owned(&[1]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
// Typed facade (invokes + reads)
/// Invoke `LaunchContent` command on cluster `Content Launcher`.
pub async fn launch_content(conn: &crate::controller::Connection, endpoint: u16, search: ContentSearch, auto_play: bool, data: Option<String>, playback_preferences: Option<PlaybackPreferences>, use_current_context: Option<bool>) -> anyhow::Result<LauncherResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_LAUNCHER, crate::clusters::defs::CLUSTER_CONTENT_LAUNCHER_CMD_ID_LAUNCHCONTENT, &encode_launch_content(search, auto_play, data, playback_preferences, use_current_context)?).await?;
decode_launcher_response(&tlv)
}
/// Invoke `LaunchURL` command on cluster `Content Launcher`.
pub async fn launch_url(conn: &crate::controller::Connection, endpoint: u16, content_url: String, display_string: Option<String>, branding_information: Option<BrandingInformation>, playback_preferences: Option<PlaybackPreferences>) -> anyhow::Result<LauncherResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_LAUNCHER, crate::clusters::defs::CLUSTER_CONTENT_LAUNCHER_CMD_ID_LAUNCHURL, &encode_launch_url(content_url, display_string, branding_information, playback_preferences)?).await?;
decode_launcher_response(&tlv)
}
/// Read `AcceptHeader` attribute from cluster `Content Launcher`.
pub async fn read_accept_header(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<String>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_LAUNCHER, crate::clusters::defs::CLUSTER_CONTENT_LAUNCHER_ATTR_ID_ACCEPTHEADER).await?;
decode_accept_header(&tlv)
}
/// Read `SupportedStreamingProtocols` attribute from cluster `Content Launcher`.
pub async fn read_supported_streaming_protocols(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SupportedProtocols> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CONTENT_LAUNCHER, crate::clusters::defs::CLUSTER_CONTENT_LAUNCHER_ATTR_ID_SUPPORTEDSTREAMINGPROTOCOLS).await?;
decode_supported_streaming_protocols(&tlv)
}