/* Device Registry - Implementação de diferentes tipos de dispositivos Zigbee */ #include "device_registry.h" #include "zcl/esp_zigbee_zcl_common.h" #include "esp_log.h" #include "esp_zb_switch.h" #ifdef DEVICE_TYPE_RELAY #include "switch_driver.h" #endif #if FEATURE_LED_INDICATOR #include "led_indicator.h" #endif static const char *TAG = "DEVICE_REGISTRY"; /* ========================= IMPLEMENTAÇÕES DE DISPOSITIVOS ========================= */ #ifdef DEVICE_TYPE_RELAY /* ===== RELÉ CONTROLÁVEL (ON/OFF LIGHT) ===== */ static esp_zb_cluster_list_t* relay_create_clusters(void) { esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(cluster_list, device_create_basic_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, device_create_identify_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_on_off_cluster(cluster_list, device_create_onoff_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } static esp_err_t relay_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) { if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF && message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) { bool on_off = message->attribute.data.value ? *(bool *)message->attribute.data.value : 0; ESP_LOGI(TAG, "Relay command received: %s", on_off ? "ON" : "OFF"); relay_set_state(on_off); #if FEATURE_LED_INDICATOR led_indicator_set_state(on_off ? LED_RELAY_ON : LED_RELAY_OFF); #endif return ESP_OK; } return ESP_ERR_NOT_FOUND; } static void relay_hardware_init(void) { ESP_LOGI(TAG, "Initializing relay hardware (GPIO %d)", GPIO_OUTPUT_RELAY); relay_driver_init(); } static void relay_update_state(void) { /* Relé é controlado por comandos Zigbee, não precisa polling */ } static const device_type_t relay_device = { .name = "Zigbee Relay", .device_id = ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID, .endpoint = HA_ONOFF_SWITCH_ENDPOINT, .create_clusters = relay_create_clusters, .attribute_handler = relay_attribute_handler, .hardware_init = relay_hardware_init, .update_state = relay_update_state, }; #endif /* DEVICE_TYPE_RELAY */ /* ========================= SENSOR DE TEMPERATURA ========================= */ #ifdef DEVICE_TYPE_TEMP_SENSOR static esp_zb_cluster_list_t* temp_sensor_create_clusters(void) { esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(cluster_list, device_create_basic_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, device_create_identify_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_temperature_meas_cluster(cluster_list, device_create_temperature_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } static esp_err_t temp_sensor_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) { /* Sensor de temperatura geralmente não recebe comandos, apenas reporta */ return ESP_ERR_NOT_FOUND; } static void temp_sensor_hardware_init(void) { ESP_LOGI(TAG, "Initializing temperature sensor (I2C SDA:%d SCL:%d)", GPIO_SENSOR_I2C_SDA, GPIO_SENSOR_I2C_SCL); /* TODO: Implementar inicialização do sensor I2C */ /* Exemplo: inicializar AHT20, SHT30, BME280, etc. */ } static void temp_sensor_update_state(void) { /* TODO: Ler temperatura do sensor e atualizar atributo Zigbee */ /* Exemplo: * float temp = read_temperature_sensor(); * int16_t temp_zigbee = (int16_t)(temp * 100); // Zigbee usa 0.01°C * esp_zb_zcl_set_attribute_val(..., ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temp_zigbee, false); */ } static const device_type_t temp_sensor_device = { .name = "Zigbee Temperature Sensor", .device_id = ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID, .endpoint = HA_ONOFF_SWITCH_ENDPOINT, .create_clusters = temp_sensor_create_clusters, .attribute_handler = temp_sensor_attribute_handler, .hardware_init = temp_sensor_hardware_init, .update_state = temp_sensor_update_state, }; #endif /* DEVICE_TYPE_TEMP_SENSOR */ /* ========================= SENSOR DE PORTA/JANELA ========================= */ #ifdef DEVICE_TYPE_DOOR_SENSOR static esp_zb_cluster_list_t* door_sensor_create_clusters(void) { esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(cluster_list, device_create_basic_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, device_create_identify_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_ias_zone_cluster(cluster_list, device_create_ias_zone_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } static esp_err_t door_sensor_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) { /* Sensor IAS Zone não recebe comandos, apenas reporta */ return ESP_ERR_NOT_FOUND; } static void door_sensor_hardware_init(void) { ESP_LOGI(TAG, "Initializing door sensor (Reed Switch GPIO %d)", GPIO_SENSOR_REED); /* TODO: Configurar GPIO do reed switch com pull-up e interrupção */ } static void door_sensor_update_state(void) { /* TODO: Ler estado do reed switch e atualizar IAS Zone status */ } static const device_type_t door_sensor_device = { .name = "Zigbee Door/Window Sensor", .device_id = ESP_ZB_HA_IAS_ZONE_DEVICE_ID, .endpoint = HA_ONOFF_SWITCH_ENDPOINT, .create_clusters = door_sensor_create_clusters, .attribute_handler = door_sensor_attribute_handler, .hardware_init = door_sensor_hardware_init, .update_state = door_sensor_update_state, }; #endif /* DEVICE_TYPE_DOOR_SENSOR */ /* ========================= SENSOR DE MOVIMENTO ========================= */ #ifdef DEVICE_TYPE_MOTION_SENSOR static esp_zb_cluster_list_t* motion_sensor_create_clusters(void) { esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(cluster_list, device_create_basic_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, device_create_identify_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_occupancy_sensing_cluster(cluster_list, device_create_occupancy_cluster(), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } static esp_err_t motion_sensor_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) { /* Sensor de ocupação não recebe comandos, apenas reporta */ return ESP_ERR_NOT_FOUND; } static void motion_sensor_hardware_init(void) { ESP_LOGI(TAG, "Initializing motion sensor (PIR GPIO %d)", GPIO_SENSOR_PIR); /* TODO: Configurar GPIO do PIR com interrupção */ } static void motion_sensor_update_state(void) { /* TODO: Ler estado do PIR e atualizar occupancy */ } static const device_type_t motion_sensor_device = { .name = "Zigbee Motion Sensor", .device_id = ESP_ZB_HA_OCCUPANCY_SENSOR_DEVICE_ID, .endpoint = HA_ONOFF_SWITCH_ENDPOINT, .create_clusters = motion_sensor_create_clusters, .attribute_handler = motion_sensor_attribute_handler, .hardware_init = motion_sensor_hardware_init, .update_state = motion_sensor_update_state, }; #endif /* DEVICE_TYPE_MOTION_SENSOR */ /* ========================= SELETOR DE DISPOSITIVO ========================= */ const device_type_t* device_get_config(void) { #ifdef DEVICE_TYPE_RELAY return &relay_device; #elif defined(DEVICE_TYPE_TEMP_SENSOR) return &temp_sensor_device; #elif defined(DEVICE_TYPE_DOOR_SENSOR) return &door_sensor_device; #elif defined(DEVICE_TYPE_MOTION_SENSOR) return &motion_sensor_device; #else #error "Nenhum tipo de dispositivo definido!" return NULL; #endif } /* ========================= FUNÇÕES AUXILIARES PARA CLUSTERS ========================= */ esp_zb_attribute_list_t* device_create_basic_cluster(void) { esp_zb_basic_cluster_cfg_t basic_cfg = { .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, }; return esp_zb_basic_cluster_create(&basic_cfg); } esp_zb_attribute_list_t* device_create_identify_cluster(void) { esp_zb_identify_cluster_cfg_t identify_cfg = { .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, }; return esp_zb_identify_cluster_create(&identify_cfg); } esp_zb_attribute_list_t* device_create_onoff_cluster(void) { esp_zb_on_off_cluster_cfg_t on_off_cfg = { .on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE, }; return esp_zb_on_off_cluster_create(&on_off_cfg); } esp_zb_attribute_list_t* device_create_temperature_cluster(void) { esp_zb_temperature_meas_cluster_cfg_t temp_cfg = { .measured_value = ESP_ZB_ZCL_TEMP_MEASUREMENT_MEASURED_VALUE_DEFAULT, .min_value = -5000, /* -50°C em unidades de 0.01°C */ .max_value = 12500, /* 125°C em unidades de 0.01°C */ }; return esp_zb_temperature_meas_cluster_create(&temp_cfg); } esp_zb_attribute_list_t* device_create_occupancy_cluster(void) { esp_zb_occupancy_sensing_cluster_cfg_t occupancy_cfg = { .occupancy = 0, .sensor_type = ESP_ZB_ZCL_OCCUPANCY_SENSING_OCCUPANCY_SENSOR_TYPE_PIR, }; return esp_zb_occupancy_sensing_cluster_create(&occupancy_cfg); } esp_zb_attribute_list_t* device_create_ias_zone_cluster(void) { esp_zb_ias_zone_cluster_cfg_t ias_zone_cfg = { .zone_state = ESP_ZB_ZCL_IAS_ZONE_ZONESTATE_NOT_ENROLLED, .zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_CONTACT_SWITCH, .zone_status = 0, }; return esp_zb_ias_zone_cluster_create(&ias_zone_cfg); }