253 lines
7.7 KiB
C
253 lines
7.7 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "esp_openclaw_node_common_device_node_cmd.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|