/* * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_openclaw_node_common_device_node_cmd.h" #include #include #include #include "cJSON.h" #include "esp_app_desc.h" #include "esp_check.h" #include "esp_chip_info.h" #include "esp_heap_caps.h" #include "esp_mac.h" #include "esp_system.h" #include "esp_timer.h" #include "esp_wifi.h" #include "esp_openclaw_node_example_json.h" #include "esp_openclaw_node_wifi.h" static const char *TAG = "esp_openclaw_node_device_cmd"; static void bytes_to_lower_hex(const uint8_t *input, size_t input_len, char *output, size_t output_size) { static const char HEX[] = "0123456789abcdef"; if (output_size < (input_len * 2U) + 1U) { if (output_size > 0) { output[0] = '\0'; } return; } for (size_t i = 0; i < input_len; ++i) { output[(i * 2U)] = HEX[(input[i] >> 4) & 0x0f]; output[(i * 2U) + 1U] = HEX[input[i] & 0x0f]; } output[input_len * 2U] = '\0'; } static const char *firmware_version(void) { return esp_app_get_description()->version; } static const char *wifi_auth_mode_name(uint8_t authmode) { switch ((wifi_auth_mode_t)authmode) { case WIFI_AUTH_OPEN: return "open"; case WIFI_AUTH_WEP: return "wep"; case WIFI_AUTH_WPA_PSK: return "wpa-psk"; case WIFI_AUTH_WPA2_PSK: return "wpa2-psk"; case WIFI_AUTH_WPA_WPA2_PSK: return "wpa-wpa2-psk"; case WIFI_AUTH_WPA2_ENTERPRISE: return "wpa2-enterprise"; case WIFI_AUTH_WPA3_PSK: return "wpa3-psk"; case WIFI_AUTH_WPA2_WPA3_PSK: return "wpa2-wpa3-psk"; case WIFI_AUTH_WAPI_PSK: return "wapi-psk"; default: return "unknown"; } } static void format_mac_address(const uint8_t mac[6], char *buffer, size_t buffer_size) { snprintf( buffer, buffer_size, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } static void add_wifi_status_fields(cJSON *object) { esp_openclaw_node_wifi_status_t wifi = {0}; esp_openclaw_node_wifi_get_status(&wifi); cJSON_AddBoolToObject(object, "connected", wifi.connected); cJSON_AddStringToObject(object, "ssid", wifi.ssid); if (wifi.ip[0] != '\0') { cJSON_AddStringToObject(object, "ip", wifi.ip); cJSON_AddStringToObject(object, "netmask", wifi.netmask); cJSON_AddStringToObject(object, "gateway", wifi.gateway); } if (wifi.connected) { cJSON_AddNumberToObject(object, "rssi", wifi.rssi); cJSON_AddNumberToObject(object, "channel", wifi.channel); cJSON_AddStringToObject(object, "authMode", wifi_auth_mode_name(wifi.authmode)); } if (wifi.last_disconnect_reason != 0) { cJSON_AddNumberToObject(object, "lastDisconnectReason", wifi.last_disconnect_reason); } } static cJSON *build_device_info_payload(esp_openclaw_node_handle_t node) { const char *device_id = esp_openclaw_node_get_device_id(node); const esp_app_desc_t *app = esp_app_get_description(); esp_chip_info_t chip = {0}; esp_chip_info(&chip); uint8_t mac[6] = {0}; char mac_text[18] = {0}; char app_sha256[65] = {0}; esp_read_mac(mac, ESP_MAC_WIFI_STA); format_mac_address(mac, mac_text, sizeof(mac_text)); bytes_to_lower_hex(app->app_elf_sha256, sizeof(app->app_elf_sha256), app_sha256, sizeof(app_sha256)); cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "deviceId", device_id != NULL ? device_id : ""); cJSON_AddStringToObject(root, "firmwareVersion", firmware_version()); cJSON_AddStringToObject(root, "idfVersion", esp_get_idf_version()); cJSON_AddStringToObject(root, "chipModel", CONFIG_IDF_TARGET); cJSON_AddNumberToObject(root, "chipRevision", chip.revision); cJSON_AddNumberToObject(root, "cores", chip.cores); cJSON_AddStringToObject(root, "projectName", app->project_name); cJSON_AddStringToObject(root, "appVersion", app->version); cJSON_AddStringToObject(root, "appElfSha256", app_sha256); cJSON_AddStringToObject(root, "wifiMac", mac_text); return root; } static cJSON *build_device_status_payload(esp_openclaw_node_handle_t node) { cJSON *root = cJSON_CreateObject(); cJSON_AddNumberToObject(root, "uptimeMs", esp_timer_get_time() / 1000LL); cJSON_AddNumberToObject(root, "freeHeap", esp_get_free_heap_size()); cJSON_AddNumberToObject(root, "minFreeHeap", esp_get_minimum_free_heap_size()); cJSON_AddNumberToObject( root, "largestFreeBlock", heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); cJSON_AddBoolToObject(root, "savedSessionAvailable", esp_openclaw_node_has_saved_session(node)); cJSON *wifi = cJSON_CreateObject(); add_wifi_status_fields(wifi); cJSON_AddItemToObject(root, "wifi", wifi); return root; } static cJSON *build_wifi_status_payload(void) { cJSON *root = cJSON_CreateObject(); add_wifi_status_fields(root); return root; } static esp_err_t handle_device_info( esp_openclaw_node_handle_t node, void *context, const char *params_json, size_t params_len, char **out_payload_json, esp_openclaw_node_error_t *out_error) { (void)context; (void)params_json; (void)params_len; (void)out_error; return esp_openclaw_node_example_take_json_payload( build_device_info_payload(node), out_payload_json); } static esp_err_t handle_device_status( esp_openclaw_node_handle_t node, void *context, const char *params_json, size_t params_len, char **out_payload_json, esp_openclaw_node_error_t *out_error) { (void)context; (void)params_json; (void)params_len; (void)out_error; return esp_openclaw_node_example_take_json_payload( build_device_status_payload(node), out_payload_json); } static esp_err_t handle_wifi_status( esp_openclaw_node_handle_t node, void *context, const char *params_json, size_t params_len, char **out_payload_json, esp_openclaw_node_error_t *out_error) { (void)node; (void)context; (void)params_json; (void)params_len; (void)out_error; return esp_openclaw_node_example_take_json_payload( build_wifi_status_payload(), out_payload_json); } esp_err_t esp_openclaw_node_common_register_device_node_commands(esp_openclaw_node_handle_t node) { static const esp_openclaw_node_command_t DEVICE_INFO_COMMAND = { .name = "device.info", .handler = handle_device_info, .context = NULL, }; static const esp_openclaw_node_command_t DEVICE_STATUS_COMMAND = { .name = "device.status", .handler = handle_device_status, .context = NULL, }; static const esp_openclaw_node_command_t WIFI_STATUS_COMMAND = { .name = "wifi.status", .handler = handle_wifi_status, .context = NULL, }; ESP_RETURN_ON_ERROR( esp_openclaw_node_register_capability(node, "device"), TAG, "registering device capability failed"); ESP_RETURN_ON_ERROR( esp_openclaw_node_register_capability(node, "wifi"), TAG, "registering wifi capability failed"); ESP_RETURN_ON_ERROR( esp_openclaw_node_register_command(node, &DEVICE_INFO_COMMAND), TAG, "registering device.info failed"); ESP_RETURN_ON_ERROR( esp_openclaw_node_register_command(node, &DEVICE_STATUS_COMMAND), TAG, "registering device.status failed"); ESP_RETURN_ON_ERROR( esp_openclaw_node_register_command(node, &WIFI_STATUS_COMMAND), TAG, "registering wifi.status failed"); return ESP_OK; }