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

Can't setup a simple switch endpoint and be visible in Home Assistant (TZ-1398) #506

Closed
renaudjenny opened this issue Dec 17, 2024 · 7 comments
Labels

Comments

@renaudjenny
Copy link

Question

Hi there, my very first question here!
First of all, thank you for your work around this! I see a lot of support for questions and requests! Hope my question will be easy to answer!

Let me also do a very quick intro: I'm a Swift developer, so not very familiar with C, my problem could totally be lack of C basic knowledge.

My project: It's quite simple: a light driven by Home Assistant, pretty ease, just used this example and everything is working perfectly to drive the light.
On top of that, I want to add a switch, with it, I'd like to drive something else in Home Assistant, basically, be a simple "remote".
From Home Assistant, I would like to be able to bind things together with my other devices. No need to bind anything from the device code itself if possible.

My problem: In Home Assistant, I well see the light, and I can toggle on/off seamlessly, it works.
But I can't see the "switch/remote", the one I'd like to bind in one of my other device via Home Assistant.
When I go to "Automation" for instance, I can't see anything related to button/switch/remote to trigger something. I only see things like "light on", "light off", etc.

Ideally, I'd like to see something like one of my button. Perhaps it's something to configure in Home Assistant itself, but I think primarily I need to somehow expose the switch ability to toggle.

image

Certainly that

    // Switch
    esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
    esp_zb_cluster_list_t *switch_cluster_list = esp_zb_zcl_cluster_list_create();
    esp_zb_cluster_list_add_basic_cluster(switch_cluster_list, esp_zb_basic_cluster_create(&switch_cfg.basic_cfg), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
    esp_zb_cluster_list_add_identify_cluster(switch_cluster_list, esp_zb_identify_cluster_create(&switch_cfg.identify_cfg), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
    esp_zb_attribute_list_t *switch_attr_list = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);
    bool switch_value = false;
    esp_zb_on_off_cluster_add_attr(switch_attr_list, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF, &switch_value);

    esp_zb_endpoint_config_t switch_endpoint_config = {
        .endpoint = BATHROOM_SWITCH_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
    };
    esp_zb_ep_list_add_ep(ep_list, switch_cluster_list, switch_endpoint_config);
    zcl_basic_manufacturer_info_t switch_info = {
        .manufacturer_name = ESP_MANUFACTURER_NAME,
        .model_identifier = ESP_MODEL_IDENTIFIER,
    };
    esp_zcl_utility_add_ep_basic_manufacturer_info(ep_list, BATHROOM_SWITCH_ENDPOINT, &switch_info);

Is not enough?

The whole project compile, and is publicly available, so don't hesitate to check it if it helps (I'm using the add-switch-for-zigbee-network-reset branch for the current development).

I did a lot of things, tried to use something else than a switch, but I'm now running out of ideas.

Thanks for your help :).

Additional context.

No response

@github-actions github-actions bot changed the title Can't setup a simple switch endpoint and be visible in Home Assistant Can't setup a simple switch endpoint and be visible in Home Assistant (TZ-1398) Dec 17, 2024
@xieqinan
Copy link
Contributor

Hi,

On top of that, I want to add a switch, with it, I'd like to drive something else in Home Assistant, basically, be a simple "remote".
From Home Assistant, I would like to be able to bind things together with my other devices. No need to bind anything from the device code itself if possible.

I think you can use the below steps for a try:

static void zb_buttons_handler(switch_func_pair_t *button_func_pair)
{
    switch (button_func_pair->func) {
    case SWITCH_ONOFF_TOGGLE_CONTROL: {
        /* send on-off toggle command to remote device */
        esp_zb_zcl_on_off_cmd_t cmd_req;
        cmd_req.zcl_basic_cmd.src_endpoint = HA_ONOFF_SWITCH_ENDPOINT;
        cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
        cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID;
        esp_zb_lock_acquire(portMAX_DELAY);
        esp_zb_zcl_on_off_cmd_req(&cmd_req);
        esp_zb_lock_release();
        ESP_EARLY_LOGI(TAG, "Send 'on_off toggle' command to address(0x%x) endpoint(%d)", on_off_light.short_addr, on_off_light.endpoint);
    } break;
    default:
        break;
    }
}

  • Let them join the coordinator created by the Home Assistant(HA).
    image

  • Configure the Switch device:
    image

  • Bind it to light
    image

  • Press the button on the switch to obverse the light state, it will be on.

@renaudjenny
Copy link
Author

renaudjenny commented Dec 18, 2024

Thanks a lot @xieqinan for the quick answer!

I will definitely give an other try with your suggestions.

Just to be clear, my goal is not to bind the light with the switch. I do understand while reading my description it's not totally clear at first glance, but let me explain what I really want, as I think I've been too vague in my initial description.

