#include "zh_gateway.h"

gateway_config_t gateway_main_config = {0};

void app_main(void)
{
    gateway_config_t *gateway_config = &gateway_main_config;
    nvs_flash_init();
    esp_netif_init();
    esp_event_loop_create_default();
    zh_load_config(gateway_config);
    if (gateway_config->software_config.is_lan_mode == true)
    {
        gpio_config_t config = {0};
        config.intr_type = GPIO_INTR_DISABLE;
        config.mode = GPIO_MODE_OUTPUT;
        config.pin_bit_mask = (1ULL << ZH_LAN_MODULE_POWER_PIN);
        config.pull_down_en = GPIO_PULLDOWN_DISABLE;
        config.pull_up_en = GPIO_PULLUP_DISABLE;
        gpio_config(&config);
        gpio_set_level(ZH_LAN_MODULE_POWER_PIN, 1);
        esp_netif_config_t esp_netif_config = ESP_NETIF_DEFAULT_ETH();
        esp_netif_t *esp_netif_eth = esp_netif_new(&esp_netif_config);
        eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
        eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
        eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
        esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
        esp_eth_phy_t *phy = ZH_LAN_MODULE_TYPE(&phy_config);
        esp_eth_config_t esp_eth_config = ETH_DEFAULT_CONFIG(mac, phy);
        esp_eth_handle_t esp_eth_handle = NULL;
        esp_eth_driver_install(&esp_eth_config, &esp_eth_handle);
        esp_netif_attach(esp_netif_eth, esp_eth_new_netif_glue(esp_eth_handle));
        esp_event_handler_instance_register(ETH_EVENT, ESP_EVENT_ANY_ID, &zh_eth_event_handler, gateway_config, NULL);
        esp_event_handler_instance_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &zh_eth_event_handler, gateway_config, NULL);
        esp_eth_start(esp_eth_handle);
        wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
        esp_wifi_init(&wifi_init_config);
        esp_wifi_set_mode(WIFI_MODE_STA);
        esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR);
        esp_wifi_start();
        esp_read_mac(gateway_config->self_mac, ESP_MAC_WIFI_STA);
    }
    else
    {
        esp_netif_create_default_wifi_sta();
        wifi_init_config_t wifi_init_sta_config = WIFI_INIT_CONFIG_DEFAULT();
        esp_wifi_init(&wifi_init_sta_config);
        wifi_config_t wifi_config = {0};
        memcpy(wifi_config.sta.ssid, gateway_config->software_config.ssid_name, strlen(gateway_config->software_config.ssid_name));
        memcpy(wifi_config.sta.password, gateway_config->software_config.ssid_password, strlen(gateway_config->software_config.ssid_password));
        esp_wifi_set_mode(WIFI_MODE_APSTA);
        esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR);
        esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
        esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &zh_wifi_event_handler, gateway_config, NULL);
        esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &zh_wifi_event_handler, gateway_config, NULL);
        esp_wifi_start();
        esp_read_mac(gateway_config->self_mac, ESP_MAC_WIFI_SOFTAP);
    }
#ifdef CONFIG_NETWORK_TYPE_DIRECT
    zh_espnow_init_config_t zh_espnow_init_config = ZH_ESPNOW_INIT_CONFIG_DEFAULT();
    zh_espnow_init_config.queue_size = 128;
    if (gateway_config->software_config.is_lan_mode == false)
    {
        zh_espnow_init_config.wifi_interface = WIFI_IF_AP;
    }
    zh_espnow_init(&zh_espnow_init_config);
    esp_event_handler_instance_register(ZH_EVENT, ESP_EVENT_ANY_ID, &zh_espnow_event_handler, gateway_config, NULL);
#else
    zh_network_init_config_t zh_network_init_config = ZH_NETWORK_INIT_CONFIG_DEFAULT();
    zh_network_init_config.queue_size = 128;
    if (gateway_config->software_config.is_lan_mode == false)
    {
        zh_network_init_config.wifi_interface = WIFI_IF_AP;
    }
    zh_network_init(&zh_network_init_config);
    esp_event_handler_instance_register(ZH_EVENT, ESP_EVENT_ANY_ID, &zh_espnow_event_handler, gateway_config, NULL);
#endif
    gateway_config->espnow_ota_data_semaphore = xSemaphoreCreateBinary();
    gateway_config->self_ota_in_progress_mutex = xSemaphoreCreateMutex();
    gateway_config->espnow_ota_in_progress_mutex = xSemaphoreCreateMutex();
    gateway_config->device_check_in_progress_mutex = xSemaphoreCreateMutex();
    zh_vector_init(&gateway_config->available_device_vector, sizeof(available_device_t), false);
    const esp_partition_t *running = esp_ota_get_running_partition();
    esp_ota_img_states_t ota_state = {0};
    esp_ota_get_state_partition(running, &ota_state);
    if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
    {
        vTaskDelay(60000 / portTICK_PERIOD_MS);
        esp_ota_mark_app_valid_cancel_rollback();
    }
}

void zh_load_config(gateway_config_t *gateway_config)
{
    nvs_handle_t nvs_handle = 0;
    nvs_open("config", NVS_READWRITE, &nvs_handle);
    uint8_t config_is_present = 0;
    if (nvs_get_u8(nvs_handle, "present", &config_is_present) == ESP_ERR_NVS_NOT_FOUND)
    {
        nvs_set_u8(nvs_handle, "present", 0xFE);
        nvs_close(nvs_handle);
    SETUP_INITIAL_SETTINGS:
#ifdef CONFIG_CONNECTION_TYPE_LAN
        gateway_config->software_config.is_lan_mode = true;
        strcpy(gateway_config->software_config.ssid_name, "NULL");
        strcpy(gateway_config->software_config.ssid_password, "NULL");
#else
        gateway_config->software_config.is_lan_mode = false;
        strcpy(gateway_config->software_config.ssid_name, CONFIG_WIFI_SSID_NAME);
        strcpy(gateway_config->software_config.ssid_password, CONFIG_WIFI_PASSWORD);
#endif
        strcpy(gateway_config->software_config.mqtt_broker_url, CONFIG_MQTT_BROKER_URL);
        strcpy(gateway_config->software_config.mqtt_topic_prefix, CONFIG_MQTT_TOPIC_PREFIX);
#ifdef CONFIG_SYSLOG_SERVER_USING
        gateway_config->software_config.is_syslog_server_usage = true;
        strcpy(gateway_config->software_config.syslog_server_ip, CONFIG_SYSLOG_SERVER_IP);
        gateway_config->software_config.syslog_server_port = CONFIG_SYSLOG_SERVER_PORT;
#else
        gateway_config->software_config.is_syslog_server_usage = false;
        strcpy(gateway_config->software_config.syslog_server_ip, "NULL");
        gateway_config->software_config.syslog_server_port = 0;
#endif
#ifdef CONFIG_NTP_SERVER_USING
        gateway_config->software_config.is_ntp_server_usage = true;
        strcpy(gateway_config->software_config.ntp_server_url, CONFIG_NTP_SERVER_URL);
        strcpy(gateway_config->software_config.ntp_time_zone, CONFIG_NTP_TIME_ZONE);
#else
        gateway_config->software_config.is_ntp_server_usage = false;
        strcpy(gateway_config->software_config.ntp_server_url, "NULL");
        strcpy(gateway_config->software_config.ntp_time_zone, "NULL");
#endif
#ifdef CONFIG_FIRMWARE_UPGRADE_SERVER_USING
        gateway_config->software_config.is_ota_server_usage = true;
        strcpy(gateway_config->software_config.firmware_upgrade_url, CONFIG_FIRMWARE_UPGRADE_URL);
#else
        gateway_config->software_config.is_ota_server_usage = false;
        strcpy(gateway_config->software_config.firmware_upgrade_url, "NULL");
#endif
        zh_save_config(gateway_config);
        return;
    }
    esp_err_t err = ESP_OK;
    size_t size = 0;
    err += nvs_get_u8(nvs_handle, "lan_mode", (uint8_t *)&gateway_config->software_config.is_lan_mode);
    err += nvs_get_str(nvs_handle, "ssid_name", NULL, &size);
    err += nvs_get_str(nvs_handle, "ssid_name", gateway_config->software_config.ssid_name, &size);
    err += nvs_get_str(nvs_handle, "ssid_password", NULL, &size);
    err += nvs_get_str(nvs_handle, "ssid_password", gateway_config->software_config.ssid_password, &size);
    err += nvs_get_str(nvs_handle, "mqtt_url", NULL, &size);
    err += nvs_get_str(nvs_handle, "mqtt_url", gateway_config->software_config.mqtt_broker_url, &size);
    err += nvs_get_str(nvs_handle, "topic_prefix", NULL, &size);
    err += nvs_get_str(nvs_handle, "topic_prefix", gateway_config->software_config.mqtt_topic_prefix, &size);
    err += nvs_get_u8(nvs_handle, "syslog_usage", (uint8_t *)&gateway_config->software_config.is_syslog_server_usage);
    err += nvs_get_str(nvs_handle, "syslog_ip", NULL, &size);
    err += nvs_get_str(nvs_handle, "syslog_ip", gateway_config->software_config.syslog_server_ip, &size);
    err += nvs_get_u32(nvs_handle, "syslog_port", &gateway_config->software_config.syslog_server_port);
    err += nvs_get_u8(nvs_handle, "ntp_usage", (uint8_t *)&gateway_config->software_config.is_ntp_server_usage);
    err += nvs_get_str(nvs_handle, "ntp_url", NULL, &size);
    err += nvs_get_str(nvs_handle, "ntp_url", gateway_config->software_config.ntp_server_url, &size);
    err += nvs_get_str(nvs_handle, "time_zone", NULL, &size);
    err += nvs_get_str(nvs_handle, "time_zone", gateway_config->software_config.ntp_time_zone, &size);
    err += nvs_get_u8(nvs_handle, "ota_usage", (uint8_t *)&gateway_config->software_config.is_ota_server_usage);
    err += nvs_get_str(nvs_handle, "upgrade_url", NULL, &size);
    err += nvs_get_str(nvs_handle, "upgrade_url", gateway_config->software_config.firmware_upgrade_url, &size);
    nvs_close(nvs_handle);
    if (err != ESP_OK)
    {
        goto SETUP_INITIAL_SETTINGS;
    }
}

