matc/clusters/codec/
ota_requestor.rs

1//! Matter TLV encoders and decoders for OTA Software Update Requestor Cluster
2//! Cluster ID: 0x002A
3//!
4//! This file is automatically generated from OTARequestor.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Enum definitions
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum AnnouncementReason {
16    /// An OTA Provider is announcing its presence.
17    Simpleannouncement = 0,
18    /// An OTA Provider is announcing, either to a single Node or to a group of Nodes, that a new Software Image MAY be available.
19    Updateavailable = 1,
20    /// An OTA Provider is announcing, either to a single Node or to a group of Nodes, that a new Software Image MAY be available, which contains an update that needs to be applied urgently.
21    Urgentupdateavailable = 2,
22}
23
24impl AnnouncementReason {
25    /// Convert from u8 value
26    pub fn from_u8(value: u8) -> Option<Self> {
27        match value {
28            0 => Some(AnnouncementReason::Simpleannouncement),
29            1 => Some(AnnouncementReason::Updateavailable),
30            2 => Some(AnnouncementReason::Urgentupdateavailable),
31            _ => None,
32        }
33    }
34
35    /// Convert to u8 value
36    pub fn to_u8(self) -> u8 {
37        self as u8
38    }
39}
40
41impl From<AnnouncementReason> for u8 {
42    fn from(val: AnnouncementReason) -> Self {
43        val as u8
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48#[repr(u8)]
49pub enum ChangeReason {
50    /// The reason for a state change is unknown.
51    Unknown = 0,
52    /// The reason for a state change is the success of a prior operation.
53    Success = 1,
54    /// The reason for a state change is the failure of a prior operation.
55    Failure = 2,
56    /// The reason for a state change is a time-out.
57    Timeout = 3,
58    /// The reason for a state change is a request by the OTA Provider to wait.
59    Delaybyprovider = 4,
60}
61
62impl ChangeReason {
63    /// Convert from u8 value
64    pub fn from_u8(value: u8) -> Option<Self> {
65        match value {
66            0 => Some(ChangeReason::Unknown),
67            1 => Some(ChangeReason::Success),
68            2 => Some(ChangeReason::Failure),
69            3 => Some(ChangeReason::Timeout),
70            4 => Some(ChangeReason::Delaybyprovider),
71            _ => None,
72        }
73    }
74
75    /// Convert to u8 value
76    pub fn to_u8(self) -> u8 {
77        self as u8
78    }
79}
80
81impl From<ChangeReason> for u8 {
82    fn from(val: ChangeReason) -> Self {
83        val as u8
84    }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
88#[repr(u8)]
89pub enum UpdateState {
90    /// Current state is not yet determined.
91    Unknown = 0,
92    /// Indicate a Node not yet in the process of software update.
93    Idle = 1,
94    /// Indicate a Node in the process of querying an OTA Provider.
95    Querying = 2,
96    /// Indicate a Node waiting after a Busy response.
97    Delayedonquery = 3,
98    /// Indicate a Node currently in the process of downloading a software update.
99    Downloading = 4,
100    /// Indicate a Node currently in the process of verifying and applying a software update.
101    Applying = 5,
102    /// Indicate a Node waiting caused by AwaitNextAction response.
103    Delayedonapply = 6,
104    /// Indicate a Node in the process of recovering to a previous version.
105    Rollingback = 7,
106    /// Indicate a Node is capable of user consent.
107    Delayedonuserconsent = 8,
108}
109
110impl UpdateState {
111    /// Convert from u8 value
112    pub fn from_u8(value: u8) -> Option<Self> {
113        match value {
114            0 => Some(UpdateState::Unknown),
115            1 => Some(UpdateState::Idle),
116            2 => Some(UpdateState::Querying),
117            3 => Some(UpdateState::Delayedonquery),
118            4 => Some(UpdateState::Downloading),
119            5 => Some(UpdateState::Applying),
120            6 => Some(UpdateState::Delayedonapply),
121            7 => Some(UpdateState::Rollingback),
122            8 => Some(UpdateState::Delayedonuserconsent),
123            _ => None,
124        }
125    }
126
127    /// Convert to u8 value
128    pub fn to_u8(self) -> u8 {
129        self as u8
130    }
131}
132
133impl From<UpdateState> for u8 {
134    fn from(val: UpdateState) -> Self {
135        val as u8
136    }
137}
138
139// Struct definitions
140
141#[derive(Debug, serde::Serialize)]
142pub struct ProviderLocation {
143    pub provider_node_id: Option<u64>,
144    pub endpoint: Option<u16>,
145}
146
147// Command encoders
148
149/// Encode AnnounceOTAProvider command (0x00)
150pub fn encode_announce_ota_provider(provider_node_id: u64, vendor_id: u16, announcement_reason: AnnouncementReason, metadata_for_node: Vec<u8>, endpoint: u16) -> anyhow::Result<Vec<u8>> {
151    let tlv = tlv::TlvItemEnc {
152        tag: 0,
153        value: tlv::TlvItemValueEnc::StructInvisible(vec![
154        (0, tlv::TlvItemValueEnc::UInt64(provider_node_id)).into(),
155        (1, tlv::TlvItemValueEnc::UInt16(vendor_id)).into(),
156        (2, tlv::TlvItemValueEnc::UInt8(announcement_reason.to_u8())).into(),
157        (3, tlv::TlvItemValueEnc::OctetString(metadata_for_node)).into(),
158        (4, tlv::TlvItemValueEnc::UInt16(endpoint)).into(),
159        ]),
160    };
161    Ok(tlv.encode()?)
162}
163
164// Attribute decoders
165
166/// Decode DefaultOTAProviders attribute (0x0000)
167pub fn decode_default_ota_providers(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ProviderLocation>> {
168    let mut res = Vec::new();
169    if let tlv::TlvItemValue::List(v) = inp {
170        for item in v {
171            res.push(ProviderLocation {
172                provider_node_id: item.get_int(&[1]),
173                endpoint: item.get_int(&[2]).map(|v| v as u16),
174            });
175        }
176    }
177    Ok(res)
178}
179
180/// Decode UpdatePossible attribute (0x0001)
181pub fn decode_update_possible(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
182    if let tlv::TlvItemValue::Bool(v) = inp {
183        Ok(*v)
184    } else {
185        Err(anyhow::anyhow!("Expected Bool"))
186    }
187}
188
189/// Decode UpdateState attribute (0x0002)
190pub fn decode_update_state(inp: &tlv::TlvItemValue) -> anyhow::Result<UpdateState> {
191    if let tlv::TlvItemValue::Int(v) = inp {
192        UpdateState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
193    } else {
194        Err(anyhow::anyhow!("Expected Integer"))
195    }
196}
197
198/// Decode UpdateStateProgress attribute (0x0003)
199pub fn decode_update_state_progress(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
200    if let tlv::TlvItemValue::Int(v) = inp {
201        Ok(Some(*v as u8))
202    } else {
203        Ok(None)
204    }
205}
206
207
208// JSON dispatcher function
209
210/// Decode attribute value and return as JSON string
211///
212/// # Parameters
213/// * `cluster_id` - The cluster identifier
214/// * `attribute_id` - The attribute identifier
215/// * `tlv_value` - The TLV value to decode
216///
217/// # Returns
218/// JSON string representation of the decoded value or error
219pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
220    // Verify this is the correct cluster
221    if cluster_id != 0x002A {
222        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x002A, got {}\"}}", cluster_id);
223    }
224
225    match attribute_id {
226        0x0000 => {
227            match decode_default_ota_providers(tlv_value) {
228                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
229                Err(e) => format!("{{\"error\": \"{}\"}}", e),
230            }
231        }
232        0x0001 => {
233            match decode_update_possible(tlv_value) {
234                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
235                Err(e) => format!("{{\"error\": \"{}\"}}", e),
236            }
237        }
238        0x0002 => {
239            match decode_update_state(tlv_value) {
240                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
241                Err(e) => format!("{{\"error\": \"{}\"}}", e),
242            }
243        }
244        0x0003 => {
245            match decode_update_state_progress(tlv_value) {
246                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
247                Err(e) => format!("{{\"error\": \"{}\"}}", e),
248            }
249        }
250        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
251    }
252}
253
254/// Get list of all attributes supported by this cluster
255///
256/// # Returns
257/// Vector of tuples containing (attribute_id, attribute_name)
258pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
259    vec![
260        (0x0000, "DefaultOTAProviders"),
261        (0x0001, "UpdatePossible"),
262        (0x0002, "UpdateState"),
263        (0x0003, "UpdateStateProgress"),
264    ]
265}
266
267#[derive(Debug, serde::Serialize)]
268pub struct StateTransitionEvent {
269    pub previous_state: Option<UpdateState>,
270    pub new_state: Option<UpdateState>,
271    pub reason: Option<ChangeReason>,
272    pub target_software_version: Option<u32>,
273}
274
275#[derive(Debug, serde::Serialize)]
276pub struct VersionAppliedEvent {
277    pub software_version: Option<u32>,
278    pub product_id: Option<u16>,
279}
280
281#[derive(Debug, serde::Serialize)]
282pub struct DownloadErrorEvent {
283    pub software_version: Option<u32>,
284    pub bytes_downloaded: Option<u64>,
285    pub progress_percent: Option<u8>,
286    pub platform_code: Option<i64>,
287}
288
289// Event decoders
290
291/// Decode StateTransition event (0x00, priority: info)
292pub fn decode_state_transition_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StateTransitionEvent> {
293    if let tlv::TlvItemValue::List(_fields) = inp {
294        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
295        Ok(StateTransitionEvent {
296                                previous_state: item.get_int(&[0]).and_then(|v| UpdateState::from_u8(v as u8)),
297                                new_state: item.get_int(&[1]).and_then(|v| UpdateState::from_u8(v as u8)),
298                                reason: item.get_int(&[2]).and_then(|v| ChangeReason::from_u8(v as u8)),
299                                target_software_version: item.get_int(&[3]).map(|v| v as u32),
300        })
301    } else {
302        Err(anyhow::anyhow!("Expected struct fields"))
303    }
304}
305
306/// Decode VersionApplied event (0x01, priority: critical)
307pub fn decode_version_applied_event(inp: &tlv::TlvItemValue) -> anyhow::Result<VersionAppliedEvent> {
308    if let tlv::TlvItemValue::List(_fields) = inp {
309        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
310        Ok(VersionAppliedEvent {
311                                software_version: item.get_int(&[0]).map(|v| v as u32),
312                                product_id: item.get_int(&[1]).map(|v| v as u16),
313        })
314    } else {
315        Err(anyhow::anyhow!("Expected struct fields"))
316    }
317}
318
319/// Decode DownloadError event (0x02, priority: info)
320pub fn decode_download_error_event(inp: &tlv::TlvItemValue) -> anyhow::Result<DownloadErrorEvent> {
321    if let tlv::TlvItemValue::List(_fields) = inp {
322        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
323        Ok(DownloadErrorEvent {
324                                software_version: item.get_int(&[0]).map(|v| v as u32),
325                                bytes_downloaded: item.get_int(&[1]),
326                                progress_percent: item.get_int(&[2]).map(|v| v as u8),
327                                platform_code: item.get_int(&[3]).map(|v| v as i64),
328        })
329    } else {
330        Err(anyhow::anyhow!("Expected struct fields"))
331    }
332}
333