feat(gateway): add DALI bus ID configuration and enhance group object write handling

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-16 03:31:12 +08:00
parent e79223c87e
commit 277379abd7
13 changed files with 288 additions and 37 deletions
+91 -1
View File
@@ -610,6 +610,23 @@ std::optional<int> MetadataInt(const DaliBridgeResult& result, const std::string
return getObjectInt(result.metadata, key);
}
std::string HexBytes(const uint8_t* data, size_t len) {
if (data == nullptr || len == 0) {
return {};
}
std::string out;
out.reserve(len * 3);
char buffer[4] = {0};
for (size_t index = 0; index < len; ++index) {
std::snprintf(buffer, sizeof(buffer), "%02X", data[index]);
out += buffer;
if (index + 1 < len) {
out.push_back(' ');
}
}
return out;
}
DaliBridgeRequest RequestForTarget(uint16_t group_address,
const GatewayKnxDaliTarget& target,
BridgeOperation operation) {
@@ -642,6 +659,17 @@ DaliBridgeResult ErrorResult(uint16_t group_address, const char* message) {
return result;
}
DaliBridgeResult IgnoredResult(uint16_t group_address, uint16_t group_object_number,
const char* reason) {
DaliBridgeResult result;
result.sequence = "knx-" + GatewayKnxGroupAddressString(group_address);
result.ok = true;
result.metadata["ignored"] = true;
result.metadata["groupObjectNumber"] = static_cast<int>(group_object_number);
result.metadata["reason"] = reason == nullptr ? "ignored" : reason;
return result;
}
bool SendAll(int sock, const uint8_t* data, size_t len, const sockaddr_in& remote) {
return sendto(sock, data, len, 0, reinterpret_cast<const sockaddr*>(&remote),
sizeof(remote)) == static_cast<int>(len);
@@ -716,6 +744,11 @@ std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value
config.main_group = static_cast<uint8_t>(
std::clamp(ObjectIntAny(object, {"mainGroup", "main_group"}).value_or(config.main_group),
0, 31));
config.dali_bus_id = static_cast<uint8_t>(std::clamp(
ObjectIntAny(object, {"daliBusId", "dali_bus_id", "targetDaliBusId",
"target_dali_bus_id"})
.value_or(config.dali_bus_id),
0, 15));
config.udp_port = static_cast<uint16_t>(std::clamp(
ObjectIntAny(object, {"udpPort", "port", "udp_port"}).value_or(config.udp_port), 1,
65535));
@@ -790,6 +823,7 @@ DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) {
out["etsDatabaseEnabled"] = config.ets_database_enabled;
out["mappingMode"] = GatewayKnxMappingModeToString(config.mapping_mode);
out["mainGroup"] = static_cast<int>(config.main_group);
out["daliBusId"] = static_cast<int>(config.dali_bus_id);
out["udpPort"] = static_cast<int>(config.udp_port);
out["multicastAddress"] = config.multicast_address;
out["ipInterfaceIndividualAddress"] =
@@ -1186,6 +1220,42 @@ DaliBridgeResult GatewayKnxBridge::handleGroupWrite(uint16_t group_address, cons
return executeForDecodedWrite(group_address, data_type.value(), target.value(), data, len);
}
DaliBridgeResult GatewayKnxBridge::handleGroupObjectWrite(uint16_t group_object_number,
const uint8_t* data, size_t len) {
const uint16_t group_address = GwReg1GroupAddressForObject(config_.main_group,
group_object_number);
const std::string payload = HexBytes(data, len);
ESP_LOGI(kTag, "OpenKNX KO write ko=%u derivedGa=%s len=%u payload=%s",
static_cast<unsigned>(group_object_number),
GatewayKnxGroupAddressString(group_address).c_str(), static_cast<unsigned>(len),
payload.c_str());
if (!config_.dali_router_enabled) {
return ErrorResult(group_address, "KNX to DALI router disabled");
}
const auto binding = GwReg1BindingForObject(config_.main_group, group_object_number);
if (!binding.has_value()) {
ESP_LOGW(kTag, "OpenKNX KO write ignored ko=%u: unsupported GW-REG1 object",
static_cast<unsigned>(group_object_number));
return IgnoredResult(group_address, group_object_number,
"unsupported GW-REG1 group object");
}
DaliBridgeResult result = executeForDecodedWrite(binding->group_address, binding->data_type,
binding->target, data, len);
result.metadata["source"] = "openknx_group_object";
result.metadata["groupObjectNumber"] = static_cast<int>(group_object_number);
result.metadata["objectRole"] = binding->object_role;
if (result.ok) {
ESP_LOGI(kTag, "OpenKNX KO write routed ko=%u role=%s target=%s",
static_cast<unsigned>(group_object_number), binding->object_role.c_str(),
TargetName(binding->target).c_str());
} else {
ESP_LOGW(kTag, "OpenKNX KO write failed ko=%u role=%s error=%s",
static_cast<unsigned>(group_object_number), binding->object_role.c_str(),
result.error.c_str());
}
return result;
}
bool GatewayKnxBridge::handleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
const uint8_t* data, size_t len,
std::vector<uint8_t>* response) {
@@ -1719,6 +1789,10 @@ void GatewayKnxTpIpRouter::setGroupWriteHandler(GroupWriteHandler handler) {
group_write_handler_ = std::move(handler);
}
void GatewayKnxTpIpRouter::setGroupObjectWriteHandler(GroupObjectWriteHandler handler) {
group_object_write_handler_ = std::move(handler);
}
const GatewayKnxConfig& GatewayKnxTpIpRouter::config() const { return config_; }
bool GatewayKnxTpIpRouter::tpUartOnline() const { return tp_uart_online_; }
@@ -1908,8 +1982,24 @@ esp_err_t GatewayKnxTpIpRouter::initializeRuntime() {
ESP_LOGD(kTag, "secure KNX group write not routed to DALI: %s", result.error.c_str());
}
});
ets_device_->setGroupObjectWriteHandler(
[this](uint16_t group_object_number, const uint8_t* data, size_t len) {
const DaliBridgeResult result = group_object_write_handler_
? group_object_write_handler_(group_object_number,
data, len)
: bridge_.handleGroupObjectWrite(group_object_number,
data, len);
const bool ignored = getObjectBool(result.metadata, "ignored").value_or(false);
if (ignored) {
const auto reason = getObjectString(result.metadata, "reason").value_or("ignored");
ESP_LOGW(kTag, "OpenKNX group object %u accepted by ETS but ignored: %s",
static_cast<unsigned>(group_object_number), reason.c_str());
} else if (!result.ok && !result.error.empty()) {
ESP_LOGW(kTag, "OpenKNX group object %u not routed to DALI: %s",
static_cast<unsigned>(group_object_number), result.error.c_str());
}
});
ets_device_->setBusFrameSender([this](const uint8_t* data, size_t len) {
routeOpenKnxGroupWrite(data, len, "KNX TP frame");
sendTunnelIndication(data, len);
sendRoutingIndication(data, len);
});