void zh_save_config(const gateway_config_t *gateway_config)
{
    nvs_handle_t nvs_handle = 0;
    nvs_open("config", NVS_READWRITE, &nvs_handle);
    nvs_set_u8(nvs_handle, "lan_mode", gateway_config->software_config.is_lan_mode);
    nvs_set_str(nvs_handle, "ssid_name", gateway_config->software_config.ssid_name);
    nvs_set_str(nvs_handle, "ssid_password", gateway_config->software_config.ssid_password);
    nvs_set_str(nvs_handle, "mqtt_url", gateway_config->software_config.mqtt_broker_url);
    nvs_set_str(nvs_handle, "topic_prefix", gateway_config->software_config.mqtt_topic_prefix);
    nvs_set_u8(nvs_handle, "syslog_usage", gateway_config->software_config.is_syslog_server_usage);
    nvs_set_str(nvs_handle, "syslog_ip", gateway_config->software_config.syslog_server_ip);
    nvs_set_u32(nvs_handle, "syslog_port", gateway_config->software_config.syslog_server_port);
    nvs_set_u8(nvs_handle, "ntp_usage", gateway_config->software_config.is_ntp_server_usage);
    nvs_set_str(nvs_handle, "ntp_url", gateway_config->software_config.ntp_server_url);
    nvs_set_str(nvs_handle, "time_zone", gateway_config->software_config.ntp_time_zone);
    nvs_set_u8(nvs_handle, "ota_usage", gateway_config->software_config.is_ota_server_usage);
    nvs_set_str(nvs_handle, "upgrade_url", gateway_config->software_config.firmware_upgrade_url);
    nvs_close(nvs_handle);
}

void zh_eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    gateway_config_t *gateway_config = arg;
    switch (event_id)
    {
    case ETHERNET_EVENT_DISCONNECTED:
        if (gateway_config->mqtt_is_enable == true)
        {
            esp_mqtt_client_stop(gateway_config->mqtt_client);
            gateway_config->mqtt_is_enable = false;
        }
        if (gateway_config->software_config.is_ntp_server_usage == true)
        {
            if (gateway_config->sntp_is_enable == true)
            {
                esp_sntp_stop();
                gateway_config->sntp_is_enable = false;
                vTaskDelete(gateway_config->gateway_current_time_task);
            }
        }
        if (gateway_config->software_config.is_syslog_server_usage == true)
        {
            if (gateway_config->syslog_is_enable == true)
            {
                zh_syslog_deinit();
                gateway_config->syslog_is_enable = false;
            }
        }
        break;
    case IP_EVENT_ETH_GOT_IP:
        if (gateway_config->mqtt_is_enable == false)
        {
            esp_mqtt_client_config_t mqtt_config = {0};
            mqtt_config.buffer.size = 2048;
            mqtt_config.broker.address.uri = gateway_config->software_config.mqtt_broker_url;
            gateway_config->mqtt_client = esp_mqtt_client_init(&mqtt_config);
            esp_mqtt_client_register_event(gateway_config->mqtt_client, ESP_EVENT_ANY_ID, zh_mqtt_event_handler, gateway_config);
            esp_mqtt_client_start(gateway_config->mqtt_client);
            gateway_config->mqtt_is_enable = true;
        }
        if (gateway_config->software_config.is_ntp_server_usage == true)
        {
            if (gateway_config->sntp_is_enable == false)
            {
                esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
                sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
                esp_sntp_setservername(0, gateway_config->software_config.ntp_server_url);
                esp_sntp_init();
                gateway_config->sntp_is_enable = true;
                xTaskCreatePinnedToCore(&zh_send_espnow_current_time_task, "NULL", ZH_SNTP_STACK_SIZE, gateway_config, ZH_SNTP_TASK_PRIORITY, (TaskHandle_t *)&gateway_config->gateway_current_time_task, tskNO_AFFINITY);
            }
        }
        if (gateway_config->software_config.is_syslog_server_usage == true)
        {
            zh_syslog_init_config_t syslog_init_config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
            memcpy(syslog_init_config.syslog_ip, gateway_config->software_config.syslog_server_ip, strlen(gateway_config->software_config.syslog_server_ip));
            syslog_init_config.syslog_port = gateway_config->software_config.syslog_server_port;
            zh_syslog_init(&syslog_init_config);
            gateway_config->syslog_is_enable = true;
        }
        break;
    default:
        break;
    }
}

void zh_wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    gateway_config_t *gateway_config = arg;
    switch (event_id)
    {
    case WIFI_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case WIFI_EVENT_STA_DISCONNECTED:
        if (gateway_config->mqtt_is_enable == true)
        {
            esp_mqtt_client_stop(gateway_config->mqtt_client);
            gateway_config->mqtt_is_enable = false;
        }
        if (gateway_config->software_config.is_ntp_server_usage == true)
        {
            if (gateway_config->sntp_is_enable == true)
            {
                esp_sntp_stop();
                gateway_config->sntp_is_enable = false;
                vTaskDelete(gateway_config->gateway_current_time_task);
            }
        }
        if (gateway_config->software_config.is_syslog_server_usage == true)
        {
            if (gateway_config->syslog_is_enable == true)
            {
                zh_syslog_deinit();
                gateway_config->syslog_is_enable = false;
            }
        }
        if (gateway_config->wifi_reconnect_retry_num < ZH_WIFI_MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            ++gateway_config->wifi_reconnect_retry_num;
        }
        else
        {
            gateway_config->wifi_reconnect_retry_num = 0;
            esp_timer_create_args_t wifi_reconnect_timer_args = {
                .callback = (void *)esp_wifi_connect};
            esp_timer_create(&wifi_reconnect_timer_args, &gateway_config->wifi_reconnect_timer);
            esp_timer_start_once(gateway_config->wifi_reconnect_timer, ZH_WIFI_RECONNECT_TIME * 1000);
        }
        break;
    case IP_EVENT_STA_GOT_IP:
        gateway_config->wifi_reconnect_retry_num = 0;
        if (gateway_config->mqtt_is_enable == false)
        {
            esp_mqtt_client_config_t mqtt_config = {0};
            mqtt_config.buffer.size = 2048;
            mqtt_config.broker.address.uri = gateway_config->software_config.mqtt_broker_url;
            gateway_config->mqtt_client = esp_mqtt_client_init(&mqtt_config);
            esp_mqtt_client_register_event(gateway_config->mqtt_client, ESP_EVENT_ANY_ID, zh_mqtt_event_handler, gateway_config);
            esp_mqtt_client_start(gateway_config->mqtt_client);
            gateway_config->mqtt_is_enable = true;
        }
        if (gateway_config->software_config.is_ntp_server_usage == true)
        {
            if (gateway_config->sntp_is_enable == false)
            {
                esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
                sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
                esp_sntp_setservername(0, gateway_config->software_config.ntp_server_url);
                esp_sntp_init();
                gateway_config->sntp_is_enable = true;
                xTaskCreatePinnedToCore(&zh_send_espnow_current_time_task, "NULL", ZH_SNTP_STACK_SIZE, gateway_config, ZH_SNTP_TASK_PRIORITY, (TaskHandle_t *)&gateway_config->gateway_current_time_task, tskNO_AFFINITY);
            }
        }
        if (gateway_config->software_config.is_syslog_server_usage == true)
        {
            zh_syslog_init_config_t syslog_init_config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
            memcpy(syslog_init_config.syslog_ip, gateway_config->software_config.syslog_server_ip, strlen(gateway_config->software_config.syslog_server_ip));
            syslog_init_config.syslog_port = gateway_config->software_config.syslog_server_port;
            zh_syslog_init(&syslog_init_config);
            gateway_config->syslog_is_enable = true;
        }
        break;
    default:
        break;
    }
}

void zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    gateway_config_t *gateway_config = arg;
    switch (event_id)
    {
#ifdef CONFIG_NETWORK_TYPE_DIRECT
    case ZH_ESPNOW_ON_RECV_EVENT:
        zh_espnow_event_on_recv_t *recv_data = event_data;
        if (recv_data->data_len != sizeof(zh_espnow_data_t))
        {
            goto ZH_ESPNOW_EVENT_HANDLER_EXIT;
        }
#else
    case ZH_NETWORK_ON_RECV_EVENT:
        zh_network_event_on_recv_t *recv_data = event_data;
        if (recv_data->data_len != sizeof(zh_espnow_data_t))
        {
            goto ZH_NETWORK_EVENT_HANDLER_EXIT;
        }
#endif
        zh_espnow_data_t *data = (zh_espnow_data_t *)recv_data->data;
        char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(data->device_type)) + 20, MALLOC_CAP_8BIT);
        memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(data->device_type)) + 20);
        sprintf(topic, "%s/%s/" MAC_STR, gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(data->device_type), MAC2STR(recv_data->mac_addr));
        switch (data->payload_type)
        {
        case ZHPT_ATTRIBUTES:
            zh_espnow_send_mqtt_json_attributes_message(data, recv_data->mac_addr, gateway_config);
            break;
        case ZHPT_KEEP_ALIVE:
            if (xSemaphoreTake(gateway_config->device_check_in_progress_mutex, portTICK_PERIOD_MS) == pdTRUE)
            {
                bool is_found = false;
                for (uint16_t i = 0; i < zh_vector_get_size(&gateway_config->available_device_vector); ++i)
                {
                    available_device_t *available_device = zh_vector_get_item(&gateway_config->available_device_vector, i);
                    if (memcmp(recv_data->mac_addr, available_device->mac_addr, 6) == 0)
                    {
                        zh_vector_delete_item(&gateway_config->available_device_vector, i);
                        is_found = true;
                    }
                }
                if (gateway_config->syslog_is_enable == true && is_found == false)
                {
                    char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                    memset(mac, 0, 18);
                    sprintf(mac, "" MAC_STR "", MAC2STR(recv_data->mac_addr));
                    zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(data->device_type), "Connected to gateway.");
                    heap_caps_free(mac);
                }
                available_device_t available_device = {0};
                available_device.device_type = data->device_type;
                memcpy(available_device.mac_addr, recv_data->mac_addr, 6);
                available_device.frequency = data->payload_data.keep_alive_message.message_frequency;
                available_device.time = esp_timer_get_time() / 1000000;
                zh_vector_push_back(&gateway_config->available_device_vector, &available_device);
                xSemaphoreGive(gateway_config->device_check_in_progress_mutex);
            }
            zh_espnow_send_mqtt_json_keep_alive_message(data, recv_data->mac_addr, gateway_config);
            break;
        case ZHPT_CONFIG:
            switch (data->device_type)
            {
            case ZHDT_SWITCH:
                zh_espnow_switch_send_mqtt_json_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_LED:
                zh_espnow_led_send_mqtt_json_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_SENSOR:
                zh_espnow_sensor_send_mqtt_json_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_BINARY_SENSOR:
                zh_espnow_binary_sensor_send_mqtt_json_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            default:
                break;
            }
            break;
        case ZHPT_HARDWARE:
            switch (data->device_type)
            {
            case ZHDT_SWITCH:
                zh_espnow_switch_send_mqtt_json_hardware_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_LED:
                zh_espnow_led_send_mqtt_json_hardware_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_SENSOR:
                zh_espnow_sensor_send_mqtt_json_hardware_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_BINARY_SENSOR:
                zh_espnow_binary_sensor_send_mqtt_json_hardware_config_message(data, recv_data->mac_addr, gateway_config);
                break;
            default:
                break;
            }
            break;
        case ZHPT_STATE:
            switch (data->device_type)
            {
            case ZHDT_SWITCH:
                zh_espnow_switch_send_mqtt_json_status_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_LED:
                zh_espnow_led_send_mqtt_json_status_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_SENSOR:
                zh_espnow_sensor_send_mqtt_json_status_message(data, recv_data->mac_addr, gateway_config);
                break;
            case ZHDT_BINARY_SENSOR:
                zh_espnow_binary_sensor_send_mqtt_json_status_message(data, recv_data->mac_addr, gateway_config);
                break;
            default:
                break;
            }
            break;
        case ZHPT_UPDATE:
            if (xSemaphoreTake(gateway_config->espnow_ota_in_progress_mutex, portTICK_PERIOD_MS) == pdTRUE)
            {
                gateway_config->espnow_ota_data.device_type = data->device_type;
                memcpy(gateway_config->espnow_ota_data.app_name, data->payload_data.ota_message.espnow_ota_data.app_name, sizeof(data->payload_data.ota_message.espnow_ota_data.app_name));
                memcpy(gateway_config->espnow_ota_data.app_version, data->payload_data.ota_message.espnow_ota_data.app_version, sizeof(data->payload_data.ota_message.espnow_ota_data.app_version));
                memcpy(gateway_config->espnow_ota_data.mac_addr, recv_data->mac_addr, 6);
                xTaskCreatePinnedToCore(&zh_espnow_ota_update_task, "NULL", ZH_OTA_STACK_SIZE, gateway_config, ZH_OTA_TASK_PRIORITY, NULL, tskNO_AFFINITY);
                xSemaphoreGive(gateway_config->espnow_ota_in_progress_mutex);
            }
            break;
        case ZHPT_UPDATE_PROGRESS:
            xSemaphoreGive(gateway_config->espnow_ota_data_semaphore);
            break;
        case ZHPT_UPDATE_FAIL:
            esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_fail", 0, 2, true);
            if (gateway_config->syslog_is_enable == true)
            {
                char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                memset(mac, 0, 18);
                sprintf(mac, "" MAC_STR "", MAC2STR(recv_data->mac_addr));
                zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(data->device_type), "Firmware update fail. Incorrect bin file.");
                heap_caps_free(mac);
            }
            break;
        case ZHPT_UPDATE_SUCCESS:
            esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_success", 0, 2, true);
            if (gateway_config->syslog_is_enable == true)
            {
                char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                memset(mac, 0, 18);
                sprintf(mac, "" MAC_STR "", MAC2STR(recv_data->mac_addr));
                zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(data->device_type), "Firmware update success.");
                heap_caps_free(mac);
            }
            break;
        case ZHPT_ERROR:
            if (gateway_config->syslog_is_enable == true)
            {
                char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                memset(mac, 0, 18);
                sprintf(mac, "" MAC_STR "", MAC2STR(recv_data->mac_addr));
                zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(data->device_type), data->payload_data.status_message.error_message.message);
                heap_caps_free(mac);
            }
            break;
        default:
            break;
        }
        heap_caps_free(topic);