I currently have in my bathroom two devices (not ESP32, but ZigBee devices bought in the market):

  • One sensor for temperature
  • One smart plug where the room heating is connected to
  • With Home Assistant, I'm using the two together to create a thermostat, it's quite useful, I set the wanted temperature of the room, let's say 19ºC, and the smart plug will be turned on to reach this temperature and when it's above, set the heater off and when it's below set it back on, etc.
  • Thermostat on Home Assistant have "modes", which could be Eco or Comfort for instance, and you can set the target temperature, I personally set Eco to 16ºC and Comfort to 19ºC to give concrete examples
  • Unfortunately, for now, nothing indicates me the current mode of the thermostat in the room, if I don't have something to check Home Assistant, like my smartphone, I can't really know what mode is on... And I can't neither easily switch from Comfort to Eco.

So the idea to have a small device of my own (with my ESP32-C6 devKit for now) to indicate me which mode my thermostat is currently on

  • Light is green: Eco mode
  • Light is red: Comfort mode
  • If possible, a switch (button) will toggle between the two modes

The "logic" with green/red/switch binding could be done directly in Home Assistant, I "just" need to expose either a RGB light or two lights (green & red), this is the easy part I'd say. The difficult one now is exposing the switch/button click in Home Assistant.

I also wonder if a binary sensor would be the thing I'm looking for perhaps, instead of a switch? So I can tell Home Assistant to switch the thermostat mode to Eco/Comfort according to the sensor state true/false? The binary sensor value could be toggled when the ESP32 button is pressed.

What do you think?

@xieqinan
Copy link
Contributor

@renaudjenny ,

The difficult one now is exposing the switch/button click in Home Assistant.

Understood, but I believe this issue is not related to the esp-zigbee-sdk. If the switch joined the HA network as the ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID type, HA should indeed not display the button to the user, as it lacks access to the switch device.

I also wonder if a binary sensor would be the thing I'm looking for perhaps, instead of a switch? So I can tell Home Assistant to switch the thermostat mode to Eco/Comfort according to the sensor state true/false? The binary sensor value could be toggled when the ESP32 button is pressed.

I think this is a good idea; you can give it a try.

@renaudjenny
Copy link
Author

I tried to get inspiration from the customised_device example, unfortunately, I've not been able to see the binary input (endpoint to get exposed) on HA... However, I can see it in "Manage Zigbee device", if I try to "read" the value with HA, it works, only the first time (let's consider that a problem for later, pretty sure I'm doing something totally wrong with the req_cmd part).

Could you please let me know if there is something wrong in my endpoint setup for Binary Input?

    // Binary input
    esp_zb_binary_input_cluster_cfg_t binary_input_cfg = {
        .out_of_service = false,
        .status_flags = 0
    };
    esp_zb_basic_cluster_cfg_t binary_input_basic_cfg = {
        .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE,
        .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,
    };
    esp_zb_identify_cluster_cfg_t binary_input_identify_cfg = {
        .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE
    };
    esp_zb_cluster_list_t *binary_input_cluster_list = esp_zb_zcl_cluster_list_create();
    esp_zb_attribute_list_t *binary_input_basic_cluster = esp_zb_basic_cluster_create(&binary_input_basic_cfg);
    esp_zb_basic_cluster_add_attr(binary_input_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, ESP_MANUFACTURER_NAME);
    esp_zb_basic_cluster_add_attr(binary_input_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, ESP_MODEL_IDENTIFIER);
    esp_zb_cluster_list_add_basic_cluster(binary_input_cluster_list, binary_input_basic_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
    esp_zb_cluster_list_add_identify_cluster(binary_input_cluster_list, esp_zb_identify_cluster_create(&binary_input_identify_cfg), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
    esp_zb_attribute_list_t *binary_input_attr_list = esp_zb_binary_input_cluster_create(&binary_input_cfg);
    bool binary_input_value = false;
    esp_zb_binary_input_cluster_add_attr(binary_input_attr_list, ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &binary_input_value);
    esp_zb_cluster_list_add_binary_input_cluster(binary_input_cluster_list, binary_input_attr_list, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);

    esp_zb_endpoint_config_t binary_input_endpoint_config = {
        .endpoint = BATHROOM_BINARY_INPUT_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID,
        .app_device_version = 0,
    };
    esp_zb_ep_list_add_ep(ep_list, binary_input_cluster_list, binary_input_endpoint_config);

(If you want to check all the code, the branch has been updated.)

Thank you :).

@xieqinan
Copy link
Contributor

Based on #506 (comment), I believe I understand your requirements. You need a physical light to indicate the thermostat state (Comfort or Eco) and a physical toggle switch to control the thermostat's state transition, with the light reflecting the state changes. If that’s the case, I don’t think it’s necessary to expose the switch/button click in Home Assistant. The following solution might meet your needs:

  • The network consists of four standalone devices: HA, a thermostat (temperature sensor and smart plug), a light, and a switch.
  • The thermostat should bind its current temperature to HA so that HA can display it.
  • The thermostat should bind its current temperature to the light device with the temperature cluster client through HA, enabling the light to adjust its color based on the temperature.
  • The light should bind its on/off or color control cluster to HA so that any state changes in the light device are reported to HA, allowing HA to display the state.
  • The switch should bind to the light, enabling the switch to toggle the light's state. The light will then report its state to HA, which will receive the change and control the thermostat accordingly.

What do you think of this approach?

@renaudjenny
Copy link
Author

Thanks for the answer @xieqinan.
You mostly got the purpose of my project. But I think I'm still struggling to clarify one part.

You need a physical light to indicate the thermostat state (Comfort or Eco) and a physical toggle switch to control the thermostat's state transition

You successfully summed up my project in one sentence!

The thing you may have misunderstood is that the logic happens in Home Assistant and not in the ESP32 project itself.

I think I still need to expose a switch in Home Assistant (HA), mostly because with ZHA (basic ZigBee integration), binding ZigBee devices between them is not the essence of ZHA, you mostly have to use HA Automation to, let's say, "link" things together (let's use "link" terminology and not "bind", just to not confuse with ZigBee binding).

