13#if defined(USE_API_USER_DEFINED_ACTIONS) && defined(USE_API_CUSTOM_SERVICES)
14#include "esphome/core/helpers.h"
28constexpr const char *MANAGEMENT_ACTION_RENAME_DEVICE =
"rename_device";
29constexpr const char *MANAGEMENT_RESULT_EVENT =
"esphome.home_io_control_action_result";
30constexpr size_t RESULT_CODE_BUFFER_SIZE = 5;
31constexpr size_t UNEXPECTED_RESPONSE_MESSAGE_BUFFER_SIZE = 64;
37#if defined(USE_API_USER_DEFINED_ACTIONS) && defined(USE_API_CUSTOM_SERVICES)
44class RenameDeviceServiceDescriptor :
public api::UserServiceDescriptor {
46 explicit RenameDeviceServiceDescriptor(IOHomeControlComponent *parent)
47 : parent_(parent), key_(fnv1_hash(MANAGEMENT_ACTION_RENAME_DEVICE)) {}
49 api::ListEntitiesServicesResponse encode_list_service_response()
override {
50 api::ListEntitiesServicesResponse response;
51 response.name = StringRef(MANAGEMENT_ACTION_RENAME_DEVICE);
52 response.key = this->key_;
53 response.supports_response = api::enums::SUPPORTS_RESPONSE_NONE;
54 response.args.init(this->arg_names_.size());
55 for (
const char *arg_name : this->arg_names_) {
56 auto &arg = response.args.emplace_back();
57 arg.name = StringRef(arg_name);
58 arg.type = api::enums::SERVICE_ARG_TYPE_STRING;
63 bool execute_service(
const api::ExecuteServiceRequest &request)
override {
64 if (request.key != this->key_ || request.args.size() != this->arg_names_.size())
67 this->parent_->api_rename_device_(request.args[0].string_.str(), request.args[1].string_.str());
71#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
72 bool execute_service(
const api::ExecuteServiceRequest &request, uint32_t)
override {
73 return this->execute_service(request);
80 const std::array<const char *, 2> arg_names_{
"device_id",
"new_name"};
88 std::transform(normalized.begin(), normalized.end(), normalized.begin(),
89 [](
unsigned char ch) { return static_cast<char>(std::toupper(ch)); });
96 std::array<char, RESULT_CODE_BUFFER_SIZE> buffer{};
97 std::snprintf(buffer.data(), buffer.size(),
"%02X", result_code);
98 return std::string(buffer.data());
102 const std::string &device_id) {
110#if defined(USE_API_USER_DEFINED_ACTIONS) && defined(USE_API_CUSTOM_SERVICES)
111 if (api::global_api_server ==
nullptr) {
112 ESP_LOGW(
detail::TAG,
"Native API server not available, rename action will not be registered");
125 ESP_LOGW(
detail::TAG,
"Management action %s for device %s failed: %s", result.
action.c_str(),
129 if (!this->is_connected())
132 std::map<std::string, std::string> event_data{{
"action", result.
action},
147 this->fire_homeassistant_event(MANAGEMENT_RESULT_EVENT, event_data);
155 const std::string &new_name) {
160 result.
message =
"hub is not initialized";
166 result.
message =
"device ID must be exactly 6 hexadecimal characters";
170 auto *dev = this->
get_device(normalized_device_id);
171 if (dev ==
nullptr) {
172 result.
message =
"device is not registered on this hub";
177 std::string normalized_name;
187 result.
message =
"failed to build rename request";
194 result.
message =
"no valid response to rename request";
200 result.
message =
"device returned an empty error response";
212 std::array<char, UNEXPECTED_RESPONSE_MESSAGE_BUFFER_SIZE> buffer{};
213 std::snprintf(buffer.data(), buffer.size(),
"unexpected rename response 0x%02X", response.
cmd);
214 result.
message = buffer.data();
219 result.
message =
"rename acknowledged by device";
222 result.
message =
"rename acknowledged but verification readback failed";
226 auto *updated_device = this->
get_device(normalized_device_id);
227 if (updated_device !=
nullptr)
232 result.
message =
"rename verified by device readback";
236 result.
message =
"rename acknowledged but readback did not match the requested name";
virtual ManagementActionResult rename_device(const std::string &device_id, const std::string &new_name)
Rename a device and verify the result by reading the name back.
bool send_and_receive_(const IoFrame &request, IoFrame &response, uint32_t freq)
Main request/response exchange with retry and automatic authentication.
friend class detail::RenameDeviceServiceDescriptor
virtual IoDevice * get_device(const std::string &device_id)
Retrieve a device by ID; returns nullptr if not found.
void log_exchange_debug_(const char *device_id) const
void api_rename_device_(const std::string &device_id, const std::string &new_name)
Native API action callback: rename a registered device.
void publish_management_result_(const ManagementActionResult &result)
Publish the outcome of a management action as a Home Assistant event and structured logs.
uint8_t node_id_[NODE_ID_SIZE]
virtual bool request_device_name(const std::string &device_id)
Request the stored device name from a device.
void register_management_actions_()
Register hub-level Home Assistant actions exposed through ESPHome's native API.
Internal helpers shared by the hub implementation .cpp files.
constexpr const char * TAG
Shared log tag for hub-level messages.
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
static constexpr uint8_t CMD_ERROR_RESP
Error response to any command.
std::string format_result_code(uint8_t result_code)
static constexpr uint8_t DEVICE_NAME_WRITE_PAYLOAD_SIZE
Fixed write payload: 15 visible chars plus trailing null/padding.
const char * command_result_description(uint8_t result)
Return a human-readable explanation for a CMD_ERROR_RESP result code.
const char * command_result_name(uint8_t result)
Return a stable symbolic name for a CMD_ERROR_RESP result code.
static constexpr uint8_t CMD_SET_NAME_RESP
Device-name write response.
DeviceNameValidationError
Validation result for outbound device-name writes.
@ NONE
Name is valid and encodable.
std::string trim_ascii_whitespace(const std::string &value)
Trim leading and trailing ASCII whitespace from a string.
IOHomeControlComponent::ManagementActionResult make_management_result(const std::string &action, const std::string &device_id)
static constexpr uint32_t FREQ_CH2
Channel 2: 868.95 MHz (1W and 2W, TX channel).
DeviceNameValidationError encode_device_name_payload(const std::string &name, uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE], std::string &normalized_name)
Validate and encode a user-supplied UTF-8 device name into the fixed Latin-1 write payload.
bool create_set_name(IoFrame &f, const uint8_t *own, const uint8_t *dst, const uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE])
Build an authenticated set-name request (0x52) using a fixed zero-padded Latin-1 payload.
std::string bool_to_string(bool value)
std::string normalize_device_id_argument(const std::string &device_id)
const char * device_name_validation_error_description(DeviceNameValidationError error)
Return a human-readable explanation for a device-name validation result.
bool hex_to_bytes(const std::string &hex, uint8_t *out, uint8_t len)
Convert a hex string (e.g., "123ABC") to a byte array.
Command builders for the IO‑Homecontrol protocol.
Result payload used by hub-level management actions such as rename.
bool has_result_code
True when result_code contains a decoded CMD_ERROR_RESP byte.
bool success
Whether the requested management action succeeded.
std::string requested_name
Requested normalized UTF-8 name for rename actions.
std::string message
Human-readable outcome summary.
uint8_t result_code
Optional CMD_ERROR_RESP result byte from the device.
std::string device_id
Target IO-homecontrol device ID.
std::string applied_name
Verified cached UTF-8 name after a readback, when available.
std::string action
Action name, for example "rename_device".
bool verified
Whether a follow-up readback verified the applied state.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t data[FRAME_MAX_DATA_SIZE]
Command parameters (0–23 bytes).
uint8_t data_len
Actual length of data.