espressif / esp-zigbee-sdk

Espressif Zigbee SDK
Apache License 2.0
178 stars 31 forks source link

Window Covering Send settings command (TZ-1032) #396

Closed renatomotorline closed 2 months ago

renatomotorline commented 4 months ago

Question

I'm able to send OPEN and CLOSE from the coordinator using the esp_zb_zcl_window_covering_cluster_send_cmd_req function, which supports these commands:

typedef enum{
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_UP_OPEN               = 0x00,    /*!< Up/Open command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_DOWN_CLOSE            = 0x01,    /*!< Down/Close command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_STOP                  = 0x02,    /*!< Stop command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_VALUE      = 0x04,    /*!< Go to Lift Value command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE = 0x05,    /*!< Go to Lift Percentage command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_TILT_VALUE      = 0x07,    /*!< Go to Tilt Value command */
  ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_TILT_PERCENTAGE = 0x08     /*!< Go to Tilt Percentage command */
}esp_zb_zcl_window_covering_cmd_t;

But I also want to change the settings. How can I change for example the ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID?

Additional context.

No response

xieqinan commented 4 months ago

@renatomotorline ,

The esp_zb_window_covering_cluster_add_attr() can be used to add this attribute, and the esp_zb_cluster_update_attr() can be used to update the attribute before the esp_zb_device_register() is called.

The esp_zb_zcl_get_attribute() can be used to get the instance of the attribute after the esp_zb_device_register() is called. You can change the related value using this instance.

renatomotorline commented 4 months ago

@xieqinan I already have the attribute added: esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &current_lift_percentage); esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &installed_open_limit_lift); esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &installed_closed_limit_lift); esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &current_position_lift); esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &physical_closed_limit_lift); I'm using this example made by @ninoweg https://github.com/ninoweg/esp-zigbee-sdk/tree/main/examples/esp_zigbee_HA_sample/HA_window_covering

From what I understand the esp_zb_cluster_update_attr() only updates before the device registers, and is done from the device side. But what I want is to change the attribute from the coordinator side.

xieqinan commented 4 months ago

@renatomotorline ,

The ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID (0x0010) is defined as the READ ONLY access in the ZCL spec, means that it can not be changed by other devices using the general command.

renatomotorline commented 4 months ago

@xieqinan

So if I need to write the best way is to create a custom attribute right?

I'm trying to create a custom attribute

static void esp_zb_task(void *pvParameters) {
    /* Initialize Zigbee stack */
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
    esp_zb_init(&zb_nwk_cfg);

    /* Create customized window covering endpoint */
    esp_zb_ep_list_t *esp_zb_window_covering_ep = esp_zb_ep_list_create();
    esp_zb_endpoint_config_t endpoint_config = {
        .endpoint = HA_WINDOW_COVERING_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID,
        .app_device_version = 0,
    };

    esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();

    /* Create basic cluster */
    esp_zb_attribute_list_t *basic_attr_list = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);
    esp_zb_basic_cluster_add_attr(basic_attr_list, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, modelid);
    esp_zb_basic_cluster_add_attr(basic_attr_list, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, manufname);
    esp_zb_cluster_list_add_basic_cluster(cluster_list, basic_attr_list, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    /* Create window covering cluster */
    esp_zb_attribute_list_t *window_attr_list = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING);
    esp_zb_window_covering_cluster_cfg_t config = {
        .covering_type = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE,
        .covering_status = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_OPERATIONAL,
        .covering_mode = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE,
    };
    esp_zb_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_WINDOW_COVERING_TYPE_ID,
                            ESP_ZB_ZCL_ATTR_TYPE_8BIT_ENUM, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY, &config.covering_type);
    esp_zb_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID,
                            ESP_ZB_ZCL_ATTR_TYPE_8BITMAP, ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY, &config.covering_status);
    esp_zb_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID,
                            ESP_ZB_ZCL_ATTR_TYPE_8BITMAP, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, &config.covering_mode);

    uint8_t current_lift_percentage = 0x00;
    uint16_t installed_open_limit_lift = 0x0000;
    uint16_t installed_closed_limit_lift = 0x1770;
    uint16_t physical_closed_limit_lift = 0xffff;
    uint16_t current_position_lift = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE;

    esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &current_lift_percentage);
    esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &installed_open_limit_lift);
    esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &installed_closed_limit_lift);
    esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &current_position_lift);
    esp_zb_window_covering_cluster_add_attr(window_attr_list, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &physical_closed_limit_lift);

    esp_zb_cluster_list_add_window_covering_cluster(cluster_list, window_attr_list, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    /********** Custom Attribute **********/
    esp_zb_attribute_list_t *custom_attr = esp_zb_zcl_attr_list_create(0x8001);
    ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(custom_attr, 0, ESP_ZB_ZCL_ATTR_TYPE_U16, ESP_ZB_ZCL_ATTR_ACCESS_REPORTING | ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, &custom_attribute));
    ESP_ERROR_CHECK(esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));

    esp_zb_ep_list_add_ep(esp_zb_window_covering_ep, cluster_list, endpoint_config);
    esp_zb_device_register(esp_zb_window_covering_ep);

    // esp_zb_device_cb_id_handler_register(device_cb);
    // esp_zb_raw_command_handler_register(raw_command_handler);
    // esp_zb_cli_resp_handler_register(esp_zb_cli_agent_handler);

    esp_zb_core_action_handler_register(zb_action_handler);
    esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
    ESP_ERROR_CHECK(esp_zb_start(false));
    esp_zb_main_loop_iteration();
}

