1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum Granularity {
18 Notimegranularity = 0,
20 Minutesgranularity = 1,
22 Secondsgranularity = 2,
24 Millisecondsgranularity = 3,
26 Microsecondsgranularity = 4,
28}
29
30impl Granularity {
31 pub fn from_u8(value: u8) -> Option<Self> {
33 match value {
34 0 => Some(Granularity::Notimegranularity),
35 1 => Some(Granularity::Minutesgranularity),
36 2 => Some(Granularity::Secondsgranularity),
37 3 => Some(Granularity::Millisecondsgranularity),
38 4 => Some(Granularity::Microsecondsgranularity),
39 _ => None,
40 }
41 }
42
43 pub fn to_u8(self) -> u8 {
45 self as u8
46 }
47}
48
49impl From<Granularity> for u8 {
50 fn from(val: Granularity) -> Self {
51 val as u8
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
56#[repr(u8)]
57pub enum StatusCode {
58 Timenotaccepted = 2,
60}
61
62impl StatusCode {
63 pub fn from_u8(value: u8) -> Option<Self> {
65 match value {
66 2 => Some(StatusCode::Timenotaccepted),
67 _ => None,
68 }
69 }
70
71 pub fn to_u8(self) -> u8 {
73 self as u8
74 }
75}
76
77impl From<StatusCode> for u8 {
78 fn from(val: StatusCode) -> Self {
79 val as u8
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[repr(u8)]
85pub enum TimeSource {
86 None = 0,
88 Unknown = 1,
90 Admin = 2,
92 Nodetimecluster = 3,
94 Nonmattersntp = 4,
96 Nonmatterntp = 5,
98 Mattersntp = 6,
100 Matterntp = 7,
102 Mixedntp = 8,
104 Nonmattersntpnts = 9,
106 Nonmatterntpnts = 10,
108 Mattersntpnts = 11,
110 Matterntpnts = 12,
112 Mixedntpnts = 13,
114 Cloudsource = 14,
116 Ptp = 15,
118 Gnss = 16,
120}
121
122impl TimeSource {
123 pub fn from_u8(value: u8) -> Option<Self> {
125 match value {
126 0 => Some(TimeSource::None),
127 1 => Some(TimeSource::Unknown),
128 2 => Some(TimeSource::Admin),
129 3 => Some(TimeSource::Nodetimecluster),
130 4 => Some(TimeSource::Nonmattersntp),
131 5 => Some(TimeSource::Nonmatterntp),
132 6 => Some(TimeSource::Mattersntp),
133 7 => Some(TimeSource::Matterntp),
134 8 => Some(TimeSource::Mixedntp),
135 9 => Some(TimeSource::Nonmattersntpnts),
136 10 => Some(TimeSource::Nonmatterntpnts),
137 11 => Some(TimeSource::Mattersntpnts),
138 12 => Some(TimeSource::Matterntpnts),
139 13 => Some(TimeSource::Mixedntpnts),
140 14 => Some(TimeSource::Cloudsource),
141 15 => Some(TimeSource::Ptp),
142 16 => Some(TimeSource::Gnss),
143 _ => None,
144 }
145 }
146
147 pub fn to_u8(self) -> u8 {
149 self as u8
150 }
151}
152
153impl From<TimeSource> for u8 {
154 fn from(val: TimeSource) -> Self {
155 val as u8
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
160#[repr(u8)]
161pub enum TimeZoneDatabase {
162 Full = 0,
164 Partial = 1,
166 None = 2,
168}
169
170impl TimeZoneDatabase {
171 pub fn from_u8(value: u8) -> Option<Self> {
173 match value {
174 0 => Some(TimeZoneDatabase::Full),
175 1 => Some(TimeZoneDatabase::Partial),
176 2 => Some(TimeZoneDatabase::None),
177 _ => None,
178 }
179 }
180
181 pub fn to_u8(self) -> u8 {
183 self as u8
184 }
185}
186
187impl From<TimeZoneDatabase> for u8 {
188 fn from(val: TimeZoneDatabase) -> Self {
189 val as u8
190 }
191}
192
193#[derive(Debug, serde::Serialize)]
196pub struct DSTOffset {
197 pub offset: Option<i32>,
198 pub valid_starting: Option<u64>,
199 pub valid_until: Option<u64>,
200}
201
202#[derive(Debug, serde::Serialize)]
203pub struct FabricScopedTrustedTimeSource {
204 pub node_id: Option<u64>,
205 pub endpoint: Option<u16>,
206}
207
208#[derive(Debug, serde::Serialize)]
209pub struct TimeZone {
210 pub offset: Option<i32>,
211 pub valid_at: Option<u64>,
212 pub name: Option<String>,
213}
214
215#[derive(Debug, serde::Serialize)]
216pub struct TrustedTimeSource {
217 pub fabric_index: Option<u8>,
218 pub node_id: Option<u64>,
219 pub endpoint: Option<u16>,
220}
221
222pub fn encode_set_utc_time(utc_time: u64, granularity: Granularity, time_source: TimeSource) -> anyhow::Result<Vec<u8>> {
226 let tlv = tlv::TlvItemEnc {
227 tag: 0,
228 value: tlv::TlvItemValueEnc::StructInvisible(vec![
229 (0, tlv::TlvItemValueEnc::UInt64(utc_time)).into(),
230 (1, tlv::TlvItemValueEnc::UInt8(granularity.to_u8())).into(),
231 (2, tlv::TlvItemValueEnc::UInt8(time_source.to_u8())).into(),
232 ]),
233 };
234 Ok(tlv.encode()?)
235}
236
237pub fn encode_set_trusted_time_source(trusted_time_source: Option<FabricScopedTrustedTimeSource>) -> anyhow::Result<Vec<u8>> {
239 let trusted_time_source_enc = if let Some(s) = trusted_time_source {
241 let mut fields = Vec::new();
242 if let Some(x) = s.node_id { fields.push((0, tlv::TlvItemValueEnc::UInt64(x)).into()); }
243 if let Some(x) = s.endpoint { fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
244 tlv::TlvItemValueEnc::StructInvisible(fields)
245 } else {
246 tlv::TlvItemValueEnc::StructInvisible(Vec::new())
247 };
248 let tlv = tlv::TlvItemEnc {
249 tag: 0,
250 value: tlv::TlvItemValueEnc::StructInvisible(vec![
251 (0, trusted_time_source_enc).into(),
252 ]),
253 };
254 Ok(tlv.encode()?)
255}
256
257pub fn encode_set_time_zone(time_zone: Vec<TimeZone>) -> anyhow::Result<Vec<u8>> {
259 let tlv = tlv::TlvItemEnc {
260 tag: 0,
261 value: tlv::TlvItemValueEnc::StructInvisible(vec![
262 (0, tlv::TlvItemValueEnc::Array(time_zone.into_iter().map(|v| {
263 let mut fields = Vec::new();
264 if let Some(x) = v.offset { fields.push((0, tlv::TlvItemValueEnc::Int32(x)).into()); }
265 if let Some(x) = v.valid_at { fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
266 if let Some(x) = v.name { fields.push((2, tlv::TlvItemValueEnc::String(x.clone())).into()); }
267 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
268 }).collect())).into(),
269 ]),
270 };
271 Ok(tlv.encode()?)
272}
273
274pub fn encode_set_dst_offset(dst_offset: Vec<DSTOffset>) -> anyhow::Result<Vec<u8>> {
276 let tlv = tlv::TlvItemEnc {
277 tag: 0,
278 value: tlv::TlvItemValueEnc::StructInvisible(vec![
279 (0, tlv::TlvItemValueEnc::Array(dst_offset.into_iter().map(|v| {
280 let mut fields = Vec::new();
281 if let Some(x) = v.offset { fields.push((0, tlv::TlvItemValueEnc::Int32(x)).into()); }
282 if let Some(x) = v.valid_starting { fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
283 if let Some(x) = v.valid_until { fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
284 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
285 }).collect())).into(),
286 ]),
287 };
288 Ok(tlv.encode()?)
289}
290
291pub fn encode_set_default_ntp(default_ntp: Option<String>) -> anyhow::Result<Vec<u8>> {
293 let tlv = tlv::TlvItemEnc {
294 tag: 0,
295 value: tlv::TlvItemValueEnc::StructInvisible(vec![
296 (0, tlv::TlvItemValueEnc::String(default_ntp.unwrap_or("".to_string()))).into(),
297 ]),
298 };
299 Ok(tlv.encode()?)
300}
301
302pub fn decode_utc_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
306 if let tlv::TlvItemValue::Int(v) = inp {
307 Ok(Some(*v))
308 } else {
309 Ok(None)
310 }
311}
312
313pub fn decode_granularity(inp: &tlv::TlvItemValue) -> anyhow::Result<Granularity> {
315 if let tlv::TlvItemValue::Int(v) = inp {
316 Granularity::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
317 } else {
318 Err(anyhow::anyhow!("Expected Integer"))
319 }
320}
321
322pub fn decode_time_source(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeSource> {
324 if let tlv::TlvItemValue::Int(v) = inp {
325 TimeSource::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
326 } else {
327 Err(anyhow::anyhow!("Expected Integer"))
328 }
329}
330
331pub fn decode_trusted_time_source(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TrustedTimeSource>> {
333 if let tlv::TlvItemValue::List(_fields) = inp {
334 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
336 Ok(Some(TrustedTimeSource {
337 fabric_index: item.get_int(&[0]).map(|v| v as u8),
338 node_id: item.get_int(&[1]),
339 endpoint: item.get_int(&[2]).map(|v| v as u16),
340 }))
341 } else {
345 Ok(None)
346 }
348}
349
350pub fn decode_default_ntp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<String>> {
352 if let tlv::TlvItemValue::String(v) = inp {
353 Ok(Some(v.clone()))
354 } else {
355 Ok(None)
356 }
357}
358
359pub fn decode_time_zone(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TimeZone>> {
361 let mut res = Vec::new();
362 if let tlv::TlvItemValue::List(v) = inp {
363 for item in v {
364 res.push(TimeZone {
365 offset: item.get_int(&[0]).map(|v| v as i32),
366 valid_at: item.get_int(&[1]),
367 name: item.get_string_owned(&[2]),
368 });
369 }
370 }
371 Ok(res)
372}
373
374pub fn decode_dst_offset(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DSTOffset>> {
376 let mut res = Vec::new();
377 if let tlv::TlvItemValue::List(v) = inp {
378 for item in v {
379 res.push(DSTOffset {
380 offset: item.get_int(&[0]).map(|v| v as i32),
381 valid_starting: item.get_int(&[1]),
382 valid_until: item.get_int(&[2]),
383 });
384 }
385 }
386 Ok(res)
387}
388
389pub fn decode_local_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
391 if let tlv::TlvItemValue::Int(v) = inp {
392 Ok(Some(*v))
393 } else {
394 Ok(None)
395 }
396}
397
398pub fn decode_time_zone_database(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeZoneDatabase> {
400 if let tlv::TlvItemValue::Int(v) = inp {
401 TimeZoneDatabase::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
402 } else {
403 Err(anyhow::anyhow!("Expected Integer"))
404 }
405}
406
407pub fn decode_ntp_server_available(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
409 if let tlv::TlvItemValue::Bool(v) = inp {
410 Ok(*v)
411 } else {
412 Err(anyhow::anyhow!("Expected Bool"))
413 }
414}
415
416pub fn decode_time_zone_list_max_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
418 if let tlv::TlvItemValue::Int(v) = inp {
419 Ok(*v as u8)
420 } else {
421 Err(anyhow::anyhow!("Expected UInt8"))
422 }
423}
424
425pub fn decode_dst_offset_list_max_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
427 if let tlv::TlvItemValue::Int(v) = inp {
428 Ok(*v as u8)
429 } else {
430 Err(anyhow::anyhow!("Expected UInt8"))
431 }
432}
433
434pub fn decode_supports_dns_resolve(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
436 if let tlv::TlvItemValue::Bool(v) = inp {
437 Ok(*v)
438 } else {
439 Err(anyhow::anyhow!("Expected Bool"))
440 }
441}
442
443
444pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
456 if cluster_id != 0x0038 {
458 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0038, got {}\"}}", cluster_id);
459 }
460
461 match attribute_id {
462 0x0000 => {
463 match decode_utc_time(tlv_value) {
464 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
465 Err(e) => format!("{{\"error\": \"{}\"}}", e),
466 }
467 }
468 0x0001 => {
469 match decode_granularity(tlv_value) {
470 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
471 Err(e) => format!("{{\"error\": \"{}\"}}", e),
472 }
473 }
474 0x0002 => {
475 match decode_time_source(tlv_value) {
476 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
477 Err(e) => format!("{{\"error\": \"{}\"}}", e),
478 }
479 }
480 0x0003 => {
481 match decode_trusted_time_source(tlv_value) {
482 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
483 Err(e) => format!("{{\"error\": \"{}\"}}", e),
484 }
485 }
486 0x0004 => {
487 match decode_default_ntp(tlv_value) {
488 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
489 Err(e) => format!("{{\"error\": \"{}\"}}", e),
490 }
491 }
492 0x0005 => {
493 match decode_time_zone(tlv_value) {
494 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
495 Err(e) => format!("{{\"error\": \"{}\"}}", e),
496 }
497 }
498 0x0006 => {
499 match decode_dst_offset(tlv_value) {
500 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
501 Err(e) => format!("{{\"error\": \"{}\"}}", e),
502 }
503 }
504 0x0007 => {
505 match decode_local_time(tlv_value) {
506 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
507 Err(e) => format!("{{\"error\": \"{}\"}}", e),
508 }
509 }
510 0x0008 => {
511 match decode_time_zone_database(tlv_value) {
512 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
513 Err(e) => format!("{{\"error\": \"{}\"}}", e),
514 }
515 }
516 0x0009 => {
517 match decode_ntp_server_available(tlv_value) {
518 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
519 Err(e) => format!("{{\"error\": \"{}\"}}", e),
520 }
521 }
522 0x000A => {
523 match decode_time_zone_list_max_size(tlv_value) {
524 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
525 Err(e) => format!("{{\"error\": \"{}\"}}", e),
526 }
527 }
528 0x000B => {
529 match decode_dst_offset_list_max_size(tlv_value) {
530 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
531 Err(e) => format!("{{\"error\": \"{}\"}}", e),
532 }
533 }
534 0x000C => {
535 match decode_supports_dns_resolve(tlv_value) {
536 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
537 Err(e) => format!("{{\"error\": \"{}\"}}", e),
538 }
539 }
540 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
541 }
542}
543
544pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
549 vec![
550 (0x0000, "UTCTime"),
551 (0x0001, "Granularity"),
552 (0x0002, "TimeSource"),
553 (0x0003, "TrustedTimeSource"),
554 (0x0004, "DefaultNTP"),
555 (0x0005, "TimeZone"),
556 (0x0006, "DSTOffset"),
557 (0x0007, "LocalTime"),
558 (0x0008, "TimeZoneDatabase"),
559 (0x0009, "NTPServerAvailable"),
560 (0x000A, "TimeZoneListMaxSize"),
561 (0x000B, "DSTOffsetListMaxSize"),
562 (0x000C, "SupportsDNSResolve"),
563 ]
564}
565
566pub fn get_command_list() -> Vec<(u32, &'static str)> {
569 vec![
570 (0x00, "SetUTCTime"),
571 (0x01, "SetTrustedTimeSource"),
572 (0x02, "SetTimeZone"),
573 (0x04, "SetDSTOffset"),
574 (0x05, "SetDefaultNTP"),
575 ]
576}
577
578pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
579 match cmd_id {
580 0x00 => Some("SetUTCTime"),
581 0x01 => Some("SetTrustedTimeSource"),
582 0x02 => Some("SetTimeZone"),
583 0x04 => Some("SetDSTOffset"),
584 0x05 => Some("SetDefaultNTP"),
585 _ => None,
586 }
587}
588
589pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
590 match cmd_id {
591 0x00 => Some(vec![
592 crate::clusters::codec::CommandField { tag: 0, name: "utc_time", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
593 crate::clusters::codec::CommandField { tag: 1, name: "granularity", kind: crate::clusters::codec::FieldKind::Enum { name: "Granularity", variants: &[(0, "Notimegranularity"), (1, "Minutesgranularity"), (2, "Secondsgranularity"), (3, "Millisecondsgranularity"), (4, "Microsecondsgranularity")] }, optional: false, nullable: false },
594 crate::clusters::codec::CommandField { tag: 2, name: "time_source", kind: crate::clusters::codec::FieldKind::Enum { name: "TimeSource", variants: &[(0, "None"), (1, "Unknown"), (2, "Admin"), (3, "Nodetimecluster"), (4, "Nonmattersntp"), (5, "Nonmatterntp"), (6, "Mattersntp"), (7, "Matterntp"), (8, "Mixedntp"), (9, "Nonmattersntpnts"), (10, "Nonmatterntpnts"), (11, "Mattersntpnts"), (12, "Matterntpnts"), (13, "Mixedntpnts"), (14, "Cloudsource"), (15, "Ptp"), (16, "Gnss")] }, optional: true, nullable: false },
595 ]),
596 0x01 => Some(vec![
597 crate::clusters::codec::CommandField { tag: 0, name: "trusted_time_source", kind: crate::clusters::codec::FieldKind::Struct { name: "FabricScopedTrustedTimeSourceStruct" }, optional: false, nullable: true },
598 ]),
599 0x02 => Some(vec![
600 crate::clusters::codec::CommandField { tag: 0, name: "time_zone", kind: crate::clusters::codec::FieldKind::List { entry_type: "TimeZoneStruct" }, optional: false, nullable: false },
601 ]),
602 0x04 => Some(vec![
603 crate::clusters::codec::CommandField { tag: 0, name: "dst_offset", kind: crate::clusters::codec::FieldKind::List { entry_type: "DSTOffsetStruct" }, optional: false, nullable: false },
604 ]),
605 0x05 => Some(vec![
606 crate::clusters::codec::CommandField { tag: 0, name: "default_ntp", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: true },
607 ]),
608 _ => None,
609 }
610}
611
612pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
613 match cmd_id {
614 0x00 => {
615 let utc_time = crate::clusters::codec::json_util::get_u64(args, "utc_time")?;
616 let granularity = {
617 let n = crate::clusters::codec::json_util::get_u64(args, "granularity")?;
618 Granularity::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid Granularity: {}", n))?
619 };
620 let time_source = {
621 let n = crate::clusters::codec::json_util::get_u64(args, "time_source")?;
622 TimeSource::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid TimeSource: {}", n))?
623 };
624 encode_set_utc_time(utc_time, granularity, time_source)
625 }
626 0x01 => Err(anyhow::anyhow!("command \"SetTrustedTimeSource\" has complex args: use raw mode")),
627 0x02 => Err(anyhow::anyhow!("command \"SetTimeZone\" has complex args: use raw mode")),
628 0x04 => Err(anyhow::anyhow!("command \"SetDSTOffset\" has complex args: use raw mode")),
629 0x05 => {
630 let default_ntp = crate::clusters::codec::json_util::get_opt_string(args, "default_ntp")?;
631 encode_set_default_ntp(default_ntp)
632 }
633 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
634 }
635}
636
637#[derive(Debug, serde::Serialize)]
638pub struct SetTimeZoneResponse {
639 pub dst_offset_required: Option<bool>,
640}
641
642pub fn decode_set_time_zone_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetTimeZoneResponse> {
646 if let tlv::TlvItemValue::List(_fields) = inp {
647 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
648 Ok(SetTimeZoneResponse {
649 dst_offset_required: item.get_bool(&[0]),
650 })
651 } else {
652 Err(anyhow::anyhow!("Expected struct fields"))
653 }
654}
655
656pub async fn set_utc_time(conn: &crate::controller::Connection, endpoint: u16, utc_time: u64, granularity: Granularity, time_source: TimeSource) -> anyhow::Result<()> {
660 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_CMD_ID_SETUTCTIME, &encode_set_utc_time(utc_time, granularity, time_source)?).await?;
661 Ok(())
662}
663
664pub async fn set_trusted_time_source(conn: &crate::controller::Connection, endpoint: u16, trusted_time_source: Option<FabricScopedTrustedTimeSource>) -> anyhow::Result<()> {
666 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_CMD_ID_SETTRUSTEDTIMESOURCE, &encode_set_trusted_time_source(trusted_time_source)?).await?;
667 Ok(())
668}
669
670pub async fn set_time_zone(conn: &crate::controller::Connection, endpoint: u16, time_zone: Vec<TimeZone>) -> anyhow::Result<SetTimeZoneResponse> {
672 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_CMD_ID_SETTIMEZONE, &encode_set_time_zone(time_zone)?).await?;
673 decode_set_time_zone_response(&tlv)
674}
675
676pub async fn set_dst_offset(conn: &crate::controller::Connection, endpoint: u16, dst_offset: Vec<DSTOffset>) -> anyhow::Result<()> {
678 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_CMD_ID_SETDSTOFFSET, &encode_set_dst_offset(dst_offset)?).await?;
679 Ok(())
680}
681
682pub async fn set_default_ntp(conn: &crate::controller::Connection, endpoint: u16, default_ntp: Option<String>) -> anyhow::Result<()> {
684 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_CMD_ID_SETDEFAULTNTP, &encode_set_default_ntp(default_ntp)?).await?;
685 Ok(())
686}
687
688pub async fn read_utc_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
690 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_UTCTIME).await?;
691 decode_utc_time(&tlv)
692}
693
694pub async fn read_granularity(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Granularity> {
696 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_GRANULARITY).await?;
697 decode_granularity(&tlv)
698}
699
700pub async fn read_time_source(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TimeSource> {
702 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_TIMESOURCE).await?;
703 decode_time_source(&tlv)
704}
705
706pub async fn read_trusted_time_source(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<TrustedTimeSource>> {
708 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_TRUSTEDTIMESOURCE).await?;
709 decode_trusted_time_source(&tlv)
710}
711
712pub async fn read_default_ntp(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<String>> {
714 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_DEFAULTNTP).await?;
715 decode_default_ntp(&tlv)
716}
717
718pub async fn read_time_zone(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TimeZone>> {
720 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_TIMEZONE).await?;
721 decode_time_zone(&tlv)
722}
723
724pub async fn read_dst_offset(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DSTOffset>> {
726 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_DSTOFFSET).await?;
727 decode_dst_offset(&tlv)
728}
729
730pub async fn read_local_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
732 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_LOCALTIME).await?;
733 decode_local_time(&tlv)
734}
735
736pub async fn read_time_zone_database(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TimeZoneDatabase> {
738 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_TIMEZONEDATABASE).await?;
739 decode_time_zone_database(&tlv)
740}
741
742pub async fn read_ntp_server_available(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
744 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_NTPSERVERAVAILABLE).await?;
745 decode_ntp_server_available(&tlv)
746}
747
748pub async fn read_time_zone_list_max_size(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
750 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_TIMEZONELISTMAXSIZE).await?;
751 decode_time_zone_list_max_size(&tlv)
752}
753
754pub async fn read_dst_offset_list_max_size(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
756 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_DSTOFFSETLISTMAXSIZE).await?;
757 decode_dst_offset_list_max_size(&tlv)
758}
759
760pub async fn read_supports_dns_resolve(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
762 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TIME_SYNCHRONIZATION, crate::clusters::defs::CLUSTER_TIME_SYNCHRONIZATION_ATTR_ID_SUPPORTSDNSRESOLVE).await?;
763 decode_supports_dns_resolve(&tlv)
764}
765
766#[derive(Debug, serde::Serialize)]
767pub struct DSTStatusEvent {
768 pub dst_offset_active: Option<bool>,
769}
770
771#[derive(Debug, serde::Serialize)]
772pub struct TimeZoneStatusEvent {
773 pub offset: Option<i32>,
774 pub name: Option<String>,
775}
776
777pub fn decode_dst_status_event(inp: &tlv::TlvItemValue) -> anyhow::Result<DSTStatusEvent> {
781 if let tlv::TlvItemValue::List(_fields) = inp {
782 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
783 Ok(DSTStatusEvent {
784 dst_offset_active: item.get_bool(&[0]),
785 })
786 } else {
787 Err(anyhow::anyhow!("Expected struct fields"))
788 }
789}
790
791pub fn decode_time_zone_status_event(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeZoneStatusEvent> {
793 if let tlv::TlvItemValue::List(_fields) = inp {
794 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
795 Ok(TimeZoneStatusEvent {
796 offset: item.get_int(&[0]).map(|v| v as i32),
797 name: item.get_string_owned(&[1]),
798 })
799 } else {
800 Err(anyhow::anyhow!("Expected struct fields"))
801 }
802}
803