diff --git a/docs/en/zigbee/ep_contact_switch.rst b/docs/en/zigbee/ep_contact_switch.rst index f7f6dc15c66..50844cdd180 100644 --- a/docs/en/zigbee/ep_contact_switch.rst +++ b/docs/en/zigbee/ep_contact_switch.rst @@ -63,6 +63,17 @@ Sets the contact switch to open state. This function will return ``true`` if successful, ``false`` otherwise. +report +^^^^^^ + +Manually reports the current contact state. + +.. code-block:: arduino + + bool report(); + +This function will return ``true`` if successful, ``false`` otherwise. + setIASClientEndpoint ^^^^^^^^^^^^^^^^^^^^ @@ -74,16 +85,38 @@ Sets the IAS Client endpoint number (default is 1). * ``ep_number`` - IAS Client endpoint number -report -^^^^^^ +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ -Manually reports the current contact state. +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. .. code-block:: arduino - bool report(); + bool requestIASZoneEnroll(); -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/docs/en/zigbee/ep_door_window_handle.rst b/docs/en/zigbee/ep_door_window_handle.rst index 53203f463dd..b2339d681a5 100644 --- a/docs/en/zigbee/ep_door_window_handle.rst +++ b/docs/en/zigbee/ep_door_window_handle.rst @@ -67,6 +67,17 @@ Sets the door/window handle to tilted position. This function will return ``true`` if successful, ``false`` otherwise. +report +^^^^^^ + +Manually reports the current handle position. + +.. code-block:: arduino + + bool report(); + +This function will return ``true`` if successful, ``false`` otherwise. + setIASClientEndpoint ^^^^^^^^^^^^^^^^^^^^ @@ -78,16 +89,38 @@ Sets the IAS Client endpoint number (default is 1). * ``ep_number`` - IAS Client endpoint number -report -^^^^^^ +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ -Manually reports the current handle position. +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. .. code-block:: arduino - bool report(); + bool requestIASZoneEnroll(); -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/docs/en/zigbee/ep_vibration_sensor.rst b/docs/en/zigbee/ep_vibration_sensor.rst index 896c4672c6d..d8ca3fdd5f4 100644 --- a/docs/en/zigbee/ep_vibration_sensor.rst +++ b/docs/en/zigbee/ep_vibration_sensor.rst @@ -73,9 +73,42 @@ Manually reports the current vibration state. .. code-block:: arduino - void report(); + bool report(); -This function does not return a value. +This function will return ``true`` if successful, ``false`` otherwise. + +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. + +.. code-block:: arduino + + bool requestIASZoneEnroll(); + +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino index ce9eedb683d..1a84c4d7471 100644 --- a/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino @@ -31,17 +31,25 @@ #endif #include "Zigbee.h" +#include /* Zigbee contact sensor configuration */ -#define CONTACT_SWITCH_ENDPOINT_NUMBER 10 +#define CONTACT_SWITCH_ENDPOINT_NUMBER 1 uint8_t button = BOOT_PIN; uint8_t sensor_pin = 4; ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER); +/* Preferences for storing ENROLLED flag to persist across reboots */ +Preferences preferences; + void setup() { Serial.begin(115200); + preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots + bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences + preferences.end(); + // Init button + switch pinMode(button, INPUT_PULLUP); pinMode(sensor_pin, INPUT_PULLUP); @@ -67,6 +75,31 @@ void setup() { delay(100); } Serial.println(); + + // Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll + if (enrolled) { + Serial.println("Device has been enrolled before - restoring IAS Zone enrollment"); + zbContactSwitch.restoreIASZoneEnroll(); + } else { + Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment"); + zbContactSwitch.requestIASZoneEnroll(); + } + + while (!zbContactSwitch.enrolled()) { + Serial.print("."); + delay(100); + } + Serial.println(); + Serial.println("Zigbee enrolled successfully!"); + + // Store ENROLLED flag only if this was a new enrollment (previous flag was false) + // Skip writing if we just restored enrollment (flag was already true) + if (!enrolled) { + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", true); // set ENROLLED flag to true + preferences.end(); + Serial.println("ENROLLED flag saved to preferences"); + } } void loop() { @@ -91,6 +124,11 @@ void loop() { if ((millis() - startTime) > 3000) { // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + // Clear the ENROLLED flag from preferences + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", false); // set ENROLLED flag to false + preferences.end(); + Serial.println("ENROLLED flag cleared from preferences"); delay(1000); Zigbee.factoryReset(); } diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino index d9ac7b6e241..b3fc6b9d18b 100644 --- a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino @@ -31,17 +31,25 @@ #endif #include "Zigbee.h" +#include /* Zigbee vibration sensor configuration */ -#define VIBRATION_SENSOR_ENDPOINT_NUMBER 10 +#define VIBRATION_SENSOR_ENDPOINT_NUMBER 1 uint8_t button = BOOT_PIN; uint8_t sensor_pin = 4; ZigbeeVibrationSensor zbVibrationSensor = ZigbeeVibrationSensor(VIBRATION_SENSOR_ENDPOINT_NUMBER); +/* Preferences for storing ENROLLED flag to persist across reboots */ +Preferences preferences; + void setup() { Serial.begin(115200); + preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots + bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences + preferences.end(); + // Init button + sensor pinMode(button, INPUT_PULLUP); pinMode(sensor_pin, INPUT); @@ -67,6 +75,31 @@ void setup() { delay(100); } Serial.println(); + + // Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll + if (enrolled) { + Serial.println("Device has been enrolled before - restoring IAS Zone enrollment"); + zbVibrationSensor.restoreIASZoneEnroll(); + } else { + Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment"); + zbVibrationSensor.requestIASZoneEnroll(); + } + + while (!zbVibrationSensor.enrolled()) { + Serial.print("."); + delay(100); + } + Serial.println(); + Serial.println("Zigbee enrolled successfully!"); + + // Store ENROLLED flag only if this was a new enrollment (previous flag was false) + // Skip writing if we just restored enrollment (flag was already true) + if (!enrolled) { + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", true); // set ENROLLED flag to true + preferences.end(); + Serial.println("ENROLLED flag saved to preferences"); + } } void loop() { @@ -95,6 +128,11 @@ void loop() { if ((millis() - startTime) > 3000) { // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + // Clear the ENROLLED flag from preferences + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", false); // set ENROLLED flag to false + preferences.end(); + Serial.println("ENROLLED flag cleared from preferences"); delay(1000); Zigbee.factoryReset(); } diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 44067fc4886..c940d85d4d1 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -209,6 +209,9 @@ setIASClientEndpoint KEYWORD2 setClosed KEYWORD2 setOpen KEYWORD2 setTilted KEYWORD2 +requestIASZoneEnroll KEYWORD2 +restoreIASZoneEnroll KEYWORD2 +enrolled KEYWORD2 # ZigbeeVibrationSensor setVibration KEYWORD2 diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp index 4142f87fe16..35e05afb290 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp @@ -31,6 +31,7 @@ ZigbeeContactSwitch::ZigbeeContactSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom contact switch configuration zigbee_contact_switch_cfg_t contact_switch_cfg = ZIGBEE_DEFAULT_CONTACT_SWITCH_CONFIG(); @@ -44,15 +45,16 @@ void ZigbeeContactSwitch::setIASClientEndpoint(uint8_t ep_number) { } bool ZigbeeContactSwitch::setClosed() { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; log_v("Setting Contact switch to closed"); uint8_t closed = 0; // ALARM1 = 0, ALARM2 = 0 esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_set_attribute_val( + ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &closed, false ); esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to set contact switch to closed: 0x%x: %s", ret, esp_err_to_name(ret)); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set contact switch to closed: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); return false; } _zone_status = closed; @@ -60,15 +62,16 @@ bool ZigbeeContactSwitch::setClosed() { } bool ZigbeeContactSwitch::setOpen() { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; log_v("Setting Contact switch to open"); uint8_t open = ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM1 | ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM2; // ALARM1 = 1, ALARM2 = 1 esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_set_attribute_val( + ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &open, false ); esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to set contact switch to open: 0x%x: %s", ret, esp_err_to_name(ret)); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set contact switch to open: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); return false; } _zone_status = open; @@ -90,12 +93,8 @@ bool ZigbeeContactSwitch::report() { status_change_notif_cmd.delay = 0; esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to send IAS Zone status changed notification: 0x%x: %s", ret, esp_err_to_name(ret)); - return false; - } log_v("IAS Zone status changed notification sent"); return true; } @@ -115,11 +114,58 @@ void ZigbeeContactSwitch::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enro ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeContactSwitch::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_CONTACT_SWITCH; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeContactSwitch::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h index b3be38c3eb4..002ed722d78 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h @@ -70,12 +70,24 @@ class ZigbeeContactSwitch : public ZigbeeEP { // Report the contact switch value, done automatically after setting the position bool report(); + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } + private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; uint8_t _zone_status; uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp index 2ca032b01e5..2a3a5c80498 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp @@ -31,6 +31,7 @@ ZigbeeDoorWindowHandle::ZigbeeDoorWindowHandle(uint8_t endpoint) : ZigbeeEP(endp _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom door window handle configuration zigbee_door_window_handle_cfg_t door_window_handle_cfg = ZIGBEE_DEFAULT_DOOR_WINDOW_HANDLE_CONFIG(); @@ -108,9 +109,8 @@ bool ZigbeeDoorWindowHandle::report() { status_change_notif_cmd.zone_id = _zone_id; status_change_notif_cmd.delay = 0; - //NOTE: Check result of esp_zb_zcl_ias_zone_status_change_notif_cmd_req() and return true if success, false if failure esp_zb_lock_acquire(portMAX_DELAY); - esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); log_v("IAS Zone status changed notification sent"); return true; @@ -131,11 +131,58 @@ void ZigbeeDoorWindowHandle::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_e ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeDoorWindowHandle::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_DOOR_WINDOW_HANDLE; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeDoorWindowHandle::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h index d4a2d81eb39..cfaf7e772a0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h @@ -74,12 +74,24 @@ class ZigbeeDoorWindowHandle : public ZigbeeEP { // Report the door/window handle value, done automatically after setting the position bool report(); + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } + private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; uint8_t _zone_status; uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp index 218638ed3cb..a9fd437a2c6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp @@ -31,6 +31,7 @@ ZigbeeVibrationSensor::ZigbeeVibrationSensor(uint8_t endpoint) : ZigbeeEP(endpoi _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom vibration sensor configuration zigbee_vibration_sensor_cfg_t vibration_sensor_cfg = ZIGBEE_DEFAULT_VIBRATION_SENSOR_CONFIG(); @@ -57,11 +58,10 @@ bool ZigbeeVibrationSensor::setVibration(bool sensed) { return false; } _zone_status = vibration; - report(); - return true; + return report(); } -void ZigbeeVibrationSensor::report() { +bool ZigbeeVibrationSensor::report() { /* Send IAS Zone status changed notification command */ esp_zb_zcl_ias_zone_status_change_notif_cmd_t status_change_notif_cmd; @@ -75,9 +75,10 @@ void ZigbeeVibrationSensor::report() { status_change_notif_cmd.delay = 0; esp_zb_lock_acquire(portMAX_DELAY); - esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); log_v("IAS Zone status changed notification sent"); + return true; } void ZigbeeVibrationSensor::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { @@ -95,11 +96,58 @@ void ZigbeeVibrationSensor::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_en ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeVibrationSensor::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_VIBRATION_MOVEMENT; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeVibrationSensor::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h index 9757257b5c1..a819be0ba8f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h @@ -65,7 +65,18 @@ class ZigbeeVibrationSensor : public ZigbeeEP { bool setVibration(bool sensed); // Report the vibration sensor value, done automatically after setting the sensed value - void report(); + bool report(); + + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; @@ -73,6 +84,7 @@ class ZigbeeVibrationSensor : public ZigbeeEP { uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED