375 lines
13 KiB
C
375 lines
13 KiB
C
/* 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"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
#ifdef DEVICE_TYPE_RELAY
|
|
#include "switch_driver.h"
|
|
#endif
|
|
|
|
#ifdef DEVICE_TYPE_TEMP_SENSOR
|
|
#include "dht11_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 TaskHandle_t temp_sensor_task_handle = NULL;
|
|
|
|
/* Task para ler sensor DHT11 periodicamente */
|
|
static void temp_sensor_task(void *pvParameters)
|
|
{
|
|
dht11_reading_t reading;
|
|
TickType_t last_wake_time = xTaskGetTickCount();
|
|
|
|
while (1) {
|
|
/* Aguarda intervalo configurado */
|
|
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(SENSOR_UPDATE_INTERVAL));
|
|
|
|
/* Lê temperatura e umidade do DHT11 */
|
|
esp_err_t ret = dht11_read(&reading);
|
|
|
|
if (ret == ESP_OK && reading.valid) {
|
|
/* Converte temperatura para formato Zigbee (0.01°C) */
|
|
int16_t temp_zigbee = (int16_t)(reading.temperature * 100);
|
|
|
|
/* Converte umidade para formato Zigbee (0.01% RH) */
|
|
uint16_t humidity_zigbee = (uint16_t)(reading.humidity * 100);
|
|
|
|
/* Atualiza atributos Zigbee */
|
|
esp_zb_lock_acquire(portMAX_DELAY);
|
|
|
|
/* Atualiza temperatura */
|
|
esp_zb_zcl_set_attribute_val(
|
|
HA_ONOFF_SWITCH_ENDPOINT,
|
|
ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,
|
|
ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
|
|
ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID,
|
|
&temp_zigbee,
|
|
false
|
|
);
|
|
|
|
/* Atualiza umidade */
|
|
esp_zb_zcl_set_attribute_val(
|
|
HA_ONOFF_SWITCH_ENDPOINT,
|
|
ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT,
|
|
ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
|
|
ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID,
|
|
&humidity_zigbee,
|
|
false
|
|
);
|
|
|
|
esp_zb_lock_release();
|
|
|
|
ESP_LOGI(TAG, "Sensor updated - Temperature: %.1f°C (Zigbee: %d), Humidity: %.1f%% (Zigbee: %u)",
|
|
reading.temperature, temp_zigbee, reading.humidity, humidity_zigbee);
|
|
|
|
#if FEATURE_LED_INDICATOR
|
|
/* Pisca LED para indicar leitura */
|
|
led_indicator_set_state(LED_CONNECTED);
|
|
#endif
|
|
} else {
|
|
ESP_LOGW(TAG, "Failed to read DHT11 sensor: %s", esp_err_to_name(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
esp_zb_cluster_list_add_humidity_meas_cluster(cluster_list, device_create_humidity_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 não recebe comandos, apenas reporta */
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
|
|
static void temp_sensor_hardware_init(void)
|
|
{
|
|
ESP_LOGI(TAG, "Initializing DHT11 temperature sensor on GPIO %d", GPIO_SENSOR_DHT11);
|
|
|
|
/* Inicializa driver DHT11 */
|
|
esp_err_t ret = dht11_init(GPIO_SENSOR_DHT11);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to initialize DHT11: %s", esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
|
|
/* Cria task para leitura periódica */
|
|
xTaskCreate(
|
|
temp_sensor_task,
|
|
"temp_sensor",
|
|
4096,
|
|
NULL,
|
|
5,
|
|
&temp_sensor_task_handle
|
|
);
|
|
|
|
ESP_LOGI(TAG, "DHT11 sensor task started (update interval: %d ms)", SENSOR_UPDATE_INTERVAL);
|
|
}
|
|
|
|
static void temp_sensor_update_state(void)
|
|
{
|
|
/* A atualização é feita automaticamente pela task periódica */
|
|
}
|
|
|
|
static const device_type_t temp_sensor_device = {
|
|
.name = "Zigbee Temperature & Humidity Sensor DHT11",
|
|
.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_humidity_cluster(void)
|
|
{
|
|
esp_zb_humidity_meas_cluster_cfg_t humidity_cfg = {
|
|
.measured_value = ESP_ZB_ZCL_REL_HUMIDITY_MEASUREMENT_MEASURED_VALUE_DEFAULT,
|
|
.min_value = 0, /* 0% RH em unidades de 0.01% */
|
|
.max_value = 10000, /* 100% RH em unidades de 0.01% */
|
|
};
|
|
return esp_zb_humidity_meas_cluster_create(&humidity_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);
|
|
}
|