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

Provide examples of how to restrict pairing as a device (TZ-222) #66

Open
nomis opened this issue Aug 6, 2023 · 19 comments
Open

Provide examples of how to restrict pairing as a device (TZ-222) #66

nomis opened this issue Aug 6, 2023 · 19 comments

Comments

@nomis
Copy link

nomis commented Aug 6, 2023

When I remove the device from the network using the coordinator I get ESP_ZB_NWK_LEAVE_TYPE_RESET but then it immediately rejoins the network when I restart the device (and only if I restart the device).

This is annoying during development because my coordinator/Home Assistant lets the device automatically reconnect even when not being requested to add new devices and it's hard to get it to clear out the old endpoint information. It's also bad for security because the device should never unexpectedly connect to someone else's network.

Please provide an example of how to make the device:

  • Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour
  • Rejoin the configured network on startup (and only that network - no fallback to new/other networks)
  • Connect to a new network only when requested to do so (e.g. by a GPIO)
  • Delete network information after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs (without restarting)
  • Connect to a network after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs and the network information has been deleted - i.e. a new "pairing" request (currently I have to restart to do this)
@github-actions github-actions bot changed the title Provide examples of how to restrict pairing as a device Provide examples of how to restrict pairing as a device (TZ-222) Aug 6, 2023
@xieqinan
Copy link
Contributor

xieqinan commented Aug 7, 2023

Hi @nomis ,

  • Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour
  • Rejoin the configured network on startup (and only that network - no fallback to new/other networks)
  • Connect to a new network only when requested to do so (e.g. by a GPIO)

The esp_zb_get_short_address() provides a quick way to identify the network configured on start-up. If there is no network configured, the return value will be 0xfffe.

  • Delete network information after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs (without restarting)
  • Connect to a network after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs and the network information has been deleted - i.e. a new "pairing" request (currently I have to restart to do this)

For this issue, could you please consider referring to the following code for implementation on your end device example?

    case ESP_ZB_ZDO_SIGNAL_LEAVE: {
        esp_zb_zdo_signal_leave_params_t *leave_params = (esp_zb_zdo_signal_leave_params_t *)esp_zb_app_signal_get_params(p_sg_p);
        if (leave_params && leave_params->leave_type == ESP_ZB_NWK_LEAVE_TYPE_RESET) {
            esp_zb_nvram_erase_at_start(true);                                          // erase previous network information.
            esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); // steering a new network.
        }
    }

@nomis
Copy link
Author

nomis commented Aug 12, 2023

When debugging this it's important to be aware that routers on the network may remain in pairing mode for up to 250 seconds, so it can look like the device is able to rejoin a network on its own (with previous configuration information) but the network is actually still in pairing mode.

* Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour

Checking esp_zb_get_short_address() != 0xffff in ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP works to determine if there is a network configured.

* Rejoin the configured network on startup (and only that network - no fallback to new/other networks)

I have not yet been able to test that it will only use the configured network because I'll need to set up a second coordinator.

* **Connect to a new network only when requested to do so (e.g. by a GPIO)**

I can do this by deciding when to call esp_zb_bdb_start_top_level_commissioning().

* Delete network information after `ESP_ZB_NWK_LEAVE_TYPE_RESET` occurs (without restarting)

This happens automatically but I can't do it manually without restarting.

I don't know why you're suggesting esp_zb_nvram_erase_at_start(true) when that's documented to only affect esp_zb_start() which has already been called.

* Connect to a network after `ESP_ZB_NWK_LEAVE_TYPE_RESET` occurs and the network information has been deleted  - i.e. a new "pairing" request (currently I have to restart to do this)

I can do this by deciding when to call esp_zb_bdb_start_top_level_commissioning() again.


I'd like to implement a "leave network" button, is there a way to do esp_zb_factory_reset() without a system reset?

This would need to work even while attempting to connect to a new/existing network.

@xieqinan
Copy link
Contributor

Hi,

Some assistance that may be useful for creating the restricted pairing device example as following.

  • Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour

If the device possesses network configurations, the stack will facilitate the device in rejoining the configured network. It will then exclusively issue the ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START signal to users. Conversely, if the device lacks network configurations, the stack will attempt to locate a network and subsequently dispatch the ESP_ZB_BDB_SIGNAL_STEERING signal to users.

  • Delete network information after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs (without restarting)
  • Connect to a network after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs and the network information has been deleted - i.e. a new "pairing" request (currently I have to restart to do this)

If the end device receive ESP_ZB_NWK_LEAVE_TYPE_RESET signal, the stack will clear the network information. I think you just need to call the esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); to make it send pair request.

I'd like to implement a "leave network" button, is there a way to do esp_zb_factory_reset() without a system reset?