I'll give you one of my personal Automation, a very simple remote switch that controls one of my smart plug, it's not related with my ESP32 project btw.

image

You can easily understand what's going on here, basically, one of my ZigBee device (an Aquara switch remote, named "Switch bureau") has an exposed "remote_short_press" pressed trigger that I'm using in my Automation as Trigger.

This Aqara switch device has a lot of trigger options

image

Then I can easily link these trigger with whatever I want HA to do. In this example a simple click on this remote will toggle my smart plug state.

My goal with my project is to do exactly that, expose a switch + a light, and do an Automation like the one above to control the thermostat state. That's what I mean when I say "the logic is in Home Assistant", I don't need to know the current thermostat state in my ESP32 project, I just need to expose a switch and two lights.

My real only problem now is that Home Assistant doesn't see my switch as a potential trigger for automation if that makes more sense now.

@renaudjenny
Copy link
Author

renaudjenny commented Jan 7, 2025

I was finally able to get what I want through a binary sensor.

image

For those who wants to try that, I think this code will help

    // Binary input
    esp_zb_binary_input_cluster_cfg_t binary_input_cfg = {
        .out_of_service = ESP_ZB_ZCL_BINARY_INPUT_OUT_OF_SERVICE_DEFAULT_VALUE,
        .status_flags = ESP_ZB_ZCL_BINARY_INPUT_STATUS_FLAG_DEFAULT_VALUE
    };
    esp_zb_basic_cluster_cfg_t binary_input_basic_cfg = {
        .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE,
        .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,
    };
    esp_zb_identify_cluster_cfg_t binary_input_identify_cfg = {
        .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE
    };
    esp_zb_cluster_list_t *binary_input_cluster_list = esp_zb_zcl_cluster_list_create();
    esp_zb_attribute_list_t *binary_input_basic_cluster = esp_zb_basic_cluster_create(&binary_input_basic_cfg);
    esp_zb_basic_cluster_add_attr(binary_input_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, ESP_MANUFACTURER_NAME);
    esp_zb_basic_cluster_add_attr(binary_input_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, ESP_MODEL_IDENTIFIER);
    esp_zb_cluster_list_add_basic_cluster(binary_input_cluster_list, binary_input_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
    esp_zb_cluster_list_add_identify_cluster(binary_input_cluster_list, esp_zb_identify_cluster_create(&binary_input_identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
    esp_zb_cluster_list_add_identify_cluster(binary_input_cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
    esp_zb_attribute_list_t *binary_input_attr_list = esp_zb_binary_input_cluster_create(&binary_input_cfg);
    esp_zb_binary_input_cluster_add_attr(binary_input_attr_list, ESP_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, "\x0C""Switch state");
    esp_zb_binary_input_cluster_add_attr(binary_input_attr_list, ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &(bool *){ false });
    esp_zb_cluster_list_add_binary_input_cluster(binary_input_cluster_list, binary_input_attr_list, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    esp_zb_endpoint_config_t binary_input_endpoint_config = {
        .endpoint = BATHROOM_BINARY_INPUT_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID,
        .app_device_version = 0,
    };
    esp_zb_ep_list_add_ep(ep_list, binary_input_cluster_list, binary_input_endpoint_config);

My main problem was actually some confusion between ESP_ZB_ZCL_CLUSTER_SERVER_ROLE and ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE. It's still not clear for me what it is, and I struggle quite a lot to find a good explanation for these.

I mostly done "try and die" methodology before finding what's wrong, and unfortunately, as I mentioned, I'm still not sure to understand what I've done properly to make it work :(.

I'm still a bit frustrated even after having my project working. Mostly because:

  • Something that looks simple at first glance requires a lot of configuration from my point of view
  • Documentation is quite good, especially compared to some other challenger, but it misses some basic concepts that are hard to understand by just reading the code or guessing what could happen
  • Some stuff are not working like the Binary Sensor description is not visible on HA and I have no idea how to make it work (doesn't look to be documented anywhere)

That being said, as my project is now working like I want and the ticket initial purpose is now fixed, I'm closing the ticket.

If you have any suggestions for more documentations about ZigBee concepts, I'd be more than happy if you can share it here.

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants