feat: Enhance EtsDeviceRuntime constructor and multicast handling

- Updated EtsDeviceRuntime constructor to accept an optional tunnel_client_address parameter with a default value of 0.
- Modified EspIdfPlatform::setupMultiCast to use IPPROTO_UDP for socket creation and improved multicast interface selection based on the current IP address.
- Ensured that the multicast interface is set only if a valid local address is available.
- Adjusted the client address assignment in EtsDeviceRuntime to use the provided tunnel_client_address if valid, falling back to the default otherwise.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-13 12:47:37 +08:00
parent b74367e5a0
commit 39ef630608
11 changed files with 595 additions and 118 deletions
@@ -2198,11 +2198,36 @@ struct GatewayBridgeService::ChannelRuntime {
cJSON* knx_json = cJSON_CreateObject();
if (knx_json != nullptr) {
auto* endpoint_runtime = service.knx_endpoint_runtime_;
if (endpoint_runtime == nullptr) {
endpoint_runtime = const_cast<GatewayBridgeService&>(service).selectKnxEndpointRuntime();
}
bool programming_mode = false;
bool programming_control_available = false;
int endpoint_owner_gateway_id = -1;
if (endpoint_runtime != nullptr) {
LockGuard owner_guard(endpoint_runtime->lock);
endpoint_owner_gateway_id = endpoint_runtime->channel.gateway_id;
programming_control_available = endpoint_runtime->knx_router != nullptr &&
endpoint_runtime->knx_router->started();
if (programming_control_available) {
programming_mode = endpoint_runtime->knx_router->programmingMode();
}
}
const auto effective_knx = knx_config.has_value() ? knx_config : service_config.default_knx_config;
cJSON_AddBoolToObject(knx_json, "enabled", service_config.knx_enabled);
cJSON_AddBoolToObject(knx_json, "startupEnabled", service_config.knx_startup_enabled);
cJSON_AddBoolToObject(knx_json, "started", knx_started);
cJSON_AddBoolToObject(knx_json, "routerReady", knx_router != nullptr && knx_router->started());
cJSON_AddBoolToObject(knx_json, "programmingMode", programming_mode);
cJSON_AddBoolToObject(knx_json, "programmingControlAvailable",
programming_control_available);
cJSON_AddBoolToObject(knx_json, "endpointOwner",
endpoint_owner_gateway_id == channel.gateway_id);
if (endpoint_owner_gateway_id >= 0) {
cJSON_AddNumberToObject(knx_json, "endpointOwnerGatewayId",
endpoint_owner_gateway_id);
}
const std::string router_error = knx_router == nullptr ? "" : knx_router->lastError();
cJSON_AddStringToObject(knx_json, "lastError",
knx_last_error.empty() ? router_error.c_str()
@@ -2269,6 +2294,8 @@ struct GatewayBridgeService::ChannelRuntime {
cJSON_AddNumberToObject(knx_json, "udpPort", effective_knx->udp_port);
cJSON_AddStringToObject(knx_json, "multicastAddress",
effective_knx->multicast_address.c_str());
cJSON_AddNumberToObject(knx_json, "ipInterfaceIndividualAddress",
effective_knx->ip_interface_individual_address);
cJSON_AddNumberToObject(knx_json, "individualAddress",
effective_knx->individual_address);
cJSON* serial_json = cJSON_CreateObject();
@@ -3066,6 +3093,25 @@ struct GatewayBridgeService::ChannelRuntime {
}
return ESP_ERR_INVALID_ARG;
}
if (config.ip_interface_individual_address == 0 ||
config.ip_interface_individual_address == 0xffff) {
if (error_message != nullptr) {
*error_message = "KNX IP interface individual address must be a configured address";
}
return ESP_ERR_INVALID_ARG;
}
if (config.individual_address == 0 || config.individual_address == 0xffff) {
if (error_message != nullptr) {
*error_message = "KNX-DALI gateway individual address must be a configured address";
}
return ESP_ERR_INVALID_ARG;
}
if (config.ip_interface_individual_address == config.individual_address) {
if (error_message != nullptr) {
*error_message = "KNX IP interface and KNX-DALI gateway addresses must differ";
}
return ESP_ERR_INVALID_ARG;
}
if (!config.ip_router_enabled || !GatewayKnxConfigUsesTpUart(config)) {
return ESP_OK;
}
@@ -3162,10 +3208,10 @@ struct GatewayBridgeService::ChannelRuntime {
GatewayKnxConfig config = service_config.default_knx_config.value();
const uint8_t channel_index = channel.channel_index;
config.main_group = static_cast<uint8_t>(std::min<int>(31, config.main_group + channel_index));
const uint16_t device = config.individual_address & 0x00ff;
const uint16_t device = config.ip_interface_individual_address & 0x00ff;
if (device > 0 && device + channel_index <= 0x00ff) {
config.individual_address = static_cast<uint16_t>((config.individual_address & 0xff00) |
(device + channel_index));
config.ip_interface_individual_address = static_cast<uint16_t>(
(config.ip_interface_individual_address & 0xff00) | (device + channel_index));
}
return config;
}
@@ -4415,6 +4461,40 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
}
return handleGet("knx", gateway_id.value());
}
if (action == "knx_programming_mode") {
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
if (body_root == nullptr) {
return ErrorResponse(ESP_ERR_INVALID_ARG, "KNX programming mode JSON is required");
}
const cJSON* enabled_item = cJSON_GetObjectItemCaseSensitive(body_root, "enabled");
if (!cJSON_IsBool(enabled_item)) {
cJSON_Delete(body_root);
return ErrorResponse(ESP_ERR_INVALID_ARG, "boolean enabled field is required");
}
const bool enabled = cJSON_IsTrue(enabled_item);
cJSON_Delete(body_root);
ChannelRuntime* owner = selectKnxEndpointRuntime();
if (owner == nullptr) {
return ErrorResponse(ESP_ERR_NOT_FOUND, "no KNX/IP endpoint owner is configured");
}
esp_err_t err = ESP_ERR_INVALID_STATE;
std::string detail = "KNX/IP router is unavailable";
{
LockGuard guard(owner->lock);
if (owner->knx_router != nullptr) {
err = owner->knx_router->setProgrammingMode(enabled);
detail = owner->knx_router->lastError();
}
owner->knx_last_error = err == ESP_OK ? std::string() : detail;
}
if (err != ESP_OK) {
return ErrorResponse(err, detail.empty() ? "failed to change KNX programming mode"
: detail.c_str());
}
return handleGet("status", gateway_id.value());
}
if (action == "knx_security_read_factory_key") {
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS) && \
defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)