matc 0.1.2

Matter protocol library (controller side)
Documentation
//! Matter TLV encoders and decoders for On/Off Cluster
//! Cluster ID: 0x0006
//!
//! This file is automatically generated from OnOff.xml

use crate::tlv;
use anyhow;
use serde_json;


// Enum definitions

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DelayedAllOffEffectVariant {
    /// Fade to off in 0.8 seconds
    Delayedofffastfade = 0,
    /// No fade
    Nofade = 1,
    /// 50% dim down in 0.8 seconds then fade to off in 12 seconds
    Delayedoffslowfade = 2,
}

impl DelayedAllOffEffectVariant {
    /// Convert from u8 value
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            0 => Some(DelayedAllOffEffectVariant::Delayedofffastfade),
            1 => Some(DelayedAllOffEffectVariant::Nofade),
            2 => Some(DelayedAllOffEffectVariant::Delayedoffslowfade),
            _ => None,
        }
    }

    /// Convert to u8 value
    pub fn to_u8(self) -> u8 {
        self as u8
    }
}

impl From<DelayedAllOffEffectVariant> for u8 {
    fn from(val: DelayedAllOffEffectVariant) -> Self {
        val as u8
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DyingLightEffectVariant {
    /// 20% dim up in 0.5s then fade to off in 1 second
    Dyinglightfadeoff = 0,
}

impl DyingLightEffectVariant {
    /// Convert from u8 value
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            0 => Some(DyingLightEffectVariant::Dyinglightfadeoff),
            _ => None,
        }
    }

    /// Convert to u8 value
    pub fn to_u8(self) -> u8 {
        self as u8
    }
}

impl From<DyingLightEffectVariant> for u8 {
    fn from(val: DyingLightEffectVariant) -> Self {
        val as u8
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum EffectIdentifier {
    /// Delayed All Off
    Delayedalloff = 0,
    /// Dying Light
    Dyinglight = 1,
}

impl EffectIdentifier {
    /// Convert from u8 value
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            0 => Some(EffectIdentifier::Delayedalloff),
            1 => Some(EffectIdentifier::Dyinglight),
            _ => None,
        }
    }

    /// Convert to u8 value
    pub fn to_u8(self) -> u8 {
        self as u8
    }
}

impl From<EffectIdentifier> for u8 {
    fn from(val: EffectIdentifier) -> Self {
        val as u8
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum StartUpOnOff {
    /// Set the OnOff attribute to FALSE
    Off = 0,
    /// Set the OnOff attribute to TRUE
    On = 1,
    /// If the previous value of the OnOff attribute is equal to FALSE, set the OnOff attribute to TRUE. If the previous value of the OnOff attribute is equal to TRUE, set the OnOff attribute to FALSE (toggle).
    Toggle = 2,
}

impl StartUpOnOff {
    /// Convert from u8 value
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            0 => Some(StartUpOnOff::Off),
            1 => Some(StartUpOnOff::On),
            2 => Some(StartUpOnOff::Toggle),
            _ => None,
        }
    }

    /// Convert to u8 value
    pub fn to_u8(self) -> u8 {
        self as u8
    }
}

impl From<StartUpOnOff> for u8 {
    fn from(val: StartUpOnOff) -> Self {
        val as u8
    }
}

// Bitmap definitions

/// OnOffControl bitmap type
pub type OnOffControl = u8;

/// Constants for OnOffControl
pub mod onoffcontrol {
    /// Indicates a command is only accepted when in On state.
    pub const ACCEPT_ONLY_WHEN_ON: u8 = 0x01;
}

// Command encoders

/// Encode OffWithEffect command (0x40)
pub fn encode_off_with_effect(effect_identifier: EffectIdentifier, effect_variant: u8) -> anyhow::Result<Vec<u8>> {
    let tlv = tlv::TlvItemEnc {
        tag: 0,
        value: tlv::TlvItemValueEnc::StructInvisible(vec![
        (0, tlv::TlvItemValueEnc::UInt8(effect_identifier.to_u8())).into(),
        (1, tlv::TlvItemValueEnc::UInt8(effect_variant)).into(),
        ]),
    };
    Ok(tlv.encode()?)
}

/// Encode OnWithTimedOff command (0x42)
pub fn encode_on_with_timed_off(on_off_control: OnOffControl, on_time: u16, off_wait_time: u16) -> anyhow::Result<Vec<u8>> {
    let tlv = tlv::TlvItemEnc {
        tag: 0,
        value: tlv::TlvItemValueEnc::StructInvisible(vec![
        (0, tlv::TlvItemValueEnc::UInt8(on_off_control)).into(),
        (1, tlv::TlvItemValueEnc::UInt16(on_time)).into(),
        (2, tlv::TlvItemValueEnc::UInt16(off_wait_time)).into(),
        ]),
    };
    Ok(tlv.encode()?)
}

// Attribute decoders

/// Decode OnOff attribute (0x0000)
pub fn decode_on_off(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
    if let tlv::TlvItemValue::Bool(v) = inp {
        Ok(*v)
    } else {
        Err(anyhow::anyhow!("Expected Bool"))
    }
}

/// Decode GlobalSceneControl attribute (0x4000)
pub fn decode_global_scene_control(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
    if let tlv::TlvItemValue::Bool(v) = inp {
        Ok(*v)
    } else {
        Err(anyhow::anyhow!("Expected Bool"))
    }
}

/// Decode OnTime attribute (0x4001)
pub fn decode_on_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
    if let tlv::TlvItemValue::Int(v) = inp {
        Ok(*v as u16)
    } else {
        Err(anyhow::anyhow!("Expected UInt16"))
    }
}

/// Decode OffWaitTime attribute (0x4002)
pub fn decode_off_wait_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
    if let tlv::TlvItemValue::Int(v) = inp {
        Ok(*v as u16)
    } else {
        Err(anyhow::anyhow!("Expected UInt16"))
    }
}

/// Decode StartUpOnOff attribute (0x4003)
pub fn decode_start_up_on_off(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<StartUpOnOff>> {
    if let tlv::TlvItemValue::Int(v) = inp {
        Ok(StartUpOnOff::from_u8(*v as u8))
    } else {
        Ok(None)
    }
}


// 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 != 0x0006 {
        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0006, got {}\"}}", cluster_id);
    }

    match attribute_id {
        0x0000 => {
            match decode_on_off(tlv_value) {
                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
                Err(e) => format!("{{\"error\": \"{}\"}}", e),
            }
        }
        0x4000 => {
            match decode_global_scene_control(tlv_value) {
                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
                Err(e) => format!("{{\"error\": \"{}\"}}", e),
            }
        }
        0x4001 => {
            match decode_on_time(tlv_value) {
                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
                Err(e) => format!("{{\"error\": \"{}\"}}", e),
            }
        }
        0x4002 => {
            match decode_off_wait_time(tlv_value) {
                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
                Err(e) => format!("{{\"error\": \"{}\"}}", e),
            }
        }
        0x4003 => {
            match decode_start_up_on_off(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, "OnOff"),
        (0x4000, "GlobalSceneControl"),
        (0x4001, "OnTime"),
        (0x4002, "OffWaitTime"),
        (0x4003, "StartUpOnOff"),
    ]
}