Instead of calling esp_zb_factory_reset() without a system reset, you can choose to use esp_zb_zdo_device_leave_req() to initiate the device's departure from the network. This action will result in erasing the network configuration information of device and the signal ESP_ZB_NWK_LEAVE_TYPE_RESET will be issued.

@nomis
Copy link
Author

nomis commented Aug 15, 2023

I have not yet been able to test that it will only use the configured network because I'll need to set up a second coordinator.

I've now verified that it won't change network (when the configured network is unavailable) even if there's another coordinator accepting new devices.

I'd like to implement a "leave network" button, is there a way to do esp_zb_factory_reset() without a system reset?

Instead of calling esp_zb_factory_reset() without a system reset, you can choose to use esp_zb_zdo_device_leave_req() to initiate the device's departure from the network. This action will result in erasing the network configuration information of device and the signal ESP_ZB_NWK_LEAVE_TYPE_RESET will be issued.

This works properly if I'm connected but not if I'm in the process of connecting.

If I do the following, I get a ESP_ZB_NWK_LEAVE_TYPE_RESET signal followed immediately by ESP_ZB_BDB_SIGNAL_STEERING that claims I'm on network 00:00:00:00:00:00:00:00 (0xffff) as device 0xfffe on the last channel that was used (or 255 if there hasn't been a connection since boot). The false network is not retained after a restart.

esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
/* wait 2 seconds */
esp_zb_zdo_mgmt_leave_req_param_t param{};
esp_zb_get_long_address(param.device_address);
param.dst_nwk_addr = 0xffff;
esp_zb_zdo_device_leave_req(&param, nullptr, nullptr);

@nomis
Copy link
Author

nomis commented Aug 16, 2023

  • Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour

If the device possesses network configurations, the stack will facilitate the device in rejoining the configured network. It will then exclusively issue the ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START signal to users. Conversely, if the device lacks network configurations, the stack will attempt to locate a network and subsequently dispatch the ESP_ZB_BDB_SIGNAL_STEERING signal to users.

There are other side effects combining this with esp_zb_zdo_device_leave_req(). If I have used esp_zb_zdo_device_leave_req() on a previous boot and then use esp_zb_start(true) on startup then there is a long delay with no signals at all (9 to 40 seconds) before I finally receive ESP_ZB_ZDO_SIGNAL_LEAVE and then ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START with a status of -1. This time the device short address is 0xfffe.

It's as if it's still trying to leave the previous network on startup before it'll do anything else.

@xieqinan
Copy link
Contributor

xieqinan commented Apr 2, 2024

@nomis ,

Regarding the issue, do you have any further topics you'd like to discuss or address? Let me know if there's anything else you need assistance with regarding this matter.

@nomis
Copy link
Author

nomis commented Apr 2, 2024

I've already made comments #66 (comment) and #66 (comment) that haven't been addressed:

When leaving there must be no previous state left that will impact the next startup/join.

It should be possible to leave at any time, including while joining. That's not possible without side effects.

@IgnacioHR
Copy link

I'm sorry this is an old topic and it remains open!

@xieqinan can you help me on this?

I just wanted to say I would like to implement similar functionality for my device. Here is a link for the project I'm working on Zigbee Gas Meter

You can see all source code I wrote. I can recognise a button "long-press" even when the device wakes-up from sleep but it doesn't leave the network when using esp_zb_zdo_device_leave_req(...) also it doesn't crash. Here is a log of what I see:

I (340) MICASA_GAS_METER: Starting Zigbee GasMeter...
I (340) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:3
I (350) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:3
I (360) MICASA_GAS_METER: Counter loaded: low=1919349 high=0
I (360) MICASA_GAS_METER: Wake up from GPIO. Time spent in deep sleep and boot: 177849ms
I (370) MICASA_GAS_METER: Enabling timer wakeup, 3600s
I (380) MICASA_GAS_METER: In esp_zb_task
I (380) MICASA_GAS_METER: Device short address 1 0x0
I (380) MICASA_GAS_METER: esp_zb_init...
I (390) phy_init: phy_version 320,348a293,Sep  3 2024,16:33:12
I (410) phy: libbtbb version: 04952fd, Sep  3 2024, 16:33:30
I (410) MICASA_GAS_METER: Device short address 2 0xffff
I (430) main_task: Returned from app_main()
I (440) MICASA_GAS_METER: Shall handle signal 0x17 - ZDO Config Ready
I (440) MICASA_GAS_METER: Shall handle signal 0x1 - ZDO Skip Start Up
I (440) MICASA_GAS_METER: Initialize Zigbee stack
I (2900) MICASA_GAS_METER: Shall handle signal 0x6 - BDB Device Reboot
I (2900) MICASA_GAS_METER: Start long press timer for 200ms
I (2900) MICASA_GAS_METER: Deferred driver initialization successful
I (2900) MICASA_GAS_METER: Device started up in non factory-reset mode
I (2910) MICASA_GAS_METER: Start one-shot timer for 120s to enter the deep sleep
I (2920) MICASA_GAS_METER: Device rebooted
I (3100) MICASA_GAS_METER: Button long press detected
I (3100) MICASA_GAS_METER: Leaving network
I (3100) MICASA_GAS_METER: Leave status 0x0
I (3110) MICASA_GAS_METER: Shall handle signal 0x3 - ZDO Leave
I (5500) MICASA_GAS_METER: Shall handle signal 0x6 - BDB Device Reboot
I (5500) MICASA_GAS_METER: Deferred driver initialization successful
I (5500) MICASA_GAS_METER: Device started up in non factory-reset mode
I (5510) MICASA_GAS_METER: Restart one-shot timer for 120s to enter the deep sleep
I (5510) MICASA_GAS_METER: Device rebooted
I (10540) MICASA_GAS_METER: In zb_action_handler callback_id=0x1005
I (10540) MICASA_GAS_METER: Default response callback: dst 0x7d9d, status: 0

