Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(zigbee): Add Time cluster support + fix of duplicate indentify cluster #10863

Merged
merged 7 commits into from
Jan 30, 2025
127 changes: 127 additions & 0 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,131 @@ void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) {
}
}

void ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) {
time_t utc_time = 0;

// Check if time is set
if (time.tm_year > 0) {
// Convert time to UTC
utc_time = mktime(&time);
}

// Create time cluster server attributes
esp_zb_attribute_list_t *time_cluster_server = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, (void *)&gmt_offset);
esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, (void *)&utc_time);
// Create time cluster client attributes
esp_zb_attribute_list_t *time_cluster_client = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
// Add time clusters to cluster list
esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_server, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_client, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);

}

void ZigbeeEP::setTime(tm time) {
time_t utc_time = mktime(&time);
esp_zb_lock_acquire(portMAX_DELAY);
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);
esp_zb_lock_release();
}

void ZigbeeEP::setTimezone(int32_t gmt_offset) {
esp_zb_lock_acquire(portMAX_DELAY);
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);
esp_zb_lock_release();
}

tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer time */
esp_zb_zcl_read_attr_cmd_t read_req;

if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}

uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;

read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;

read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;

// clear read time
_read_time = 0;

log_v("Reading time from endpoint %d", endpoint);
esp_zb_zcl_read_attr_cmd_req(&read_req);

//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading time");
return tm();
}

struct tm *timeinfo = localtime(&_read_time);
if (timeinfo) {
return *timeinfo;
} else {
log_e("Error while converting time");
return tm();
}
}

int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer timezone */
esp_zb_zcl_read_attr_cmd_t read_req;

if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}

uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;

read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;

read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;

// clear read timezone
_read_timezone = 0;

log_v("Reading timezone from endpoint %d", endpoint);
esp_zb_zcl_read_attr_cmd_req(&read_req);

//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading timezone");
}

return _read_timezone;
}

void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) {
/* Time cluster attributes */
if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME) {
log_v("Time attribute received");
log_v("Time: %lld", *(uint32_t *)attribute->data.value);
_read_time = *(uint32_t *)attribute->data.value;
xSemaphoreGive(lock);
}
else if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S32) {
log_v("Timezone attribute received");
log_v("Timezone: %d", *(int32_t *)attribute->data.value);
_read_timezone = *(int32_t *)attribute->data.value;
xSemaphoreGive(lock);
}
}

#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED
23 changes: 19 additions & 4 deletions libraries/Zigbee/src/ZigbeeEP.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,28 @@ class ZigbeeEP {
_allow_multiple_binding = bind;
}

// Manufacturer name and model implemented
// Set Manufacturer name and model
void setManufacturerAndModel(const char *name, const char *model);
void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255);
void setBatteryPercentage(uint8_t percentage);
void reportBatteryPercentage();

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

// Set Power source and battery percentage for battery powered devices
void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255);
void setBatteryPercentage(uint8_t percentage);
void reportBatteryPercentage();

// Set time
void addTimeCluster(tm time = {0}, int32_t gmt_offset = 0); // gmt offset in seconds
void setTime(tm time);
void setTimezone(int32_t gmt_offset);

// Get time from Coordinator or specific endpoint (blocking until response)
struct tm getTime(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0});
int32_t getTimezone(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0}); // gmt offset in seconds


bool epAllowMultipleBinding() {
return _allow_multiple_binding;
}
Expand All @@ -104,6 +116,7 @@ class ZigbeeEP {
virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {};
virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message);
virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented

void onIdentify(void (*callback)(uint16_t)) {
_on_identify = callback;
Expand All @@ -113,6 +126,8 @@ class ZigbeeEP {
char *_read_manufacturer;
char *_read_model;
void (*_on_identify)(uint16_t time);
time_t _read_time;
int32_t _read_timezone;

protected:
uint8_t _endpoint;
Expand Down
2 changes: 2 additions & 0 deletions libraries/Zigbee/src/ZigbeeHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re
if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BASIC) {
(*it)->zbReadBasicCluster(&variable->attribute); //method zbReadBasicCluster implemented in the common EP class
} else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) {
(*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class
} else {
(*it)->zbAttributeRead(message->info.cluster, &variable->attribute); //method zbAttributeRead must be implemented in specific EP class
}
Expand Down
1 change: 0 additions & 1 deletion libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ esp_zb_cluster_list_t *zigbee_carbon_dioxide_sensor_clusters_create(zigbee_carbo
esp_zb_cluster_list_add_carbon_dioxide_measurement_cluster(
cluster_list, esp_zb_carbon_dioxide_measurement_cluster_create(carbon_dioxide_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE
);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
return cluster_list;
}

Expand Down
1 change: 0 additions & 1 deletion libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_flow_sensor_clusters_create(zigbee_flow_sensor_cfg
esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_flow_meas_cluster(cluster_list, esp_zb_flow_meas_cluster_create(flow_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
return cluster_list;
}

Expand Down
1 change: 0 additions & 1 deletion libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_occupancy_sensor_clusters_create(zigbee_occupancy_
esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_occupancy_sensing_cluster(cluster_list, esp_zb_occupancy_sensing_cluster_create(occupancy_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
return cluster_list;
}

Expand Down
5 changes: 2 additions & 3 deletions libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_pressure_sensor_clusters_create(zigbee_pressure_se
esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_pressure_meas_cluster(cluster_list, esp_zb_pressure_meas_cluster_create(pressure_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
return cluster_list;
}

Expand All @@ -26,14 +25,14 @@ ZigbeePressureSensor::ZigbeePressureSensor(uint8_t endpoint) : ZigbeeEP(endpoint
void ZigbeePressureSensor::setMinMaxValue(int16_t min, int16_t max) {

esp_zb_attribute_list_t *pressure_measure_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_update_attr(pressure_measure_cluster, ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MIN_VALUE_ID, (void *)&min);
esp_zb_cluster_update_attr(pressure_measure_cluster, ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MAX_VALUE_ID, (void *)&max);
}

void ZigbeePressureSensor::setTolerance(uint16_t tolerance) {
esp_zb_attribute_list_t *pressure_measure_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_temperature_meas_cluster_add_attr(pressure_measure_cluster, ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_TOLERANCE_ID, (void *)&tolerance);
}

Expand Down
Loading