#ifdef CONFIG_NETWORK_TYPE_DIRECT
    ZH_ESPNOW_EVENT_HANDLER_EXIT:
        heap_caps_free(recv_data->data);
        break;
    case ZH_ESPNOW_ON_SEND_EVENT:
        break;
#else
    ZH_NETWORK_EVENT_HANDLER_EXIT:
        heap_caps_free(recv_data->data);
        break;
    case ZH_NETWORK_ON_SEND_EVENT:
        break;
#endif
    default:
        break;
    }
}

void zh_mqtt_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    gateway_config_t *gateway_config = arg;
    esp_mqtt_event_handle_t event = event_data;
    switch (event_id)
    {
    case MQTT_EVENT_CONNECTED:
        if (gateway_config->mqtt_is_connected == false)
        {
            if (gateway_config->syslog_is_enable == true)
            {
                char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                memset(mac, 0, 18);
                sprintf(mac, "" MAC_STR "", MAC2STR(gateway_config->self_mac));
                zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Connected to MQTT.");
                heap_caps_free(mac);
            }
            char *topic_for_subscribe = NULL;
            char *supported_device_type = NULL;
            for (zh_device_type_t i = 1; i <= ZHDT_MAX; ++i)
            {
                supported_device_type = zh_get_device_type_value_name(i);
                topic_for_subscribe = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(supported_device_type) + 4, MALLOC_CAP_8BIT);
                memset(topic_for_subscribe, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(supported_device_type) + 4);
                sprintf(topic_for_subscribe, "%s/%s/#", gateway_config->software_config.mqtt_topic_prefix, supported_device_type);
                esp_mqtt_client_subscribe(gateway_config->mqtt_client, topic_for_subscribe, 2);
                heap_caps_free(topic_for_subscribe);
            }
            zh_gateway_send_mqtt_json_config_message(gateway_config);
            xTaskCreatePinnedToCore(&zh_gateway_send_mqtt_json_attributes_message_task, "NULL", ZH_MESSAGE_STACK_SIZE, gateway_config, ZH_MESSAGE_TASK_PRIORITY, (TaskHandle_t *)&gateway_config->gateway_attributes_message_task, tskNO_AFFINITY);
            xTaskCreatePinnedToCore(&zh_gateway_send_mqtt_json_keep_alive_message_task, "NULL", ZH_MESSAGE_STACK_SIZE, gateway_config, ZH_MESSAGE_TASK_PRIORITY, (TaskHandle_t *)&gateway_config->gateway_keep_alive_message_task, tskNO_AFFINITY);
            xTaskCreatePinnedToCore(&zh_device_availability_check_task, "NULL", ZH_CHECK_STACK_SIZE, gateway_config, ZH_CHECK_TASK_PRIORITY, (TaskHandle_t *)&gateway_config->device_availability_check_task, tskNO_AFFINITY);
        }
        gateway_config->mqtt_is_connected = true;
        break;
    case MQTT_EVENT_DISCONNECTED:
        if (gateway_config->mqtt_is_connected == true)
        {
            if (gateway_config->syslog_is_enable == true)
            {
                char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                memset(mac, 0, 18);
                sprintf(mac, "" MAC_STR "", MAC2STR(gateway_config->self_mac));
                zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Disconnected from MQTT.");
                heap_caps_free(mac);
            }
            vTaskDelete(gateway_config->gateway_attributes_message_task);
            vTaskDelete(gateway_config->gateway_keep_alive_message_task);
            vTaskDelete(gateway_config->device_availability_check_task);
            zh_espnow_data_t data = {0};
            data.device_type = ZHDT_GATEWAY;
            data.payload_type = ZHPT_KEEP_ALIVE;
            data.payload_data.keep_alive_message.online_status = ZH_OFFLINE;
            zh_send_message(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t));
        }
        gateway_config->mqtt_is_connected = false;
        break;
    case MQTT_EVENT_DATA:
        char incoming_topic[(event->topic_len) + 1];
        memset(incoming_topic, 0, sizeof(incoming_topic));
        strncat(incoming_topic, event->topic, event->topic_len);
        uint8_t incoming_data_mac[6] = {0};
        zh_device_type_t incoming_data_device_type = ZHDT_NONE;
        zh_payload_type_t incoming_data_payload_type = ZHPT_NONE;
        char *extracted_topic_data = strtok(incoming_topic, "/"); // Extract topic prefix.
        if (extracted_topic_data == NULL)
        {
            break;
        }
        extracted_topic_data = strtok(NULL, "/"); // Extract device type.
        if (extracted_topic_data == NULL)
        {
            break;
        }
        for (zh_device_type_t i = 1; i < ZHDT_MAX; ++i)
        {
            if (strncmp(extracted_topic_data, zh_get_device_type_value_name(i), strlen(extracted_topic_data) + 1) == 0)
            {
                incoming_data_device_type = i;
                break;
            }
        }
        extracted_topic_data = strtok(NULL, "/"); // Extract MAC address.
        if (extracted_topic_data == NULL)
        {
            break;
        }
        sscanf(extracted_topic_data, "%hhX-%hhX-%hhX-%hhX-%hhX-%hhX", &incoming_data_mac[0], &incoming_data_mac[1], &incoming_data_mac[2], &incoming_data_mac[3], &incoming_data_mac[4], &incoming_data_mac[5]);
        extracted_topic_data = strtok(NULL, "/"); // Extract payload type.
        if (extracted_topic_data != NULL)
        {
            for (zh_payload_type_t i = 1; i < ZHPT_MAX; ++i)
            {
                if (strncmp(extracted_topic_data, zh_get_payload_type_value_name(i), strlen(extracted_topic_data) + 1) == 0)
                {
                    incoming_data_payload_type = i;
                    break;
                }
            }
        }
        char incoming_payload[(event->data_len) + 1];
        memset(incoming_payload, 0, sizeof(incoming_payload));
        strncat(incoming_payload, event->data, event->data_len);
        zh_espnow_data_t data = {0};
        data.device_type = ZHDT_GATEWAY;
        switch (incoming_data_device_type)
        {
        case ZHDT_GATEWAY:
            if (incoming_data_payload_type == ZHPT_NONE)
            {
                if (memcmp(gateway_config->self_mac, incoming_data_mac, 6) == 0)
                {
                    if (strncmp(incoming_payload, "update", strlen(incoming_payload) + 1) == 0 && gateway_config->software_config.is_ota_server_usage == true)
                    {
                        if (xSemaphoreTake(gateway_config->self_ota_in_progress_mutex, portTICK_PERIOD_MS) == pdTRUE)
                        {
                            xTaskCreatePinnedToCore(&zh_self_ota_update_task, "NULL", ZH_OTA_STACK_SIZE, gateway_config, ZH_OTA_TASK_PRIORITY, NULL, tskNO_AFFINITY);
                            xSemaphoreGive(gateway_config->self_ota_in_progress_mutex);
                        }
                    }
                    else if (strncmp(incoming_payload, "restart", strlen(incoming_payload) + 1) == 0)
                    {
                        zh_espnow_data_t data = {0};
                        data.device_type = ZHDT_GATEWAY;
                        data.payload_type = ZHPT_KEEP_ALIVE;
                        data.payload_data.keep_alive_message.online_status = ZH_OFFLINE;
                        zh_send_message(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                        vTaskDelay(1000 / portTICK_PERIOD_MS);
                        esp_restart();
                    }
                    else
                    {
                        break;
                    }
                }
            }
            break;
        case ZHDT_SWITCH:
            switch (incoming_data_payload_type)
            {
            case ZHPT_NONE:
                if (strncmp(incoming_payload, "update", strlen(incoming_payload) + 1) == 0 && gateway_config->software_config.is_ota_server_usage == true)
                {
                    data.payload_type = ZHPT_UPDATE;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else if (strncmp(incoming_payload, "restart", strlen(incoming_payload) + 1) == 0)
                {
                    data.payload_type = ZHPT_RESTART;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else
                {
                    break;
                }
                break;
            case ZHPT_SET:
                for (ha_on_off_type_t i = 1; i < HAONOFT_MAX; ++i)
                {
                    if (strncmp(incoming_payload, zh_get_on_off_type_value_name(i), strlen(incoming_payload) + 1) == 0)
                    {
                        data.payload_data.status_message.switch_status_message.status = i;
                        data.payload_type = ZHPT_SET;
                        zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                        break;
                    }
                }
                break;
            case ZHPT_HARDWARE:
                char *extracted_hardware_data = strtok(incoming_payload, ","); // Extract relay gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.relay_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract relay on level value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.relay_on_level = zh_bool_value_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract led gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.led_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract led on level value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.led_on_level = zh_bool_value_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract internal button GPIO number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.int_button_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract internal button on level value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.int_button_on_level = zh_bool_value_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract external button GPIO number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.ext_button_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract external button on level value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.ext_button_on_level = zh_bool_value_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor GPIO number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.sensor_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor type value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.switch_hardware_config_message.sensor_type = zh_sensor_type_check(atoi(extracted_hardware_data));
                data.payload_type = ZHPT_HARDWARE;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            default:
                break;
            }
            break;
        case ZHDT_LED:
            switch (incoming_data_payload_type)
            {
            case ZHPT_NONE:
                if (strncmp(incoming_payload, "update", strlen(incoming_payload) + 1) == 0 && gateway_config->software_config.is_ota_server_usage == true)
                {
                    data.payload_type = ZHPT_UPDATE;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else if (strncmp(incoming_payload, "restart", strlen(incoming_payload) + 1) == 0)
                {
                    data.payload_type = ZHPT_RESTART;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else
                {
                    break;
                }
                break;
            case ZHPT_SET:
                for (ha_on_off_type_t i = 1; i < HAONOFT_MAX; ++i)
                {
                    if (strncmp(incoming_payload, zh_get_on_off_type_value_name(i), strlen(incoming_payload) + 1) == 0)
                    {
                        data.payload_data.status_message.led_status_message.status = i;
                        data.payload_type = ZHPT_SET;
                        zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                        break;
                    }
                }
                break;
            case ZHPT_BRIGHTNESS:
                data.payload_data.status_message.led_status_message.brightness = atoi(incoming_payload);
                data.payload_type = ZHPT_BRIGHTNESS;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            case ZHPT_TEMPERATURE:
                data.payload_data.status_message.led_status_message.temperature = atoi(incoming_payload);
                data.payload_type = ZHPT_TEMPERATURE;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            case ZHPT_RGB:
                char *extracted_rgb_data = strtok(incoming_payload, ","); // Extract red value.
                if (extracted_rgb_data == NULL)
                {
                    break;
                }
                data.payload_data.status_message.led_status_message.red = atoi(extracted_rgb_data);
                extracted_rgb_data = strtok(NULL, ","); // Extract green value.
                if (extracted_rgb_data == NULL)
                {
                    break;
                }
                data.payload_data.status_message.led_status_message.green = atoi(extracted_rgb_data);
                extracted_rgb_data = strtok(NULL, ","); // Extract blue value.
                if (extracted_rgb_data == NULL)
                {
                    break;
                }
                data.payload_data.status_message.led_status_message.blue = atoi(extracted_rgb_data);
                data.payload_type = ZHPT_RGB;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            case ZHPT_HARDWARE:
                char *extracted_hardware_data = strtok(incoming_payload, ","); // Extract led type value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.led_type = zh_led_type_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract first white gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.first_white_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract second white gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.second_white_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract red gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.red_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract green gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.green_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract blue gpio number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.led_hardware_config_message.blue_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                data.payload_type = ZHPT_HARDWARE;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            default:
                break;
            }
            break;
        case ZHDT_SENSOR:
            switch (incoming_data_payload_type)
            {
            case ZHPT_NONE:
                if (strncmp(incoming_payload, "update", strlen(incoming_payload) + 1) == 0 && gateway_config->software_config.is_ota_server_usage == true)
                {
                    data.payload_type = ZHPT_UPDATE;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else if (strncmp(incoming_payload, "restart", strlen(incoming_payload) + 1) == 0)
                {
                    data.payload_type = ZHPT_RESTART;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else
                {
                    break;
                }
                break;
            case ZHPT_HARDWARE:
                char *extracted_hardware_data = strtok(incoming_payload, ","); // Extract sensor type value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.sensor_type = zh_sensor_type_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor GPIO number 1 value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.sensor_pin_1 = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor GPIO number 2 value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.sensor_pin_2 = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor power control GPIO number value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.power_pin = zh_gpio_number_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract sensor measurement frequency value.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.measurement_frequency = zh_uint16_value_check(atoi(extracted_hardware_data));
                extracted_hardware_data = strtok(NULL, ","); // Extract power mode.
                if (extracted_hardware_data == NULL)
                {
                    break;
                }
                data.payload_data.config_message.sensor_hardware_config_message.battery_power = zh_bool_value_check(atoi(extracted_hardware_data));
                data.payload_type = ZHPT_HARDWARE;
                zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                break;
            default:
                break;
            }
            break;
        case ZHDT_BINARY_SENSOR:
            switch (incoming_data_payload_type)
            {
            case ZHPT_NONE:
                if (strncmp(incoming_payload, "update", strlen(incoming_payload) + 1) == 0 && gateway_config->software_config.is_ota_server_usage == true)
                {
                    data.payload_type = ZHPT_UPDATE;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else if (strncmp(incoming_payload, "restart", strlen(incoming_payload) + 1) == 0)
                {
                    data.payload_type = ZHPT_RESTART;
                    zh_send_message(incoming_data_mac, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                }
                else
                {
                    break;
                }
                break;
            default:
                break;
            }
            break;
        default:
            break;
        }
        break;
    }
}

void zh_self_ota_update_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
    memset(mac, 0, 18);
    sprintf(mac, "" MAC_STR "", MAC2STR(gateway_config->self_mac));
    xSemaphoreTake(gateway_config->self_ota_in_progress_mutex, portMAX_DELAY);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(ZHDT_GATEWAY)) + 20, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(ZHDT_GATEWAY)) + 20);
    sprintf(topic, "%s/%s/" MAC_STR, gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(ZHDT_GATEWAY), MAC2STR(gateway_config->self_mac));
    char self_ota_write_data[1025] = {0};
    esp_ota_handle_t update_handle = {0};
    const esp_partition_t *update_partition = NULL;
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_begin", 0, 2, true);
    if (gateway_config->syslog_is_enable == true)
    {
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Firmware update begin.");
    }
    const esp_app_desc_t *app_info = esp_app_get_description();
    char *app_name = (char *)heap_caps_malloc(strlen(gateway_config->software_config.firmware_upgrade_url) + strlen(app_info->project_name) + 6, MALLOC_CAP_8BIT);
    memset(app_name, 0, strlen(gateway_config->software_config.firmware_upgrade_url) + strlen(app_info->project_name) + 6);
    sprintf(app_name, "%s/%s.bin", gateway_config->software_config.firmware_upgrade_url, app_info->project_name);
    esp_http_client_config_t config = {
        .url = app_name,
        .cert_pem = (char *)server_certificate_pem_start,
        .timeout_ms = 5000,
        .keep_alive_enable = true,
        .skip_cert_common_name_check = true,
    };
    esp_http_client_handle_t https_client = esp_http_client_init(&config);
    heap_caps_free(app_name);
    esp_http_client_open(https_client, 0);
    esp_http_client_fetch_headers(https_client);
    update_partition = esp_ota_get_next_update_partition(NULL);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_progress", 0, 2, true);
    if (gateway_config->syslog_is_enable == true)
    {
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Firmware update progress.");
    }
    esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
    for (;;)
    {
        int data_read_size = esp_http_client_read(https_client, self_ota_write_data, 1024);
        if (data_read_size < 0)
        {
            esp_http_client_close(https_client);
            esp_http_client_cleanup(https_client);
            esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_error_data_size", 0, 2, true);
            if (gateway_config->syslog_is_enable == true)
            {
                zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Firmware update error. Incorrect size of read data.");
            }
            heap_caps_free(topic);
            heap_caps_free(mac);
            xSemaphoreGive(gateway_config->self_ota_in_progress_mutex);
            vTaskDelete(NULL);
        }
        else if (data_read_size > 0)
        {
            esp_ota_write(update_handle, (const void *)self_ota_write_data, data_read_size);
        }
        else
        {
            break;
        }
    }
    if (esp_ota_end(update_handle) != ESP_OK)
    {
        esp_http_client_close(https_client);
        esp_http_client_cleanup(https_client);
        esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_fail", 0, 2, true);
        if (gateway_config->syslog_is_enable == true)
        {
            zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Firmware update fail. Incorrect bin file.");
        }
        heap_caps_free(topic);
        heap_caps_free(mac);
        xSemaphoreGive(gateway_config->self_ota_in_progress_mutex);
        vTaskDelete(NULL);
    }
    esp_ota_set_boot_partition(update_partition);
    esp_http_client_close(https_client);
    esp_http_client_cleanup(https_client);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_success", 0, 2, true);
    if (gateway_config->syslog_is_enable == true)
    {
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Firmware update success.");
    }
    heap_caps_free(topic);
    heap_caps_free(mac);
    zh_espnow_data_t data = {0};
    data.device_type = ZHDT_GATEWAY;
    data.payload_type = ZHPT_KEEP_ALIVE;
    data.payload_data.keep_alive_message.online_status = ZH_OFFLINE;
    zh_send_message(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t));
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    esp_restart();
}

void zh_espnow_ota_update_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
    memset(mac, 0, 18);
    sprintf(mac, "" MAC_STR "", MAC2STR(gateway_config->espnow_ota_data.mac_addr));
    // heap_caps_free(mac);
    xSemaphoreTake(gateway_config->espnow_ota_in_progress_mutex, portMAX_DELAY);
    zh_espnow_data_t data = {0};
    data.device_type = ZHDT_GATEWAY;
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type)) + 20, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type)) + 20);
    sprintf(topic, "%s/%s/" MAC_STR, gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), MAC2STR(gateway_config->espnow_ota_data.mac_addr));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_begin", 0, 2, true);
    if (gateway_config->syslog_is_enable == true)
    {
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update begin.");
    }
    char espnow_ota_write_data[sizeof(data.payload_data.ota_message.espnow_ota_message.data) + 1] = {0};
    char *app_name = (char *)heap_caps_malloc(strlen(gateway_config->software_config.firmware_upgrade_url) + strlen(gateway_config->espnow_ota_data.app_name) + 6, MALLOC_CAP_8BIT);
    memset(app_name, 0, strlen(gateway_config->software_config.firmware_upgrade_url) + strlen(gateway_config->espnow_ota_data.app_name) + 6);
    sprintf(app_name, "%s/%s.bin", gateway_config->software_config.firmware_upgrade_url, gateway_config->espnow_ota_data.app_name);
    esp_http_client_config_t config = {
        .url = app_name,
        .cert_pem = (char *)server_certificate_pem_start,
        .timeout_ms = 5000,
        .keep_alive_enable = true,
        .skip_cert_common_name_check = true,
    };
    esp_http_client_handle_t https_client = esp_http_client_init(&config);
    heap_caps_free(app_name);
    esp_http_client_open(https_client, 0);
    esp_http_client_fetch_headers(https_client);
    data.payload_type = ZHPT_UPDATE_BEGIN;
    zh_send_message(gateway_config->espnow_ota_data.mac_addr, (uint8_t *)&data, sizeof(zh_espnow_data_t));
    if (xSemaphoreTake(gateway_config->espnow_ota_data_semaphore, 30000 / portTICK_PERIOD_MS) != pdTRUE)
    {
        esp_http_client_close(https_client);
        esp_http_client_cleanup(https_client);
        data.payload_type = ZHPT_UPDATE_ERROR;
        zh_send_message(gateway_config->espnow_ota_data.mac_addr, (uint8_t *)&data, sizeof(zh_espnow_data_t));
        esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_error_begin_timeout", 0, 2, true);
        if (gateway_config->syslog_is_enable == true)
        {
            zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update error. Timeout exceed.");
        }
        heap_caps_free(mac);
        heap_caps_free(topic);
        xSemaphoreGive(gateway_config->espnow_ota_in_progress_mutex);
        vTaskDelete(NULL);
    }
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_progress", 0, 2, true);
    if (gateway_config->syslog_is_enable == true)
    {
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update progress.");
    }
    for (;;)
    {
        int data_read_size = esp_http_client_read(https_client, espnow_ota_write_data, sizeof(data.payload_data.ota_message.espnow_ota_message.data));
        if (data_read_size < 0)
        {
            esp_http_client_close(https_client);
            esp_http_client_cleanup(https_client);
            esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_error_data_size", 0, 2, true);
            if (gateway_config->syslog_is_enable == true)
            {
                zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update error. Incorrect size of read data.");
            }
            heap_caps_free(mac);
            heap_caps_free(topic);
            xSemaphoreGive(gateway_config->espnow_ota_in_progress_mutex);
            vTaskDelete(NULL);
        }
        else if (data_read_size > 0)
        {
            data.payload_data.ota_message.espnow_ota_message.data_len = data_read_size;
            ++data.payload_data.ota_message.espnow_ota_message.part;
            memcpy(data.payload_data.ota_message.espnow_ota_message.data, espnow_ota_write_data, data_read_size);
            data.payload_type = ZHPT_UPDATE_PROGRESS;
            zh_send_message(gateway_config->espnow_ota_data.mac_addr, (uint8_t *)&data, sizeof(zh_espnow_data_t));
            if (xSemaphoreTake(gateway_config->espnow_ota_data_semaphore, 15000 / portTICK_PERIOD_MS) != pdTRUE)
            {
                esp_http_client_close(https_client);
                esp_http_client_cleanup(https_client);
                data.payload_type = ZHPT_UPDATE_ERROR;
                zh_send_message(gateway_config->espnow_ota_data.mac_addr, (uint8_t *)&data, sizeof(zh_espnow_data_t));
                esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_error_progress_timeout", 0, 2, true);
                if (gateway_config->syslog_is_enable == true)
                {
                    zh_syslog_send(ZH_USER, ZH_ERR, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update error. Timeout exceed.");
                }
                heap_caps_free(mac);
                heap_caps_free(topic);
                xSemaphoreGive(gateway_config->espnow_ota_in_progress_mutex);
                vTaskDelete(NULL);
            }
        }
        else
        {
            esp_http_client_close(https_client);
            esp_http_client_cleanup(https_client);
            data.payload_type = ZHPT_UPDATE_END;
            zh_send_message(gateway_config->espnow_ota_data.mac_addr, (uint8_t *)&data, sizeof(zh_espnow_data_t));
            esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_end", 0, 2, true);
            if (gateway_config->syslog_is_enable == true)
            {
                zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(gateway_config->espnow_ota_data.device_type), "Firmware update end.");
            }
            heap_caps_free(mac);
            heap_caps_free(topic);
            xSemaphoreGive(gateway_config->espnow_ota_in_progress_mutex);
            vTaskDelete(NULL);
        }
    }
}

