diff --git a/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/Zigbee_Occupancy_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/Zigbee_Occupancy_Sensor.ino index 46afdf3d273..a7208b744d2 100644 --- a/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/Zigbee_Occupancy_Sensor.ino +++ b/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/Zigbee_Occupancy_Sensor.ino @@ -34,7 +34,7 @@ #include "Zigbee.h" /* Zigbee occupancy sensor configuration */ -#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10 +#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1 uint8_t button = BOOT_PIN; uint8_t sensor_pin = 4; @@ -47,9 +47,24 @@ void setup() { pinMode(button, INPUT_PULLUP); pinMode(sensor_pin, INPUT); - // Optional: set Zigbee device name and model + // Set Zigbee device name and model zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor"); + // Optional: Set sensor type (PIR, Ultrasonic, PIR and Ultrasonic or Physical Contact) + zbOccupancySensor.setSensorType(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR, ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PIR); + + // Optional: Set occupied to unoccupied delay (if your sensor supports it) to PIR sensor as its set by setSensorType + zbOccupancySensor.setOccupiedToUnoccupiedDelay(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR, 1); // 1 second delay + + // Optional: Set unoccupied to occupied delay (if your sensor supports it) to PIR sensor as its set by setSensorType + zbOccupancySensor.setUnoccupiedToOccupiedDelay(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR, 1); // 1 second delay + + // Optional: Set unoccupied to occupied threshold (if your sensor supports it) to PIR sensor as its set by setSensorType + zbOccupancySensor.setUnoccupiedToOccupiedThreshold(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR, 1); // 1 movement event threshold + + // Optional: Set callback function for occupancy config change + zbOccupancySensor.onOccupancyConfigChange(occupancyConfigChange); + // Add endpoint to Zigbee Core Zigbee.addEndpoint(&zbOccupancySensor); @@ -101,3 +116,9 @@ void loop() { } delay(100); } + +// Callback function for occupancy config change +void occupancyConfigChange(ZigbeeOccupancySensorType sensor_type, uint16_t occ_to_unocc_delay, uint16_t unocc_to_occ_delay, uint8_t unocc_to_occ_threshold) { + // Handle sensor configuration here + Serial.printf("Occupancy config change: sensor type: %d, occ to unocc delay: %d, unocc to occ delay: %d, unocc to occ threshold: %d\n", sensor_type, occ_to_unocc_delay, unocc_to_occ_delay, unocc_to_occ_threshold); +} \ No newline at end of file diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp index b8f88fed4a4..7f8c4647779 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp @@ -22,8 +22,11 @@ ZigbeeOccupancySensor::ZigbeeOccupancySensor(uint8_t endpoint) : ZigbeeEP(endpoi _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0}; } -bool ZigbeeOccupancySensor::setSensorType(uint8_t sensor_type) { - uint8_t sensor_type_bitmap = 1 << sensor_type; +bool ZigbeeOccupancySensor::setSensorType(ZigbeeOccupancySensorType sensor_type, ZigbeeOccupancySensorTypeBitmap sensor_type_bitmap) { + uint8_t sensor_type_bitmap_value = sensor_type_bitmap; + if(sensor_type_bitmap == ZIGBEE_OCCUPANCY_SENSOR_BITMAP_DEFAULT) { + sensor_type_bitmap_value = (1 << sensor_type); // Default to single sensor type if bitmap is default + } esp_zb_attribute_list_t *occupancy_sens_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_err_t ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_OCCUPANCY_SENSOR_TYPE_ID, (void *)&sensor_type); @@ -31,11 +34,152 @@ bool ZigbeeOccupancySensor::setSensorType(uint8_t sensor_type) { log_e("Failed to set sensor type: 0x%x: %s", ret, esp_err_to_name(ret)); return false; } - ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_OCCUPANCY_SENSOR_TYPE_BITMAP_ID, (void *)&sensor_type_bitmap); + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_OCCUPANCY_SENSOR_TYPE_BITMAP_ID, (void *)&sensor_type_bitmap_value); if (ret != ESP_OK) { log_e("Failed to set sensor type bitmap: 0x%x: %s", ret, esp_err_to_name(ret)); return false; } + + // Handle PIR attributes if PIR bit is set (bit 0) + if (sensor_type_bitmap & ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PIR) { + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_OCC_TO_UNOCC_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_PIR_OCC_TO_UNOCC_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set PIR occupied to unoccupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set PIR unoccupied to occupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + uint8_t pir_threshold = ESP_ZB_ZCL_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_THRESHOLD_DEFAULT_VALUE; + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_THRESHOLD_ID, &pir_threshold); + if (ret != ESP_OK) { + log_e("Failed to set PIR unoccupied to occupied threshold: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + } + + // Handle Ultrasonic attributes if Ultrasonic bit is set (bit 1) + if (sensor_type_bitmap & ZIGBEE_OCCUPANCY_SENSOR_BITMAP_ULTRASONIC) { + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_OCCUPIED_TO_UNOCCUPIED_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_ULTRASONIC_OCCUPIED_TO_UNOCCUPIED_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set ultrasonic occupied to unoccupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set ultrasonic unoccupied to occupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + uint8_t ultrasonic_threshold = ESP_ZB_ZCL_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_DEFAULT_VALUE; + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID, &ultrasonic_threshold); + if (ret != ESP_OK) { + log_e("Failed to set ultrasonic unoccupied to occupied threshold: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + } + + // Handle Physical Contact attributes if Physical Contact bit is set (bit 2) + if (sensor_type_bitmap & ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PHYSICAL_CONTACT_AND_PIR) { + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_OCCUPIED_TO_UNOCCUPIED_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_PHYSICAL_CONTACT_OCCUPIED_TO_UNOCCUPIED_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set physical contact occupied to unoccupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_DELAY_ID, ESP_ZB_ZCL_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_DELAY_DEFAULT_VALUE); + if (ret != ESP_OK) { + log_e("Failed to set physical contact unoccupied to occupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + uint8_t physical_contact_threshold = ESP_ZB_ZCL_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_MIN_VALUE; + ret = esp_zb_occupancy_sensing_cluster_add_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID, &physical_contact_threshold); + if (ret != ESP_OK) { + log_e("Failed to set physical contact unoccupied to occupied threshold: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + } + return true; +} + +bool ZigbeeOccupancySensor::setOccupiedToUnoccupiedDelay(ZigbeeOccupancySensorType sensor_type, uint16_t delay) { + esp_zb_attribute_list_t *occupancy_sens_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + esp_err_t ret; + switch (sensor_type) { + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_OCC_TO_UNOCC_DELAY_ID, (void *)&delay); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_OCCUPIED_TO_UNOCCUPIED_DELAY_ID, (void *)&delay); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_OCCUPIED_TO_UNOCCUPIED_DELAY_ID, (void *)&delay); + break; + default: + log_e("Invalid sensor type for delay setting: 0x%x", sensor_type); + return false; + } + + if (ret != ESP_OK) { + log_e("Failed to set occupied to unoccupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeOccupancySensor::setUnoccupiedToOccupiedDelay(ZigbeeOccupancySensorType sensor_type, uint16_t delay) { + esp_zb_attribute_list_t *occupancy_sens_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + esp_err_t ret; + switch (sensor_type) { + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_DELAY_ID, (void *)&delay); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_DELAY_ID, (void *)&delay); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_DELAY_ID, (void *)&delay); + break; + default: + log_e("Invalid sensor type for delay setting: 0x%x", sensor_type); + return false; + } + + if (ret != ESP_OK) { + log_e("Failed to set unoccupied to occupied delay: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeOccupancySensor::setUnoccupiedToOccupiedThreshold(ZigbeeOccupancySensorType sensor_type, uint8_t threshold) { + esp_zb_attribute_list_t *occupancy_sens_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + esp_err_t ret; + switch (sensor_type) { + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_THRESHOLD_ID, (void *)&threshold); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID, (void *)&threshold); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT: + ret = esp_zb_cluster_update_attr(occupancy_sens_cluster, ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID, (void *)&threshold); + break; + default: + log_e("Invalid sensor type for threshold setting: 0x%x", sensor_type); + return false; + } + + if (ret != ESP_OK) { + log_e("Failed to set unoccupied to occupied threshold: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } return true; } @@ -77,4 +221,68 @@ bool ZigbeeOccupancySensor::report() { return true; } +//set attribute method -> method overridden in child class +void ZigbeeOccupancySensor::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING) { + //PIR + if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_OCC_TO_UNOCC_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _pir_occ_to_unocc_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _pir_unocc_to_occ_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PIR_UNOCC_TO_OCC_THRESHOLD_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + _pir_unocc_to_occ_threshold = *(uint8_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR); + } + //Ultrasonic + else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_OCCUPIED_TO_UNOCCUPIED_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _ultrasonic_occ_to_unocc_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _ultrasonic_unocc_to_occ_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + _ultrasonic_unocc_to_occ_threshold = *(uint8_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC); + } + //Physical Contact + else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_OCCUPIED_TO_UNOCCUPIED_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _physical_contact_occ_to_unocc_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_DELAY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _physical_contact_unocc_to_occ_delay = *(uint16_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT); + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_OCCUPANCY_SENSING_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + _physical_contact_unocc_to_occ_threshold = *(uint8_t *)message->attribute.data.value; + occupancyConfigChanged(ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for Occupancy Sensor endpoint", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Occupancy Sensor endpoint", message->info.cluster); + } +} + +void ZigbeeOccupancySensor::occupancyConfigChanged(ZigbeeOccupancySensorType sensor_type) { + if (_on_occupancy_config_change) { + switch (sensor_type) { + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR: + _on_occupancy_config_change(sensor_type, _pir_occ_to_unocc_delay, _pir_unocc_to_occ_delay, _pir_unocc_to_occ_threshold); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC: + _on_occupancy_config_change(sensor_type, _ultrasonic_occ_to_unocc_delay, _ultrasonic_unocc_to_occ_delay, _ultrasonic_unocc_to_occ_threshold); + break; + case ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT: + _on_occupancy_config_change(sensor_type, _physical_contact_occ_to_unocc_delay, _physical_contact_unocc_to_occ_delay, _physical_contact_unocc_to_occ_threshold); + break; + default: + log_e("Invalid sensor type for occupancy config change: 0x%x", sensor_type); + break; + } + } else { + log_w("No callback function set for occupancy config change"); + } +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h index 7408e10a76b..432f2b9257b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h @@ -30,6 +30,24 @@ } // clang-format on +enum ZigbeeOccupancySensorType{ + ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR = 0, + ZIGBEE_OCCUPANCY_SENSOR_TYPE_ULTRASONIC = 1, + ZIGBEE_OCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONIC = 2, + ZIGBEE_OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT = 3 +}; + +enum ZigbeeOccupancySensorTypeBitmap{ + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PIR = 0x01, + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_ULTRASONIC = 0x02, + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PIR_AND_ULTRASONIC = 0x03, + // ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PHYSICAL_CONTACT = 0x04, // No info in cluster specification R8 + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PHYSICAL_CONTACT_AND_PIR = 0x05, + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PHYSICAL_CONTACT_AND_ULTRASONIC = 0x06, + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_PHYSICAL_CONTACT_AND_PIR_AND_ULTRASONIC = 0x07, + ZIGBEE_OCCUPANCY_SENSOR_BITMAP_DEFAULT = 0xff +}; + typedef struct zigbee_occupancy_sensor_cfg_s { esp_zb_basic_cluster_cfg_t basic_cfg; esp_zb_identify_cluster_cfg_t identify_cfg; @@ -44,11 +62,47 @@ class ZigbeeOccupancySensor : public ZigbeeEP { // Set the occupancy value. True for occupied, false for unoccupied bool setOccupancy(bool occupied); - // Set the sensor type, see esp_zb_zcl_occupancy_sensing_occupancy_sensor_type_t - bool setSensorType(uint8_t sensor_type); + // Set the sensor type, see ZigbeeOccupancySensorType + bool setSensorType(ZigbeeOccupancySensorType sensor_type, ZigbeeOccupancySensorTypeBitmap sensor_type_bitmap = ZIGBEE_OCCUPANCY_SENSOR_BITMAP_DEFAULT); + + // Set the occupied to unoccupied delay + // Specifies the time delay, in seconds, before the sensor changes to its unoccupied state after the last detection of movement in the sensed area. + bool setOccupiedToUnoccupiedDelay(ZigbeeOccupancySensorType sensor_type, uint16_t delay); + + // Set the unoccupied to occupied delay + // Specifies the time delay, in seconds, before the sensor changes to its occupied state after the detection of movement in the sensed area. + bool setUnoccupiedToOccupiedDelay(ZigbeeOccupancySensorType sensor_type, uint16_t delay); + + // Set the unoccupied to occupied threshold + // Specifies the number of movement detection events that must occur in the period unoccupied to occupied delay, before the sensor changes to its occupied state. + bool setUnoccupiedToOccupiedThreshold(ZigbeeOccupancySensorType sensor_type, uint8_t threshold); + void onOccupancyConfigChange(void (*callback)(ZigbeeOccupancySensorType sensor_type, uint16_t occ_to_unocc_delay, uint16_t unocc_to_occ_delay, uint8_t unocc_to_occ_threshold)) { + _on_occupancy_config_change = callback; + } // Report the occupancy value bool report(); + +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + + void (*_on_occupancy_config_change)(ZigbeeOccupancySensorType sensor_type, uint16_t occ_to_unocc_delay, uint16_t unocc_to_occ_delay, uint8_t unocc_to_occ_threshold); + void occupancyConfigChanged(ZigbeeOccupancySensorType sensor_type); + + // PIR sensor configuration + uint16_t _pir_occ_to_unocc_delay; + uint16_t _pir_unocc_to_occ_delay; + uint8_t _pir_unocc_to_occ_threshold; + + // Ultrasonic sensor configuration + uint16_t _ultrasonic_occ_to_unocc_delay; + uint16_t _ultrasonic_unocc_to_occ_delay; + uint8_t _ultrasonic_unocc_to_occ_threshold; + + // Physical contact sensor configuration + uint16_t _physical_contact_occ_to_unocc_delay; + uint16_t _physical_contact_unocc_to_occ_delay; + uint8_t _physical_contact_unocc_to_occ_threshold; }; #endif // CONFIG_ZB_ENABLED