feat(gateway): enhance KNX support with DALI integration and configuration updates

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-12 20:34:33 +08:00
parent e58115d303
commit de0edd5ad9
11 changed files with 395 additions and 30 deletions
@@ -57,6 +57,10 @@ constexpr uint32_t kBacnetMaxObjectInstance = 4194303;
constexpr uint32_t kBacnetReliabilityNoFaultDetected = 0;
constexpr uint32_t kBacnetReliabilityCommunicationFailure = 12;
constexpr const char* kModbusManagementPrefix = "@DALIGW";
constexpr uint8_t kDaliGroupRawMin = 0x80;
constexpr uint8_t kDaliGroupRawMax = 0x9F;
constexpr uint8_t kDaliCmdOff = 0x00;
constexpr uint8_t kDaliCmdRecallMax = 0x05;
struct GatewayBridgeStoredConfig {
BridgeRuntimeConfig bridge;
@@ -71,6 +75,11 @@ struct BridgeDiscoveryEntry {
DaliDomainSnapshot discovery;
};
struct DaliKnxStatusUpdate {
GatewayKnxDaliTarget target;
uint8_t actual_level{0};
};
using BridgeDiscoveryInventory = std::map<int, BridgeDiscoveryEntry>;
class LockGuard {
@@ -220,6 +229,51 @@ bool ValidDaliAddress(int address) {
return address >= 0 && address <= 127;
}
std::optional<GatewayKnxDaliTarget> DecodeKnxDaliTarget(uint8_t raw_addr) {
if (raw_addr <= 0x7F) {
return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kShortAddress,
static_cast<int>(raw_addr >> 1)};
}
if (raw_addr >= kDaliGroupRawMin && raw_addr <= kDaliGroupRawMax) {
return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kGroup,
static_cast<int>((raw_addr - kDaliGroupRawMin) >> 1)};
}
return std::nullopt;
}
std::optional<DaliKnxStatusUpdate> DecodeDaliKnxStatusUpdate(const DaliRawFrame& frame) {
if (frame.data.size() != 2 && frame.data.size() != 3) {
return std::nullopt;
}
uint8_t raw_addr = 0;
uint8_t command = 0;
if (frame.data.size() == 2) {
raw_addr = frame.data[0];
command = frame.data[1];
if (raw_addr == 0xBE) {
return std::nullopt;
}
} else {
raw_addr = frame.data[1];
command = frame.data[2];
}
auto target = DecodeKnxDaliTarget(raw_addr);
if (!target.has_value()) {
return std::nullopt;
}
if ((raw_addr & 0x01U) == 0) {
if (command > 254) {
return std::nullopt;
}
return DaliKnxStatusUpdate{*target, command};
}
if (command == kDaliCmdOff || command == kDaliCmdRecallMax) {
return DaliKnxStatusUpdate{*target,
static_cast<uint8_t>(command == kDaliCmdOff ? 0 : 254)};
}
return std::nullopt;
}
bool ValidShortAddress(int address) {
return address >= 0 && address <= kMaxDaliShortAddress;
}
@@ -3652,6 +3706,9 @@ esp_err_t GatewayBridgeService::start() {
runtimes_.push_back(std::move(runtime));
}
dali_domain_.addRawFrameSink(
[this](const DaliRawFrame& frame) { handleDaliRawFrame(frame); });
std::set<int> used_serial_uarts;
if (config_.modbus_enabled && config_.modbus_startup_enabled) {
std::set<uint16_t> used_modbus_ports;
@@ -3708,6 +3765,22 @@ const GatewayBridgeService::ChannelRuntime* GatewayBridgeService::findRuntime(
return nullptr;
}
void GatewayBridgeService::handleDaliRawFrame(const DaliRawFrame& frame) {
const auto update = DecodeDaliKnxStatusUpdate(frame);
if (!update.has_value()) {
return;
}
auto* runtime = findRuntime(frame.gateway_id);
if (runtime == nullptr) {
return;
}
LockGuard guard(runtime->lock);
if (!runtime->knx_started || runtime->knx_router == nullptr) {
return;
}
runtime->knx_router->publishDaliStatus(update->target, update->actual_level);
}
void GatewayBridgeService::collectUsedRuntimeResources(
uint8_t except_gateway_id,
std::set<uint16_t>* modbus_tcp_ports,