Skip to content

Commit c6b34f5

Browse files
committed
feat(zigbee): Add Time cluster support
1 parent 2f423af commit c6b34f5

File tree

3 files changed

+151
-4
lines changed

3 files changed

+151
-4
lines changed

libraries/Zigbee/src/ZigbeeEP.cpp

+130
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,134 @@ void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) {
240240
}
241241
}
242242

243+
void ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) {
244+
// default values for time cluster
245+
time_t utc_time = 0;
246+
time_t undefined_time = 0xffffffff;
247+
uint8_t time_status = 0;
248+
249+
// Check if time is set
250+
if (time.tm_year > 0) {
251+
// Convert time to UTC
252+
utc_time = mktime(&time);
253+
}
254+
255+
// Create time cluster server attributes
256+
esp_zb_attribute_list_t *time_cluster_server = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
257+
esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, (void *)&gmt_offset);
258+
esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, (void *)&utc_time);
259+
// Create time cluster client attributes
260+
esp_zb_attribute_list_t *time_cluster_client = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
261+
// Add time clusters to cluster list
262+
esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_server, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
263+
esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_client, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
264+
265+
}
266+
267+
void ZigbeeEP::setTime(tm time) {
268+
time_t utc_time = mktime(&time);
269+
esp_zb_lock_acquire(portMAX_DELAY);
270+
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, &utc_time, false);
271+
esp_zb_lock_release();
272+
}
273+
274+
void ZigbeeEP::setTimezone(int32_t gmt_offset) {
275+
esp_zb_lock_acquire(portMAX_DELAY);
276+
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, &gmt_offset, false);
277+
esp_zb_lock_release();
278+
}
279+
280+
tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
281+
/* Read peer time */
282+
esp_zb_zcl_read_attr_cmd_t read_req;
283+
284+
if (short_addr >= 0) {
285+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
286+
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
287+
} else {
288+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
289+
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
290+
}
291+
292+
uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID};
293+
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
294+
read_req.attr_field = attributes;
295+
296+
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
297+
298+
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
299+
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
300+
301+
// clear read time
302+
_read_time = 0;
303+
304+
log_v("Reading time from endpoint %d", endpoint);
305+
esp_zb_zcl_read_attr_cmd_req(&read_req);
306+
307+
//Wait for response or timeout
308+
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
309+
log_e("Error while reading time");
310+
return tm();
311+
}
312+
313+
struct tm *timeinfo = localtime(&_read_time);
314+
if (timeinfo) {
315+
return *timeinfo;
316+
} else {
317+
log_e("Error while converting time");
318+
return tm();
319+
}
320+
}
321+
322+
int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
323+
/* Read peer timezone */
324+
esp_zb_zcl_read_attr_cmd_t read_req;
325+
326+
if (short_addr >= 0) {
327+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
328+
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
329+
} else {
330+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
331+
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
332+
}
333+
334+
uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID};
335+
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
336+
read_req.attr_field = attributes;
337+
338+
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
339+
340+
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
341+
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
342+
343+
// clear read timezone
344+
_read_timezone = 0;
345+
346+
log_v("Reading timezone from endpoint %d", endpoint);
347+
esp_zb_zcl_read_attr_cmd_req(&read_req);
348+
349+
//Wait for response or timeout
350+
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
351+
log_e("Error while reading timezone");
352+
}
353+
354+
return _read_timezone;
355+
}
356+
357+
void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) {
358+
/* Time cluster attributes */
359+
if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME) {
360+
log_v("Time attribute received");
361+
log_v("Time: %lld", *(uint32_t *)attribute->data.value);
362+
_read_time = *(uint32_t *)attribute->data.value;
363+
xSemaphoreGive(lock);
364+
}
365+
else if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S32) {
366+
log_v("Timezone attribute received");
367+
log_v("Timezone: %d", *(int32_t *)attribute->data.value);
368+
_read_timezone = *(int32_t *)attribute->data.value;
369+
xSemaphoreGive(lock);
370+
}
371+
}
372+
243373
#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED

libraries/Zigbee/src/ZigbeeEP.h

+19-4
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,28 @@ class ZigbeeEP {
8282
_allow_multiple_binding = bind;
8383
}
8484

85-
// Manufacturer name and model implemented
85+
// Set Manufacturer name and model
8686
void setManufacturerAndModel(const char *name, const char *model);
87-
void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255);
88-
void setBatteryPercentage(uint8_t percentage);
89-
void reportBatteryPercentage();
9087

9188
// Methods to read manufacturer and model name from selected endpoint and short address
9289
char *readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
9390
char *readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
9491

92+
// Set Power source and battery percentage for battery powered devices
93+
void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255);
94+
void setBatteryPercentage(uint8_t percentage);
95+
void reportBatteryPercentage();
96+
97+
// Set time
98+
void addTimeCluster(tm time = {0}, int32_t gmt_offset = 0); // gmt offset in seconds
99+
void setTime(tm time);
100+
void setTimezone(int32_t gmt_offset);
101+
102+
// Get time from Coordinator or specific endpoint (blocking until response)
103+
struct tm getTime(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0});
104+
int32_t getTimezone(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0}); // gmt offset in seconds
105+
106+
95107
bool epAllowMultipleBinding() {
96108
return _allow_multiple_binding;
97109
}
@@ -104,6 +116,7 @@ class ZigbeeEP {
104116
virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {};
105117
virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
106118
virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message);
119+
virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
107120

108121
void onIdentify(void (*callback)(uint16_t)) {
109122
_on_identify = callback;
@@ -113,6 +126,8 @@ class ZigbeeEP {
113126
char *_read_manufacturer;
114127
char *_read_model;
115128
void (*_on_identify)(uint16_t time);
129+
time_t _read_time;
130+
int32_t _read_timezone;
116131

117132
protected:
118133
uint8_t _endpoint;

libraries/Zigbee/src/ZigbeeHandlers.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re
9595
if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) {
9696
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BASIC) {
9797
(*it)->zbReadBasicCluster(&variable->attribute); //method zbReadBasicCluster implemented in the common EP class
98+
} else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) {
99+
(*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class
98100
} else {
99101
(*it)->zbAttributeRead(message->info.cluster, &variable->attribute); //method zbAttributeRead must be implemented in specific EP class
100102
}

0 commit comments

Comments
 (0)