void zh_send_espnow_current_time_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED)
    {
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
    if (gateway_config->syslog_is_enable == true)
    {
        char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
        memset(mac, 0, 18);
        sprintf(mac, "" MAC_STR "", MAC2STR(gateway_config->self_mac));
        zh_syslog_send(ZH_USER, ZH_INFO, mac, zh_get_device_type_value_name(ZHDT_GATEWAY), "Connected to NTP.");
        heap_caps_free(mac);
    }
    time_t now;
    setenv("TZ", gateway_config->software_config.ntp_time_zone, 1);
    tzset();
    for (;;)
    {
        time(&now);
        // To Do.
        vTaskDelay(86400000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

void zh_device_availability_check_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    for (;;)
    {
        xSemaphoreTake(gateway_config->device_check_in_progress_mutex, portMAX_DELAY);
        for (uint16_t i = 0; i < zh_vector_get_size(&gateway_config->available_device_vector); ++i)
        {
        CHECK:
            available_device_t *available_device = zh_vector_get_item(&gateway_config->available_device_vector, i);
            if (available_device == NULL)
            {
                break;
            }
            if (esp_timer_get_time() / 1000000 > available_device->time + (available_device->frequency * 1.25)) // + 25% just in case.
            {
                char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(available_device->device_type)) + 27, MALLOC_CAP_8BIT);
                memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(available_device->device_type)) + 27);
                sprintf(topic, "%s/%s/" MAC_STR "/status", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(available_device->device_type), MAC2STR(available_device->mac_addr));
                esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "offline", 0, 2, true);
                heap_caps_free(topic);
                if (gateway_config->syslog_is_enable == true)
                {
                    char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
                    memset(mac, 0, 18);
                    sprintf(mac, "" MAC_STR "", MAC2STR(available_device->mac_addr));
                    zh_syslog_send(ZH_USER, ZH_WARNING, mac, zh_get_device_type_value_name(available_device->device_type), "Disconnected from gateway.");
                    heap_caps_free(mac);
                }
                zh_vector_delete_item(&gateway_config->available_device_vector, i);
                goto CHECK; // Since the vector is shifted after item deletion, the item needs to be re-checked.
            }
        }
        xSemaphoreGive(gateway_config->device_check_in_progress_mutex);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