Can someone help review what I'm doing wrong there? What I want to achieve can be described as:

Long pressing the device button should:

  • Leave the network
  • Clear short and long address
  • Start trying to join another network (or the same network if it is allowing new devices to enter)

Thanks in advance

@xieqinan
Copy link
Contributor

@IgnacioHR ,

Based on your project, you may refer to the code below to achieve your objective:

void leave_callback(esp_zb_zdp_status_t zdo_status, void *args)
{
    ESP_LOGI(TAG, "Leave status 0x%x", zdo_status);
}

static void s_longpress_timer_callback(void *arg)
{
    ESP_LOGI(TAG, "Button long press detected");

    esp_zb_zdo_mgmt_leave_req_param_t leave_request = {
        .device_address = {},
        .dst_nwk_addr = esp_zb_get_short_address(),
        .remove_children = 0,
        .rejoin = 0,
    };
    esp_zb_get_long_address(leave_request.device_address);
    esp_zb_zdo_device_leave_req(&leave_request, leave_callback, NULL);
}

void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
         ...
         case ESP_ZB_ZDO_SIGNAL_LEAVE:
          ESP_LOGW(TAG, "Try to join another network");
          esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
          break;
}

@IgnacioHR
Copy link

@xieqinan Thank you!

Works like a charm! rejoin was set to 1 and setting it to 0 made it to work as expected!

@nomis should you close this issue?

@nomis
Copy link
Author

nomis commented Jan 22, 2025

@nomis should you close this issue?

My comments in #66 (comment) have not yet been addressed.

@IgnacioHR
Copy link

In any case, I want to thank you @nomis because your explanations on this topic helped me to move forward.

Cheers
Ignacio

@xieqinan
Copy link
Contributor

@nomis ,

Sorry for the delay. Let's revisit and discuss the issue based on the esp-zigbee-sdk v1.6.2.

Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour
Rejoin the configured network on startup (and only that network - no fallback to new/other networks)

The device cannot configure all network parameters to rejoin a specific network directly, but it can be restricted to join a specific network by calling esp_zb_set_extended_pan_id(). However, if the device has joined the network through commissioning and is then rebooted, the esp_zb_bdb_is_factory_new() function provides a way to determine the device's state.

Connect to a new network only when requested to do so (e.g. by a GPIO)

The esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION); can be used to start a network commissioning.

Delete network information after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs (without restarting)
Connect to a network after ESP_ZB_NWK_LEAVE_TYPE_RESET occurs and the network information has been deleted - i.e. a new "pairing" request (currently I have to restart to do this)

If the ESP_ZB_ZDO_SIGNAL_LEAVE signal with ESP_ZB_NWK_LEAVE_TYPE_RESET is triggered, it indicates that the stack has erased the old network information. If you wish to recommission, please call esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);. If the device is expected to remain off-network, no additional action is required. refer to this comment.

@nomis
Copy link
Author

nomis commented Jan 22, 2025

You've replied to my original message instead of this comment: #66 (comment)

Specifically: "This works properly if I'm connected but not if I'm in the process of connecting." and "There are other side effects combining this with esp_zb_zdo_device_leave_req()."

@xieqinan
Copy link
Contributor

Specifically: "This works properly if I'm connected but not if I'm in the process of connecting." and "There are other side effects combining this with esp_zb_zdo_device_leave_req()."

Okay, I’m not sure if the scenario is common, but you can address the concern by calling esp_zb_bdb_cancel_steering() before esp_zb_zdo_device_leave_req().

@xieqinan
Copy link
Contributor

xieqinan commented Feb 6, 2025

Hi @nomis ,

To resolve this issue, I still have two questions that I would like to confirm with you.

Identify if there has been a network configured on startup (without any RF communication) - this is necessary to implement "automatically pair only if there's no network configured" behaviour