And send from the coordinator

esp_zb_zcl_custom_cluster_cmd_req_t cmd_req;
cmd_req.zcl_basic_cmd.src_endpoint = HA_ENDPOINT;
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
cmd_req.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
cmd_req.cluster_id = 0x8001;
cmd_req.custom_cmd_id = 0;
cmd_req.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
cmd_req.data.type = ESP_ZB_ZCL_ATTR_TYPE_U16;
cmd_req.data.size = sizeof(uint16_t);
cmd_req.data.value = to_open ? &position_up : &position_down;
to_open = !to_open;
esp_zb_lock_acquire(portMAX_DELAY);
uint8_t seq = esp_zb_zcl_custom_cluster_cmd_req(&cmd_req);
esp_zb_lock_release();

But don't receive anything in the zb_action_handler of the end device. What I'm doing wrong?

renatomotorline commented 4 months ago

Ok now I'm receiving it, I added esp_zb_zdo_match_cluster when the end device is commissioned.

But I don't receive any report from the end device in the coordinator.

End device:

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(HA_WINDOW_COVERING_ENDPOINT,
   ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
   ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
   ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
   &lift_percentage,
   false);
esp_zb_zcl_set_attribute_val(HA_WINDOW_COVERING_ENDPOINT,
   ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
   ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
   ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID,
   &lift_value,
   false);
esp_zb_zcl_status_t status = esp_zb_zcl_set_attribute_val(HA_WINDOW_COVERING_ENDPOINT,
    0x8001,
    ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
    0,
    &custom_attribute,
    false);
ESP_LOGE(TAG, "Status: 0x%02x", status);
esp_zb_lock_release();

Coordinator:

static esp_zb_cluster_list_t *custom_window_covering_clusters_create(esp_zb_window_covering_cfg_t *window_covering) {
    esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
    esp_zb_attribute_list_t *basic_cluster = esp_zb_basic_cluster_create(&(window_covering->basic_cfg));
    ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, MANUFACTURER_NAME));
    ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, MODEL_IDENTIFIER));
    ESP_ERROR_CHECK(esp_zb_cluster_list_add_basic_cluster(cluster_list, basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));
    ESP_ERROR_CHECK(esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(&(window_covering->identify_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));
    ESP_ERROR_CHECK(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_CLIENT_ROLE));

    ESP_ERROR_CHECK(esp_zb_cluster_list_add_window_covering_cluster(cluster_list, esp_zb_window_covering_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE));

    esp_zb_attribute_list_t *custom_attr = esp_zb_zcl_attr_list_create(0x8001);
    ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(custom_attr, 0, ESP_ZB_ZCL_ATTR_TYPE_U16, ESP_ZB_ZCL_ATTR_ACCESS_REPORTING | ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, &custom_attribute));
    ESP_ERROR_CHECK(esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));

    return cluster_list;
}

static void custom_window_covering_ep_create(esp_zb_ep_list_t *ep_list, uint8_t endpoint_id, esp_zb_window_covering_cfg_t *window_covering) {
    esp_zb_endpoint_config_t endpoint_config = {
        .endpoint = endpoint_id,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID,
        .app_device_version = 0};
    esp_zb_ep_list_add_ep(ep_list, custom_window_covering_clusters_create(window_covering), endpoint_config);
}

static void esp_zb_task(void *pvParameters) {
    // Initialize Zigbee stack
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
    esp_zb_init(&zb_nwk_cfg);

    // Create endpoint list
    esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();

    // Create window covering
    esp_zb_window_covering_cfg_t window_covering_cfg = ESP_ZB_DEFAULT_WINDOW_COVERING_CONFIG();
    custom_window_covering_ep_create(ep_list, HA_ENDPOINT, &window_covering_cfg);

    // Register the device
    esp_zb_device_register(ep_list);

    esp_zb_core_action_handler_register(zb_action_handler);
    esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
    ESP_ERROR_CHECK(esp_zb_start(false));
    esp_zb_main_loop_iteration();
}
xieqinan commented 4 months ago

@renatomotorline ,

But I don't receive any report from the end device in the coordinator.

If you expect the end device to report the custom attribute to the coordinator, you need to add a binding and configure the report operation on the end device side so that the end device knows the report's destination. These operations can refer to the customized switch example. By the way, the custom cluster SHOULD be the client role on the coordinator.

chshu commented 2 months ago

@renatomotorline please reopen if any follow up questions.