uint8_t zh_gpio_number_check(int gpio)
{
    return (gpio >= 0 && gpio <= ZH_MAX_GPIO_NUMBERS) ? gpio : ZH_NOT_USED;
}

bool zh_bool_value_check(int value)
{
    return (value == 0) ? false : true;
}

uint8_t zh_sensor_type_check(int type)
{
    return (type > HAST_NONE && type < HAST_MAX) ? type : HAST_NONE;
}

uint8_t zh_led_type_check(int type)
{
    return (type > HALT_NONE && type < HALT_MAX) ? type : HALT_NONE;
}

uint16_t zh_uint16_value_check(int value)
{
    return (value >= 0 && value <= 65536) ? value : 60;
}

void zh_gateway_send_mqtt_json_attributes_message_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    const esp_app_desc_t *app_info = esp_app_get_description();
    zh_espnow_data_t data = {0};
    data.device_type = ZHDT_GATEWAY;
    data.payload_type = ZHPT_ATTRIBUTES;
    data.payload_data.attributes_message.chip_type = ZH_CHIP_TYPE;
    strcpy(data.payload_data.attributes_message.flash_size, CONFIG_ESPTOOLPY_FLASHSIZE);
    data.payload_data.attributes_message.cpu_frequency = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
    data.payload_data.attributes_message.reset_reason = (uint8_t)esp_reset_reason();
    strcpy(data.payload_data.attributes_message.app_name, app_info->project_name);
    strcpy(data.payload_data.attributes_message.app_version, app_info->version);
    for (;;)
    {
        data.payload_data.attributes_message.heap_size = esp_get_free_heap_size();
        data.payload_data.attributes_message.min_heap_size = esp_get_minimum_free_heap_size();
        data.payload_data.attributes_message.uptime = esp_timer_get_time() / 1000000;
        zh_espnow_send_mqtt_json_attributes_message(&data, gateway_config->self_mac, gateway_config);
        vTaskDelay(60000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

void zh_gateway_send_mqtt_json_config_message(const gateway_config_t *gateway_config)
{
    zh_espnow_data_t data = {0};
    data.device_type = ZHDT_GATEWAY;
    data.payload_type = ZHPT_CONFIG;
    data.payload_data.config_message.binary_sensor_config_message.unique_id = 1;
    data.payload_data.config_message.binary_sensor_config_message.binary_sensor_device_class = HABSDC_CONNECTIVITY;
    data.payload_data.config_message.binary_sensor_config_message.payload_on = HAONOFT_CONNECT;
    data.payload_data.config_message.binary_sensor_config_message.payload_off = HAONOFT_NONE;
    data.payload_data.config_message.binary_sensor_config_message.expire_after = 30;
    data.payload_data.config_message.binary_sensor_config_message.enabled_by_default = true;
    data.payload_data.config_message.binary_sensor_config_message.force_update = true;
    data.payload_data.config_message.binary_sensor_config_message.qos = 2;
    data.payload_data.config_message.binary_sensor_config_message.retain = true;
    zh_espnow_binary_sensor_send_mqtt_json_config_message(&data, gateway_config->self_mac, gateway_config);
}

void zh_gateway_send_mqtt_json_keep_alive_message_task(void *pvParameter)
{
    gateway_config_t *gateway_config = pvParameter;
    zh_espnow_data_t data = {0};
    data.device_type = ZHDT_GATEWAY;
    data.payload_data.keep_alive_message.online_status = ZH_ONLINE;
    data.payload_data.status_message.binary_sensor_status_message.sensor_type = HAST_GATEWAY;
    data.payload_data.status_message.binary_sensor_status_message.connect = HAONOFT_CONNECT;
    for (;;)
    {
        data.payload_type = ZHPT_KEEP_ALIVE;
        zh_send_message(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t));
        data.payload_type = ZHPT_STATE;
        zh_espnow_binary_sensor_send_mqtt_json_status_message(&data, gateway_config->self_mac, gateway_config);
        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

void zh_espnow_send_mqtt_json_attributes_message(zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    uint32_t secs = device_data->payload_data.attributes_message.uptime;
    uint32_t mins = secs / 60;
    uint32_t hours = mins / 60;
    uint32_t days = hours / 24;
    char *mac = (char *)heap_caps_malloc(18, MALLOC_CAP_8BIT);
    memset(mac, 0, 18);
    sprintf(mac, "" MAC_STR "", MAC2STR(device_mac));
    char *uptime = (char *)heap_caps_malloc(44, MALLOC_CAP_8BIT);
    memset(uptime, 0, 44);
    sprintf(uptime, "Days:%lu Hours:%lu Mins:%lu", days, hours - (days * 24), mins - (hours * 60));
    char *cpu_frequency = heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(cpu_frequency, 0, 4);
    sprintf(cpu_frequency, "%d", device_data->payload_data.attributes_message.cpu_frequency);
    char *heap_size = heap_caps_malloc(7, MALLOC_CAP_8BIT);
    memset(heap_size, 0, 7);
    sprintf(heap_size, "%lu", device_data->payload_data.attributes_message.heap_size);
    char *min_heap_size = heap_caps_malloc(7, MALLOC_CAP_8BIT);
    memset(min_heap_size, 0, 7);
    sprintf(min_heap_size, "%lu", device_data->payload_data.attributes_message.min_heap_size);
    char *reset_reason = heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(reset_reason, 0, 4);
    sprintf(reset_reason, "%d", device_data->payload_data.attributes_message.reset_reason);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "Type", zh_get_device_type_value_name(device_data->device_type));
    zh_json_add(&json, "MAC", mac);
    zh_json_add(&json, "Chip", zh_get_chip_type_value_name(device_data->payload_data.attributes_message.chip_type));
    if (device_data->payload_data.attributes_message.sensor_type != 0)
    {
        zh_json_add(&json, "Sensor", zh_get_sensor_type_value_name(device_data->payload_data.attributes_message.sensor_type));
    }
    zh_json_add(&json, "CPU frequency", cpu_frequency);
    zh_json_add(&json, "Flash size", device_data->payload_data.attributes_message.flash_size);
    zh_json_add(&json, "Current heap size", heap_size);
    zh_json_add(&json, "Minimum heap size", min_heap_size);
    zh_json_add(&json, "Last reset reason", reset_reason);
    zh_json_add(&json, "App name", device_data->payload_data.attributes_message.app_name);
    zh_json_add(&json, "App version", device_data->payload_data.attributes_message.app_version);
    zh_json_add(&json, "Uptime", uptime);
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(topic, "%s/%s/" MAC_STR "/attributes", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(mac);
    heap_caps_free(uptime);
    heap_caps_free(cpu_frequency);
    heap_caps_free(heap_size);
    heap_caps_free(min_heap_size);
    heap_caps_free(reset_reason);
    heap_caps_free(topic);
}

void zh_espnow_send_mqtt_json_keep_alive_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *status = (device_data->payload_data.keep_alive_message.online_status == ZH_ONLINE) ? "online" : "offline";
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(topic, "%s/%s/" MAC_STR "/status", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, status, 0, 2, true);
    heap_caps_free(topic);
}

void zh_espnow_switch_send_mqtt_json_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *name = (char *)heap_caps_malloc(strlen(zh_get_device_type_value_name(device_data->device_type)) + 19, MALLOC_CAP_8BIT);
    memset(name, 0, strlen(zh_get_device_type_value_name(device_data->device_type)) + 19);
    sprintf(name, "%s " MAC_STR "", zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *unique_id = (char *)heap_caps_malloc(22, MALLOC_CAP_8BIT);
    memset(unique_id, 0, 22);
    sprintf(unique_id, "" MAC_STR "-%d", MAC2STR(device_mac), device_data->payload_data.config_message.switch_config_message.unique_id);
    char *state_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(state_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(state_topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *availability_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(availability_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(availability_topic, "%s/%s/" MAC_STR "/status", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *command_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24, MALLOC_CAP_8BIT);
    memset(command_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24);
    sprintf(command_topic, "%s/%s/" MAC_STR "/set", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *json_attributes_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(json_attributes_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(json_attributes_topic, "%s/%s/" MAC_STR "/attributes", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *qos = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(qos, 0, 4);
    sprintf(qos, "%d", device_data->payload_data.config_message.switch_config_message.qos);
    zh_json_t json = {0};
    char buffer[1024] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "platform", "mqtt");
    zh_json_add(&json, "name", name);
    zh_json_add(&json, "unique_id", unique_id);
    zh_json_add(&json, "device_class", zh_get_switch_device_class_value_name(device_data->payload_data.config_message.switch_config_message.device_class));
    zh_json_add(&json, "state_topic", state_topic);
    zh_json_add(&json, "value_template", "{{ value_json.state }}");
    zh_json_add(&json, "availability_topic", availability_topic);
    zh_json_add(&json, "command_topic", command_topic);
    zh_json_add(&json, "json_attributes_topic", json_attributes_topic);
    zh_json_add(&json, "enabled_by_default", (device_data->payload_data.config_message.switch_config_message.enabled_by_default == true) ? "true" : "false");
    zh_json_add(&json, "optimistic", (device_data->payload_data.config_message.switch_config_message.optimistic == true) ? "true" : "false");
    zh_json_add(&json, "payload_on", zh_get_on_off_type_value_name(device_data->payload_data.config_message.switch_config_message.payload_on));
    zh_json_add(&json, "payload_off", zh_get_on_off_type_value_name(device_data->payload_data.config_message.switch_config_message.payload_off));
    zh_json_add(&json, "qos", qos);
    zh_json_add(&json, "retain", (device_data->payload_data.config_message.switch_config_message.retain == true) ? "true" : "false");
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 16, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 16);
    sprintf(topic, "%s/switch/%s/config", gateway_config->software_config.mqtt_topic_prefix, unique_id);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(name);
    heap_caps_free(unique_id);
    heap_caps_free(state_topic);
    heap_caps_free(availability_topic);
    heap_caps_free(command_topic);
    heap_caps_free(json_attributes_topic);
    heap_caps_free(qos);
    heap_caps_free(topic);
}

void zh_espnow_switch_send_mqtt_json_hardware_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *relay_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(relay_pin, 0, 4);
    char *led_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(led_pin, 0, 4);
    char *int_button_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(int_button_pin, 0, 4);
    char *ext_button_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(ext_button_pin, 0, 4);
    char *sensor_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(sensor_pin, 0, 4);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    if (device_data->payload_data.config_message.switch_hardware_config_message.relay_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Relay GPIO number", "Not used");
        zh_json_add(&json, "Relay ON level", "Not used");
    }
    else
    {
        sprintf(relay_pin, "%d", device_data->payload_data.config_message.switch_hardware_config_message.relay_pin);
        zh_json_add(&json, "Relay GPIO number", relay_pin);
        zh_json_add(&json, "Relay ON level", (device_data->payload_data.config_message.switch_hardware_config_message.relay_on_level == ZH_LOW) ? "Low" : "High");
    }
    if (device_data->payload_data.config_message.switch_hardware_config_message.led_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Led GPIO number", "Not used");
        zh_json_add(&json, "Led ON level", "Not used");
    }
    else
    {
        sprintf(led_pin, "%d", device_data->payload_data.config_message.switch_hardware_config_message.led_pin);
        zh_json_add(&json, "Led GPIO number", led_pin);
        zh_json_add(&json, "Led ON level", (device_data->payload_data.config_message.switch_hardware_config_message.led_on_level == ZH_LOW) ? "Low" : "High");
    }
    if (device_data->payload_data.config_message.switch_hardware_config_message.int_button_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Internal button GPIO number", "Not used");
        zh_json_add(&json, "Internal button trigger level", "Not used");
    }
    else
    {
        sprintf(int_button_pin, "%d", device_data->payload_data.config_message.switch_hardware_config_message.int_button_pin);
        zh_json_add(&json, "Internal button GPIO number", int_button_pin);
        zh_json_add(&json, "Internal button trigger level", (device_data->payload_data.config_message.switch_hardware_config_message.int_button_on_level == ZH_LOW) ? "Low" : "High");
    }
    if (device_data->payload_data.config_message.switch_hardware_config_message.ext_button_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "External button GPIO number", "Not used");
        zh_json_add(&json, "External button trigger level", "Not used");
    }
    else
    {
        sprintf(ext_button_pin, "%d", device_data->payload_data.config_message.switch_hardware_config_message.ext_button_pin);
        zh_json_add(&json, "External button GPIO number", ext_button_pin);
        zh_json_add(&json, "External button trigger level", (device_data->payload_data.config_message.switch_hardware_config_message.ext_button_on_level == ZH_LOW) ? "Low" : "High");
    }

    if (device_data->payload_data.config_message.switch_hardware_config_message.sensor_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Sensor GPIO number", "Not used");
        zh_json_add(&json, "Sensor type", "Not used");
    }
    else
    {
        sprintf(sensor_pin, "%d", device_data->payload_data.config_message.switch_hardware_config_message.sensor_pin);
        zh_json_add(&json, "Sensor GPIO number", sensor_pin);
        zh_json_add(&json, "Sensor type", zh_get_sensor_type_value_name(device_data->payload_data.config_message.switch_hardware_config_message.sensor_type));
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(topic, "%s/%s/" MAC_STR "/config", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(relay_pin);
    heap_caps_free(led_pin);
    heap_caps_free(int_button_pin);
    heap_caps_free(ext_button_pin);
    heap_caps_free(sensor_pin);
    heap_caps_free(topic);
}

void zh_espnow_switch_send_mqtt_json_status_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    zh_json_t json = {0};
    char buffer[128] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "state", zh_get_on_off_type_value_name(device_data->payload_data.status_message.switch_status_message.status));
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(topic);
}

void zh_espnow_led_send_mqtt_json_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *name = (char *)heap_caps_malloc(strlen(zh_get_device_type_value_name(device_data->device_type)) + 19, MALLOC_CAP_8BIT);
    memset(name, 0, strlen(zh_get_device_type_value_name(device_data->device_type)) + 19);
    sprintf(name, "%s " MAC_STR "", zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *unique_id = (char *)heap_caps_malloc(22, MALLOC_CAP_8BIT);
    memset(unique_id, 0, 22);
    sprintf(unique_id, "" MAC_STR "-%d", MAC2STR(device_mac), device_data->payload_data.config_message.led_config_message.unique_id);
    char *state_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(state_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(state_topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *availability_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(availability_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(availability_topic, "%s/%s/" MAC_STR "/status", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *command_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24, MALLOC_CAP_8BIT);
    memset(command_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24);
    sprintf(command_topic, "%s/%s/" MAC_STR "/set", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *json_attributes_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(json_attributes_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(json_attributes_topic, "%s/%s/" MAC_STR "/attributes", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *qos = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(qos, 0, 4);
    sprintf(qos, "%d", device_data->payload_data.config_message.led_config_message.qos);
    char *brightness_command_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(brightness_command_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(brightness_command_topic, "%s/%s/" MAC_STR "/brightness", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *color_temp_command_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 32, MALLOC_CAP_8BIT);
    memset(color_temp_command_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 32);
    sprintf(color_temp_command_topic, "%s/%s/" MAC_STR "/temperature", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *rgb_command_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24, MALLOC_CAP_8BIT);
    memset(rgb_command_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 24);
    sprintf(rgb_command_topic, "%s/%s/" MAC_STR "/rgb", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    zh_json_t json = {0};
    char buffer[1024] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "platform", "mqtt");
    zh_json_add(&json, "name", name);
    zh_json_add(&json, "unique_id", unique_id);
    zh_json_add(&json, "state_topic", state_topic);
    zh_json_add(&json, "state_value_template", "{{ value_json.state }}");
    zh_json_add(&json, "availability_topic", availability_topic);
    zh_json_add(&json, "command_topic", command_topic);
    zh_json_add(&json, "json_attributes_topic", json_attributes_topic);
    zh_json_add(&json, "enabled_by_default", (device_data->payload_data.config_message.led_config_message.enabled_by_default == true) ? "true" : "false");
    zh_json_add(&json, "optimistic", (device_data->payload_data.config_message.led_config_message.optimistic == true) ? "true" : "false");
    zh_json_add(&json, "payload_on", zh_get_on_off_type_value_name(device_data->payload_data.config_message.led_config_message.payload_on));
    zh_json_add(&json, "payload_off", zh_get_on_off_type_value_name(device_data->payload_data.config_message.led_config_message.payload_off));
    zh_json_add(&json, "qos", qos);
    zh_json_add(&json, "retain", (device_data->payload_data.config_message.led_config_message.retain == true) ? "true" : "false");
    zh_json_add(&json, "brightness_state_topic", state_topic);
    zh_json_add(&json, "brightness_value_template", "{{ value_json.brightness }}");
    zh_json_add(&json, "brightness_command_topic", brightness_command_topic);
    if (device_data->payload_data.config_message.led_config_message.led_type == HALT_WW || device_data->payload_data.config_message.led_config_message.led_type == HALT_RGBWW)
    {
        zh_json_add(&json, "color_temp_state_topic", state_topic);
        zh_json_add(&json, "color_temp_value_template", "{{ value_json.temperature }}");
        zh_json_add(&json, "color_temp_command_topic", color_temp_command_topic);
    }
    if (device_data->payload_data.config_message.led_config_message.led_type == HALT_RGB || device_data->payload_data.config_message.led_config_message.led_type == HALT_RGBW || device_data->payload_data.config_message.led_config_message.led_type == HALT_RGBWW)
    {
        zh_json_add(&json, "rgb_state_topic", state_topic);
        zh_json_add(&json, "rgb_value_template", "{{ value_json.rgb | join(',') }}");
        zh_json_add(&json, "rgb_command_topic", rgb_command_topic);
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 15, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 15);
    sprintf(topic, "%s/light/%s/config", gateway_config->software_config.mqtt_topic_prefix, unique_id);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(name);
    heap_caps_free(unique_id);
    heap_caps_free(state_topic);
    heap_caps_free(availability_topic);
    heap_caps_free(command_topic);
    heap_caps_free(json_attributes_topic);
    heap_caps_free(qos);
    heap_caps_free(brightness_command_topic);
    heap_caps_free(color_temp_command_topic);
    heap_caps_free(rgb_command_topic);
    heap_caps_free(topic);
}

void zh_espnow_led_send_mqtt_json_hardware_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *first_white_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(first_white_pin, 0, 4);
    char *second_white_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(second_white_pin, 0, 4);
    char *red_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(red_pin, 0, 4);
    char *green_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(green_pin, 0, 4);
    char *blue_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(blue_pin, 0, 4);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    if (device_data->payload_data.config_message.led_hardware_config_message.first_white_pin == ZH_NOT_USED && device_data->payload_data.config_message.led_hardware_config_message.red_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Led type", "Not used");
    }
    else
    {
        switch (device_data->payload_data.config_message.led_hardware_config_message.led_type)
        {
        case HALT_W:
            zh_json_add(&json, "Led type", "W");
            break;
        case HALT_WW:
            zh_json_add(&json, "Led type", "WW");
            break;
        case HALT_RGB:
            zh_json_add(&json, "Led type", "RGB");
            break;
        case HALT_RGBW:
            zh_json_add(&json, "Led type", "RGBW");
            break;
        case HALT_RGBWW:
            zh_json_add(&json, "Led type", "RGBWW");
            break;
        default:
            zh_json_add(&json, "Led type", "Not used");
            break;
        }
    }
    if (device_data->payload_data.config_message.led_hardware_config_message.first_white_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "First white GPIO number", "Not used");
    }
    else
    {
        sprintf(first_white_pin, "%d", device_data->payload_data.config_message.led_hardware_config_message.first_white_pin);
        zh_json_add(&json, "First white GPIO number", first_white_pin);
    }
    if (device_data->payload_data.config_message.led_hardware_config_message.second_white_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Second white GPIO number", "Not used");
    }
    else
    {
        sprintf(second_white_pin, "%d", device_data->payload_data.config_message.led_hardware_config_message.second_white_pin);
        zh_json_add(&json, "Second white GPIO number", second_white_pin);
    }
    if (device_data->payload_data.config_message.led_hardware_config_message.red_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Red GPIO number", "Not used");
    }
    else
    {
        sprintf(red_pin, "%d", device_data->payload_data.config_message.led_hardware_config_message.red_pin);
        zh_json_add(&json, "Red GPIO number", red_pin);
    }
    if (device_data->payload_data.config_message.led_hardware_config_message.green_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Green GPIO number", "Not used");
    }
    else
    {
        sprintf(green_pin, "%d", device_data->payload_data.config_message.led_hardware_config_message.green_pin);
        zh_json_add(&json, "Green GPIO number", green_pin);
    }
    if (device_data->payload_data.config_message.led_hardware_config_message.blue_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Blue GPIO number", "Not used");
    }
    else
    {
        sprintf(blue_pin, "%d", device_data->payload_data.config_message.led_hardware_config_message.blue_pin);
        zh_json_add(&json, "Blue GPIO number", blue_pin);
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(topic, "%s/%s/" MAC_STR "/config", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(first_white_pin);
    heap_caps_free(second_white_pin);
    heap_caps_free(red_pin);
    heap_caps_free(green_pin);
    heap_caps_free(blue_pin);
    heap_caps_free(topic);
}

void zh_espnow_led_send_mqtt_json_status_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *brightness = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(brightness, 0, 4);
    sprintf(brightness, "%d", device_data->payload_data.status_message.led_status_message.brightness);
    char *temperature = (char *)heap_caps_malloc(6, MALLOC_CAP_8BIT);
    memset(temperature, 0, 6);
    sprintf(temperature, "%d", device_data->payload_data.status_message.led_status_message.temperature);
    char *rgb = (char *)heap_caps_malloc(12, MALLOC_CAP_8BIT);
    memset(rgb, 0, 12);
    sprintf(rgb, "%d,%d,%d", device_data->payload_data.status_message.led_status_message.red, device_data->payload_data.status_message.led_status_message.green, device_data->payload_data.status_message.led_status_message.blue);
    zh_json_t json = {0};
    char buffer[128] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "state", zh_get_on_off_type_value_name(device_data->payload_data.status_message.led_status_message.status));
    zh_json_add(&json, "brightness", brightness);
    zh_json_add(&json, "temperature", temperature);
    zh_json_add(&json, "rgb", rgb);
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(topic);
}

void zh_espnow_sensor_send_mqtt_json_config_message(zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *name = (char *)heap_caps_malloc(strlen(zh_get_device_type_value_name(device_data->device_type)) + 19, MALLOC_CAP_8BIT);
    memset(name, 0, strlen(zh_get_device_type_value_name(device_data->device_type)) + 19);
    sprintf(name, "%s " MAC_STR "", zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *unique_id = (char *)heap_caps_malloc(22, MALLOC_CAP_8BIT);
    memset(unique_id, 0, 22);
    sprintf(unique_id, "" MAC_STR "-%d", MAC2STR(device_mac), device_data->payload_data.config_message.sensor_config_message.unique_id);
    char *state_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(state_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(state_topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *value_template = (char *)heap_caps_malloc(strlen(zh_get_sensor_device_class_value_name(device_data->payload_data.config_message.sensor_config_message.sensor_device_class)) + 18, MALLOC_CAP_8BIT);
    memset(value_template, 0, strlen(zh_get_sensor_device_class_value_name(device_data->payload_data.config_message.sensor_config_message.sensor_device_class)) + 18);
    sprintf(value_template, "{{ value_json.%s }}", zh_get_sensor_device_class_value_name(device_data->payload_data.config_message.sensor_config_message.sensor_device_class));
    char *json_attributes_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(json_attributes_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(json_attributes_topic, "%s/%s/" MAC_STR "/attributes", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *qos = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(qos, 0, 4);
    sprintf(qos, "%d", device_data->payload_data.config_message.sensor_config_message.qos);
    char *expire_after = (char *)heap_caps_malloc(6, MALLOC_CAP_8BIT);
    memset(expire_after, 0, 6);
    sprintf(expire_after, "%d", device_data->payload_data.config_message.sensor_config_message.expire_after);
    char *suggested_display_precision = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(suggested_display_precision, 0, 4);
    sprintf(suggested_display_precision, "%d", device_data->payload_data.config_message.sensor_config_message.suggested_display_precision);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "platform", "mqtt");
    zh_json_add(&json, "name", name);
    zh_json_add(&json, "unique_id", unique_id);
    zh_json_add(&json, "device_class", zh_get_sensor_device_class_value_name(device_data->payload_data.config_message.sensor_config_message.sensor_device_class));
    zh_json_add(&json, "state_topic", state_topic);
    zh_json_add(&json, "value_template", value_template);
    zh_json_add(&json, "json_attributes_topic", json_attributes_topic);
    zh_json_add(&json, "enabled_by_default", (device_data->payload_data.config_message.sensor_config_message.enabled_by_default == true) ? "true" : "false");
    zh_json_add(&json, "qos", qos);
    zh_json_add(&json, "retain", (device_data->payload_data.config_message.sensor_config_message.retain == true) ? "true" : "false");
    zh_json_add(&json, "expire_after", expire_after);
    zh_json_add(&json, "force_update", (device_data->payload_data.config_message.sensor_config_message.force_update == true) ? "true" : "false");
    zh_json_add(&json, "suggested_display_precision", suggested_display_precision);
    zh_json_add(&json, "unit_of_measurement", device_data->payload_data.config_message.sensor_config_message.unit_of_measurement);
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 16, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 16);
    sprintf(topic, "%s/sensor/%s/config", gateway_config->software_config.mqtt_topic_prefix, unique_id);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(name);
    heap_caps_free(unique_id);
    heap_caps_free(state_topic);
    heap_caps_free(value_template);
    heap_caps_free(json_attributes_topic);
    heap_caps_free(qos);
    heap_caps_free(expire_after);
    heap_caps_free(suggested_display_precision);
    heap_caps_free(topic);
}

void zh_espnow_sensor_send_mqtt_json_hardware_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *sensor_pin_1 = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(sensor_pin_1, 0, 4);
    char *sensor_pin_2 = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(sensor_pin_2, 0, 4);
    char *power_pin = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(power_pin, 0, 4);
    char *measurement_frequency = (char *)heap_caps_malloc(6, MALLOC_CAP_8BIT);
    memset(measurement_frequency, 0, 6);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    if (device_data->payload_data.config_message.sensor_hardware_config_message.sensor_pin_1 == ZH_NOT_USED)
    {
        zh_json_add(&json, "Sensor type", "Not used");
        zh_json_add(&json, "Sensor GPIO number 1", "Not used");
    }
    else
    {
        zh_json_add(&json, "Sensor type", zh_get_sensor_type_value_name(device_data->payload_data.config_message.sensor_hardware_config_message.sensor_type));
        sprintf(sensor_pin_1, "%d", device_data->payload_data.config_message.sensor_hardware_config_message.sensor_pin_1);
        zh_json_add(&json, "Sensor GPIO number 1", sensor_pin_1);
    }
    if (device_data->payload_data.config_message.sensor_hardware_config_message.sensor_pin_2 == ZH_NOT_USED)
    {
        zh_json_add(&json, "Sensor GPIO number 2", "Not used");
    }
    else
    {
        if (device_data->payload_data.config_message.sensor_hardware_config_message.sensor_pin_1 == ZH_NOT_USED)
        {
            zh_json_add(&json, "Sensor GPIO number 2", "Not used");
        }
        else
        {
            sprintf(sensor_pin_2, "%d", device_data->payload_data.config_message.sensor_hardware_config_message.sensor_pin_2);
            zh_json_add(&json, "Sensor GPIO number 2", sensor_pin_2);
        }
    }
    if (device_data->payload_data.config_message.sensor_hardware_config_message.power_pin == ZH_NOT_USED)
    {
        zh_json_add(&json, "Sensor power control GPIO number", "Not used");
    }
    else
    {
        sprintf(power_pin, "%d", device_data->payload_data.config_message.sensor_hardware_config_message.power_pin);
        zh_json_add(&json, "Sensor power control GPIO number", power_pin);
    }
    sprintf(measurement_frequency, "%d", device_data->payload_data.config_message.sensor_hardware_config_message.measurement_frequency);
    zh_json_add(&json, "Measurement frequency", measurement_frequency);
    zh_json_add(&json, "Power mode", (device_data->payload_data.config_message.sensor_hardware_config_message.battery_power == true) ? "Battery" : "External");
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 27);
    sprintf(topic, "%s/%s/" MAC_STR "/config", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(sensor_pin_1);
    heap_caps_free(sensor_pin_2);
    heap_caps_free(power_pin);
    heap_caps_free(measurement_frequency);
    heap_caps_free(topic);
}

void zh_espnow_sensor_send_mqtt_json_status_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *voltage = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(voltage, 0, 317);
    char *temperature = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(temperature, 0, 317);
    char *humidity = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(humidity, 0, 317);
    char *atmospheric_pressure = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(atmospheric_pressure, 0, 317);
    char *aqi = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(aqi, 0, 317);
    char *illuminance = (char *)heap_caps_malloc(317, MALLOC_CAP_8BIT);
    memset(illuminance, 0, 317);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    sprintf(voltage, "%f", device_data->payload_data.status_message.sensor_status_message.voltage);
    zh_json_add(&json, "voltage", voltage);
    switch (device_data->payload_data.status_message.sensor_status_message.sensor_type)
    {
    case HAST_DS18B20:
        sprintf(temperature, "%f", device_data->payload_data.status_message.sensor_status_message.temperature);
        zh_json_add(&json, "temperature", temperature);
        break;
    case HAST_DHT:
    case HAST_AHT:
    case HAST_SHT:
    case HAST_HTU:
    case HAST_HDC1080:
        sprintf(temperature, "%f", device_data->payload_data.status_message.sensor_status_message.temperature);
        zh_json_add(&json, "temperature", temperature);
        sprintf(humidity, "%f", device_data->payload_data.status_message.sensor_status_message.humidity);
        zh_json_add(&json, "humidity", humidity);
        break;
    case HAST_BH1750:
        sprintf(illuminance, "%f", device_data->payload_data.status_message.sensor_status_message.illuminance);
        zh_json_add(&json, "illuminance", illuminance);
        break;
    case HAST_BMP280:
        sprintf(temperature, "%f", device_data->payload_data.status_message.sensor_status_message.temperature);
        zh_json_add(&json, "temperature", temperature);
        sprintf(atmospheric_pressure, "%f", device_data->payload_data.status_message.sensor_status_message.atmospheric_pressure);
        zh_json_add(&json, "atmospheric_pressure", atmospheric_pressure);
        break;
    case HAST_BME280:
        sprintf(temperature, "%f", device_data->payload_data.status_message.sensor_status_message.temperature);
        zh_json_add(&json, "temperature", temperature);
        sprintf(humidity, "%f", device_data->payload_data.status_message.sensor_status_message.humidity);
        zh_json_add(&json, "humidity", humidity);
        sprintf(atmospheric_pressure, "%f", device_data->payload_data.status_message.sensor_status_message.atmospheric_pressure);
        zh_json_add(&json, "atmospheric_pressure", atmospheric_pressure);
        break;
    case HAST_BME680:
        sprintf(temperature, "%f", device_data->payload_data.status_message.sensor_status_message.temperature);
        zh_json_add(&json, "temperature", temperature);
        sprintf(humidity, "%f", device_data->payload_data.status_message.sensor_status_message.humidity);
        zh_json_add(&json, "humidity", humidity);
        sprintf(atmospheric_pressure, "%f", device_data->payload_data.status_message.sensor_status_message.atmospheric_pressure);
        zh_json_add(&json, "atmospheric_pressure", atmospheric_pressure);
        sprintf(aqi, "%f", device_data->payload_data.status_message.sensor_status_message.aqi);
        zh_json_add(&json, "aqi", aqi);
        break;
    default:
        break;
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(voltage);
    heap_caps_free(temperature);
    heap_caps_free(humidity);
    heap_caps_free(atmospheric_pressure);
    heap_caps_free(aqi);
    heap_caps_free(illuminance);
    heap_caps_free(topic);
}

void zh_espnow_binary_sensor_send_mqtt_json_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    char *name = (char *)heap_caps_malloc(strlen(zh_get_device_type_value_name(device_data->device_type)) + 19, MALLOC_CAP_8BIT);
    memset(name, 0, strlen(zh_get_device_type_value_name(device_data->device_type)) + 19);
    sprintf(name, "%s " MAC_STR "", zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *unique_id = (char *)heap_caps_malloc(22, MALLOC_CAP_8BIT);
    memset(unique_id, 0, 22);
    sprintf(unique_id, "" MAC_STR "-%d", MAC2STR(device_mac), device_data->payload_data.config_message.binary_sensor_config_message.unique_id);
    char *state_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26, MALLOC_CAP_8BIT);
    memset(state_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 26);
    sprintf(state_topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *value_template = (char *)heap_caps_malloc(strlen(zh_get_binary_sensor_device_class_value_name(device_data->payload_data.config_message.binary_sensor_config_message.binary_sensor_device_class)) + 18, MALLOC_CAP_8BIT);
    memset(value_template, 0, strlen(zh_get_binary_sensor_device_class_value_name(device_data->payload_data.config_message.binary_sensor_config_message.binary_sensor_device_class)) + 18);
    sprintf(value_template, "{{ value_json.%s }}", zh_get_binary_sensor_device_class_value_name(device_data->payload_data.config_message.binary_sensor_config_message.binary_sensor_device_class));
    char *json_attributes_topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(json_attributes_topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(json_attributes_topic, "%s/%s/" MAC_STR "/attributes", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    char *qos = (char *)heap_caps_malloc(4, MALLOC_CAP_8BIT);
    memset(qos, 0, 4);
    sprintf(qos, "%d", device_data->payload_data.config_message.binary_sensor_config_message.qos);
    char *expire_after = (char *)heap_caps_malloc(6, MALLOC_CAP_8BIT);
    memset(expire_after, 0, 6);
    sprintf(expire_after, "%d", device_data->payload_data.config_message.binary_sensor_config_message.expire_after);
    char *off_delay = (char *)heap_caps_malloc(6, MALLOC_CAP_8BIT);
    memset(off_delay, 0, 6);
    sprintf(off_delay, "%d", device_data->payload_data.config_message.binary_sensor_config_message.off_delay);
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    zh_json_add(&json, "platform", "mqtt");
    zh_json_add(&json, "name", name);
    zh_json_add(&json, "unique_id", unique_id);
    zh_json_add(&json, "device_class", zh_get_binary_sensor_device_class_value_name(device_data->payload_data.config_message.binary_sensor_config_message.binary_sensor_device_class));
    zh_json_add(&json, "state_topic", state_topic);
    zh_json_add(&json, "value_template", value_template);
    zh_json_add(&json, "json_attributes_topic", json_attributes_topic);
    zh_json_add(&json, "enabled_by_default", (device_data->payload_data.config_message.binary_sensor_config_message.enabled_by_default == true) ? "true" : "false");
    zh_json_add(&json, "payload_on", zh_get_on_off_type_value_name(device_data->payload_data.config_message.binary_sensor_config_message.payload_on));
    zh_json_add(&json, "payload_off", zh_get_on_off_type_value_name(device_data->payload_data.config_message.binary_sensor_config_message.payload_off));
    zh_json_add(&json, "qos", qos);
    zh_json_add(&json, "retain", (device_data->payload_data.config_message.binary_sensor_config_message.retain == true) ? "true" : "false");
    if (device_data->payload_data.config_message.binary_sensor_config_message.expire_after != 0)
    {
        zh_json_add(&json, "expire_after", expire_after);
    }
    zh_json_add(&json, "force_update", (device_data->payload_data.config_message.binary_sensor_config_message.force_update == true) ? "true" : "false");
    if (device_data->payload_data.config_message.binary_sensor_config_message.off_delay != 0)
    {
        zh_json_add(&json, "off_delay", off_delay);
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 23, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(unique_id) + 23);
    sprintf(topic, "%s/binary_sensor/%s/config", gateway_config->software_config.mqtt_topic_prefix, unique_id);
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(name);
    heap_caps_free(unique_id);
    heap_caps_free(state_topic);
    heap_caps_free(value_template);
    heap_caps_free(json_attributes_topic);
    heap_caps_free(qos);
    heap_caps_free(expire_after);
    heap_caps_free(off_delay);
    heap_caps_free(topic);
}

void zh_espnow_binary_sensor_send_mqtt_json_hardware_config_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
}

void zh_espnow_binary_sensor_send_mqtt_json_status_message(const zh_espnow_data_t *device_data, const uint8_t *device_mac, const gateway_config_t *gateway_config)
{
    zh_json_t json = {0};
    char buffer[512] = {0};
    zh_json_init(&json);
    switch (device_data->payload_data.status_message.binary_sensor_status_message.sensor_type)
    {
    case HAST_GATEWAY:
        zh_json_add(&json, "connectivity", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.connect));
        break;
    case HAST_WINDOW:
        zh_json_add(&json, "window", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.open));
        zh_json_add(&json, "battery", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.battery));
        break;
    case HAST_DOOR:
        zh_json_add(&json, "door", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.open));
        zh_json_add(&json, "battery", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.battery));
        break;
    case HAST_LEAKAGE:
        zh_json_add(&json, "moisture", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.leakage));
        zh_json_add(&json, "battery", zh_get_on_off_type_value_name(device_data->payload_data.status_message.binary_sensor_status_message.battery));
        break;
    default:
        break;
    }
    zh_json_create(&json, buffer);
    zh_json_free(&json);
    char *topic = (char *)heap_caps_malloc(strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31, MALLOC_CAP_8BIT);
    memset(topic, 0, strlen(gateway_config->software_config.mqtt_topic_prefix) + strlen(zh_get_device_type_value_name(device_data->device_type)) + 31);
    sprintf(topic, "%s/%s/" MAC_STR "/state", gateway_config->software_config.mqtt_topic_prefix, zh_get_device_type_value_name(device_data->device_type), MAC2STR(device_mac));
    esp_mqtt_client_publish(gateway_config->mqtt_client, topic, buffer, 0, 2, true);
    heap_caps_free(topic);
}