Regarding this requirement, I understand that "network configuration" can be regarded as the device having previously joined a network, meaning its network settings are already configured, rather than using specific APIs to set up network parameters for a factory-new device. If this is incorrect, please clarify.

esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
/* wait 2 seconds */
esp_zb_zdo_mgmt_leave_req_param_t param{};
esp_zb_get_long_address(param.device_address);
param.dst_nwk_addr = 0xffff;
esp_zb_zdo_device_leave_req(&param, nullptr, nullptr);

I still have a bit confuse for the above calling. Could you please share the reason for the above action? If you intend for the device to steer, it means the device is still not on a network, so why do you want it to leave the network

@nomis
Copy link
Author

nomis commented Feb 6, 2025

If you intend for the device to steer, it means the device is still not on a network, so why do you want it to leave the network

I have a "join" and "leave" (not be a part of any network) action, and expect to be able to "leave" at any time without workarounds like "reboot the device". I expect that cancelling steering will work although I can't test it right now.

Without documentation explaining how to do these kinds of things there's no way to know what functions are available or when they can be called.

@xieqinan
Copy link
Contributor

xieqinan commented Feb 6, 2025

I have a "join" and "leave" (not be a part of any network) action, and expect to be able to "leave" at any time without workarounds like "reboot the device". I expect that cancelling steering will work although I can't test it right now.

Understood. We will ensure that leave can be called at any time and is not affected by the join procedure in the next release.

@xieqinan
Copy link
Contributor

xieqinan commented Feb 7, 2025

Hi

void zb_leave(uint8_t param)
{
    esp_zb_zdo_mgmt_leave_req_param_t cmd_req;

    esp_zb_get_long_address(cmd_req.device_address);
    cmd_req.dst_nwk_addr = 0xffff;
    ESP_LOGI(TAG, "Leave address: 0x%llx, 0x%x", *(uint64_t *)cmd_req.device_address, cmd_req.dst_nwk_addr);
    esp_zb_zdo_device_leave_req(&cmd_req, NULL, NULL);
}

void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
    uint32_t *p_sg_p       = signal_struct->p_app_signal;
    esp_err_t err_status = signal_struct->esp_err_status;
    esp_zb_app_signal_type_t sig_type = *p_sg_p;
    switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
        ESP_LOGI(TAG, "Initialize Zigbee stack");
        esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
        break;
    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
    case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
        if (err_status == ESP_OK) {
            ESP_LOGI(TAG, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
            ESP_LOGI(TAG, "Device started up in%s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : " non");
            if (esp_zb_bdb_is_factory_new()) {
                ESP_LOGI(TAG, "Start network steering");
                esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
                esp_zb_scheduler_alarm((esp_zb_callback_t)zb_leave, 0, 1000);
                esp_zb_scheduler_alarm((esp_zb_callback_t)zb_leave, 0, 2000);
                esp_zb_scheduler_alarm((esp_zb_callback_t)zb_leave, 0, 3000);
                esp_zb_scheduler_alarm((esp_zb_callback_t)zb_leave, 0, 5000);
            } else {
                ESP_LOGI(TAG, "Device rebooted");
            }
        } else {
            ESP_LOGW(TAG, "%s failed with status: %s, retrying", esp_zb_zdo_signal_to_string(sig_type),
                     esp_err_to_name(err_status));
            esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb,
                                   ESP_ZB_BDB_MODE_INITIALIZATION, 1000);
        }
        break;
    case ESP_ZB_BDB_SIGNAL_STEERING:
        printf("error code: 0x%x\n", err_status);
        esp_zb_ieee_addr_t extended_pan_id;
        esp_zb_get_extended_pan_id(extended_pan_id);
        ESP_LOGI(TAG,
                 "Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: "
                 "0x%04hx, Channel:%d, Short Address: 0x%04hx)",
                 extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3],
                 extended_pan_id[2], extended_pan_id[1], extended_pan_id[0], esp_zb_get_pan_id(),
                 esp_zb_get_current_channel(), esp_zb_get_short_address());
        break;
    default:
        ESP_LOGI(TAG, "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
                 esp_err_to_name(err_status));
        break;
    }
}

I used the code above to reproduce this issue. Have you verified the status of the ESP_ZB_BDB_SIGNAL_STEERING signal?

When esp_zb_zdo_device_leave_req() is called during the network connection process, the stack checks the device state. If commissioning is in progress or device is not on network, a BDB commissioning signal is triggered to halt the commissioning steps and report an error status to the application. The leave command then clears network information and notifies the application via the ESP_ZB_ZDO_SIGNAL_LEAVE signal.

If an error status is received from the ESP_ZB_BDB_SIGNAL_STEERING signal, the commissioning process should be restarted; otherwise, network information may become inconsistent. Could you confirm these steps are functioning as intended?

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

No branches or pull requests

4 participants