#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); 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); 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); #ifdef CONFIG_CONNECTION_TYPE_LAN gateway_config->software_config.is_lan_mode = true; strcpy(gateway_config->software_config.ssid_name, "ssid"); strcpy(gateway_config->software_config.ssid_password, "password"); #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); strcpy(gateway_config->software_config.ntp_server_url, CONFIG_NTP_SERVER_URL); strcpy(gateway_config->software_config.ntp_time_zone, CONFIG_NTP_TIME_ZONE); strcpy(gateway_config->software_config.firmware_upgrade_url, CONFIG_FIRMWARE_UPGRADE_URL); zh_save_config(gateway_config); return; } nvs_get_u8(nvs_handle, "lan_mode", (uint8_t *)&gateway_config->software_config.is_lan_mode); size_t size = 0; nvs_get_str(nvs_handle, "ssid_name", NULL, &size); nvs_get_str(nvs_handle, "ssid_name", gateway_config->software_config.ssid_name, &size); nvs_get_str(nvs_handle, "ssid_password", NULL, &size); nvs_get_str(nvs_handle, "ssid_password", gateway_config->software_config.ssid_password, &size); nvs_get_str(nvs_handle, "mqtt_url", NULL, &size); nvs_get_str(nvs_handle, "mqtt_url", gateway_config->software_config.mqtt_broker_url, &size); nvs_get_str(nvs_handle, "topic_prefix", NULL, &size); nvs_get_str(nvs_handle, "topic_prefix", gateway_config->software_config.mqtt_topic_prefix, &size); nvs_get_str(nvs_handle, "ntp_url", NULL, &size); nvs_get_str(nvs_handle, "ntp_url", gateway_config->software_config.ntp_server_url, &size); nvs_get_str(nvs_handle, "time_zone", NULL, &size); nvs_get_str(nvs_handle, "time_zone", gateway_config->software_config.ntp_time_zone, &size); nvs_get_str(nvs_handle, "upgrade_url", NULL, &size); nvs_get_str(nvs_handle, "upgrade_url", gateway_config->software_config.firmware_upgrade_url, &size); nvs_close(nvs_handle); } 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_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_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->sntp_is_enable == true) { esp_sntp_stop(); gateway_config->sntp_is_enable = false; vTaskDelete(gateway_config->gateway_current_time_task); } 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->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); } 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->sntp_is_enable == true) { esp_sntp_stop(); gateway_config->sntp_is_enable = false; vTaskDelete(gateway_config->gateway_current_time_task); } 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->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); } 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) { 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); } } 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); break; case ZHPT_UPDATE_SUCCESS: esp_mqtt_client_publish(gateway_config->mqtt_client, topic, "update_success", 0, 2, true); 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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; 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); 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); 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); heap_caps_free(topic); 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); heap_caps_free(topic); 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); heap_caps_free(topic); 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; 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); 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); 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); 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); 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, 10000 / 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); 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); 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); } 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 * 3)) { 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); 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(10000 / 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_DHT11: // Deprecated. Will be removed soon. case HAST_DHT22: // Deprecated. Will be removed soon. case HAST_DHT: 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(temperature, "%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); }