#include "gateway_knx_private.hpp" namespace gateway { namespace { GatewayKnxCloudRemoteConfig GatewayKnxCloudRemoteConfigFromValue(const DaliValue* value) { GatewayKnxCloudRemoteConfig config; if (value == nullptr || value->asObject() == nullptr) { return config; } const auto& object = *value->asObject(); config.enabled = ObjectBoolAny(object, {"enabled", "cloudRemoteEnabled", "cloud_remote_enabled"}) .value_or(config.enabled); config.mode = ObjectStringAny(object, {"mode", "remoteMode", "remote_mode"}) .value_or(config.mode); config.relay_endpoint = ObjectStringAny(object, {"relayEndpoint", "relay_endpoint", "endpoint"}) .value_or(config.relay_endpoint); config.mqtt_topic_prefix = ObjectStringAny(object, {"mqttTopicPrefix", "mqtt_topic_prefix", "topicPrefix", "topic_prefix"}) .value_or(config.mqtt_topic_prefix); config.auth_token_ref = ObjectStringAny(object, {"authTokenRef", "auth_token_ref", "tokenRef", "token_ref"}) .value_or(config.auth_token_ref); config.require_secure_tunnel = ObjectBoolAny(object, {"requireSecureTunnel", "require_secure_tunnel"}) .value_or(config.require_secure_tunnel); config.udp_punch_enabled = ObjectBoolAny(object, {"udpPunchEnabled", "udp_punch_enabled"}) .value_or(config.udp_punch_enabled); return config; } DaliValue GatewayKnxCloudRemoteConfigToValue(const GatewayKnxCloudRemoteConfig& config) { DaliValue::Object out; out["enabled"] = config.enabled; out["mode"] = config.mode; out["relayEndpoint"] = config.relay_endpoint; out["mqttTopicPrefix"] = config.mqtt_topic_prefix; out["authTokenRef"] = config.auth_token_ref; out["requireSecureTunnel"] = config.require_secure_tunnel; out["udpPunchEnabled"] = config.udp_punch_enabled; return DaliValue(std::move(out)); } } // namespace std::optional GatewayKnxOamRouterConfigFromValue( const DaliValue* value) { if (value == nullptr || value->asObject() == nullptr) { return std::nullopt; } const auto& object = *value->asObject(); GatewayKnxOamRouterConfig config; config.enabled = ObjectBoolAny(object, {"enabled", "oamRouterEnabled", "oam_router_enabled"}) .value_or(config.enabled); config.ets_database_enabled = ObjectBoolAny(object, {"etsDatabaseEnabled", "ets_database_enabled"}) .value_or(config.ets_database_enabled); config.secure_tunnel_enabled = ObjectBoolAny(object, {"secureTunnelEnabled", "secure_tunnel_enabled"}) .value_or(config.secure_tunnel_enabled); config.secure_routing_enabled = ObjectBoolAny(object, {"secureRoutingEnabled", "secure_routing_enabled"}) .value_or(config.secure_routing_enabled); config.individual_address = static_cast(std::clamp( ObjectIntAny(object, {"individualAddress", "individual_address", "routerAddress", "router_address"}) .value_or(config.individual_address), 0, 0xffff)); config.tunnel_address_base = static_cast(std::clamp( ObjectIntAny(object, {"tunnelAddressBase", "tunnel_address_base", "tunnelBaseAddress", "tunnel_base_address"}) .value_or(config.tunnel_address_base), 0, 0xffff)); config.programming_button_gpio = std::clamp( ObjectIntAny(object, {"programmingButtonGpio", "programming_button_gpio"}) .value_or(config.programming_button_gpio), -1, 48); config.programming_button_active_low = ObjectBoolAny(object, {"programmingButtonActiveLow", "programming_button_active_low"}) .value_or(config.programming_button_active_low); config.programming_led_gpio = std::clamp( ObjectIntAny(object, {"programmingLedGpio", "programming_led_gpio"}) .value_or(config.programming_led_gpio), -1, 48); config.programming_led_active_high = ObjectBoolAny(object, {"programmingLedActiveHigh", "programming_led_active_high"}) .value_or(config.programming_led_active_high); config.cloud_remote = GatewayKnxCloudRemoteConfigFromValue( ObjectValueAny(object, {"cloudRemote", "cloud_remote", "remoteAccess", "remote_access"})); return config; } DaliValue GatewayKnxOamRouterConfigToValue(const GatewayKnxOamRouterConfig& config) { DaliValue::Object out; out["enabled"] = config.enabled; out["etsDatabaseEnabled"] = config.ets_database_enabled; out["secureTunnelEnabled"] = config.secure_tunnel_enabled; out["secureRoutingEnabled"] = config.secure_routing_enabled; out["individualAddress"] = static_cast(config.individual_address); out["tunnelAddressBase"] = static_cast(config.tunnel_address_base); out["programmingButtonGpio"] = config.programming_button_gpio; out["programmingButtonActiveLow"] = config.programming_button_active_low; out["programmingLedGpio"] = config.programming_led_gpio; out["programmingLedActiveHigh"] = config.programming_led_active_high; out["cloudRemote"] = GatewayKnxCloudRemoteConfigToValue(config.cloud_remote); return DaliValue(std::move(out)); } std::optional GatewayKnxConfigFromValue(const DaliValue* value) { if (value == nullptr || value->asObject() == nullptr) { return std::nullopt; } const auto& object = *value->asObject(); GatewayKnxConfig config; config.dali_router_enabled = ObjectBoolAny(object, {"daliRouterEnabled", "dali_router_enabled"}) .value_or(config.dali_router_enabled); config.ip_router_enabled = ObjectBoolAny(object, {"ipRouterEnabled", "ip_router_enabled"}) .value_or(config.ip_router_enabled); config.tunnel_enabled = ObjectBoolAny(object, {"tunnelEnabled", "tunnel_enabled"}) .value_or(config.tunnel_enabled); config.multicast_enabled = ObjectBoolAny(object, {"multicastEnabled", "multicast_enabled"}) .value_or(config.multicast_enabled); if (const auto mode = ObjectStringAny(object, {"mappingMode", "mapping_mode"})) { config.mapping_mode = GatewayKnxMappingModeFromString(mode.value()); } config.ets_database_enabled = ObjectBoolAny(object, {"etsDatabaseEnabled", "ets_database_enabled"}) .value_or(config.ets_database_enabled); config.ets_associations = ParseEtsAssociations(object); config.main_group = static_cast( std::clamp(ObjectIntAny(object, {"mainGroup", "main_group"}).value_or(config.main_group), 0, 31)); config.dali_bus_id = static_cast(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(std::clamp( ObjectIntAny(object, {"udpPort", "port", "udp_port"}).value_or(config.udp_port), 1, 65535)); config.multicast_address = ObjectStringAny(object, {"multicastAddress", "multicast_address"}) .value_or(config.multicast_address); config.ip_interface_individual_address = static_cast(std::clamp( ObjectIntAny(object, {"ipInterfaceIndividualAddress", "ip_interface_individual_address", "ipInterfaceAddress", "ip_interface_address"}) .value_or(config.ip_interface_individual_address), 0, 0xffff)); config.individual_address = static_cast(std::clamp( ObjectIntAny(object, {"individualAddress", "individual_address", "knxDaliGatewayIndividualAddress", "knx_dali_gateway_individual_address", "deviceIndividualAddress", "device_individual_address"}) .value_or(config.individual_address), 0, 0xffff)); config.programming_button_gpio = std::clamp( ObjectIntAny(object, {"programmingButtonGpio", "programming_button_gpio"}) .value_or(config.programming_button_gpio), -1, 48); config.programming_button_active_low = ObjectBoolAny(object, {"programmingButtonActiveLow", "programming_button_active_low"}) .value_or(config.programming_button_active_low); config.programming_led_gpio = std::clamp( ObjectIntAny(object, {"programmingLedGpio", "programming_led_gpio"}) .value_or(config.programming_led_gpio), -1, 48); config.programming_led_active_high = ObjectBoolAny(object, {"programmingLedActiveHigh", "programming_led_active_high"}) .value_or(config.programming_led_active_high); if (const auto oam_router = GatewayKnxOamRouterConfigFromValue( ObjectValueAny(object, {"oamRouter", "oam_router", "routerApplication", "router_application"}))) { config.oam_router = oam_router.value(); } else { config.oam_router.enabled = ObjectBoolAny(object, {"oamRouterEnabled", "oam_router_enabled"}) .value_or(config.oam_router.enabled); } const auto* tp_uart = getObjectValue(object, "tpUart"); if (tp_uart == nullptr) { tp_uart = getObjectValue(object, "tp_uart"); } if (tp_uart != nullptr && tp_uart->asObject() != nullptr) { const auto& serial = *tp_uart->asObject(); config.tp_uart.uart_port = std::clamp( ObjectIntAny(serial, {"uartPort", "uart_port"}).value_or(config.tp_uart.uart_port), -1, 2); config.tp_uart.tx_pin = ObjectIntAny(serial, {"txPin", "tx_pin"}).value_or(config.tp_uart.tx_pin); config.tp_uart.rx_pin = ObjectIntAny(serial, {"rxPin", "rx_pin"}).value_or(config.tp_uart.rx_pin); config.tp_uart.baudrate = static_cast(std::max( 1200, ObjectIntAny(serial, {"baudrate", "baud"}).value_or(config.tp_uart.baudrate))); config.tp_uart.rx_buffer_size = static_cast(std::max( 128, ObjectIntAny(serial, {"rxBufferSize", "rx_buffer_size"}) .value_or(static_cast(config.tp_uart.rx_buffer_size)))); config.tp_uart.tx_buffer_size = static_cast(std::max( 128, ObjectIntAny(serial, {"txBufferSize", "tx_buffer_size"}) .value_or(static_cast(config.tp_uart.tx_buffer_size)))); config.tp_uart.startup_timeout_ms = static_cast(std::max( 0, ObjectIntAny(serial, {"startupTimeoutMs", "startup_timeout_ms"}) .value_or(static_cast(config.tp_uart.startup_timeout_ms)))); config.tp_uart.read_timeout_ms = static_cast(std::max( 1, ObjectIntAny(serial, {"readTimeoutMs", "read_timeout_ms"}) .value_or(static_cast(config.tp_uart.read_timeout_ms)))); config.tp_uart.nine_bit_mode = ObjectBoolAny( serial, {"nineBitMode", "nine_bit_mode", "use9BitMode", "use_9_bit_mode"}) .value_or(config.tp_uart.nine_bit_mode); } return config; } DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) { DaliValue::Object out; out["daliRouterEnabled"] = config.dali_router_enabled; out["ipRouterEnabled"] = config.ip_router_enabled; out["tunnelEnabled"] = config.tunnel_enabled; out["multicastEnabled"] = config.multicast_enabled; out["etsDatabaseEnabled"] = config.ets_database_enabled; out["mappingMode"] = GatewayKnxMappingModeToString(config.mapping_mode); out["mainGroup"] = static_cast(config.main_group); out["daliBusId"] = static_cast(config.dali_bus_id); out["udpPort"] = static_cast(config.udp_port); out["multicastAddress"] = config.multicast_address; out["ipInterfaceIndividualAddress"] = static_cast(config.ip_interface_individual_address); out["individualAddress"] = static_cast(config.individual_address); out["programmingButtonGpio"] = config.programming_button_gpio; out["programmingButtonActiveLow"] = config.programming_button_active_low; out["programmingLedGpio"] = config.programming_led_gpio; out["programmingLedActiveHigh"] = config.programming_led_active_high; out["oamRouter"] = GatewayKnxOamRouterConfigToValue(config.oam_router); DaliValue::Object serial; serial["uartPort"] = config.tp_uart.uart_port; serial["txPin"] = config.tp_uart.tx_pin; serial["rxPin"] = config.tp_uart.rx_pin; serial["baudrate"] = static_cast(config.tp_uart.baudrate); serial["rxBufferSize"] = static_cast(config.tp_uart.rx_buffer_size); serial["txBufferSize"] = static_cast(config.tp_uart.tx_buffer_size); serial["startupTimeoutMs"] = static_cast(config.tp_uart.startup_timeout_ms); serial["readTimeoutMs"] = static_cast(config.tp_uart.read_timeout_ms); serial["nineBitMode"] = config.tp_uart.nine_bit_mode; out["tpUart"] = std::move(serial); DaliValue::Array ets_associations; ets_associations.reserve(config.ets_associations.size()); for (const auto& association : config.ets_associations) { DaliValue::Object entry; entry["groupAddress"] = static_cast(association.group_address); entry["groupObjectNumber"] = static_cast(association.group_object_number); ets_associations.emplace_back(std::move(entry)); } out["etsAssociations"] = std::move(ets_associations); return DaliValue(std::move(out)); } bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config) { return config.ip_router_enabled && config.tp_uart.uart_port >= 0; } const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode) { switch (mode) { case GatewayKnxMappingMode::kEtsDatabase: return "ets_database"; case GatewayKnxMappingMode::kGwReg1Direct: return "gw_reg1_direct"; case GatewayKnxMappingMode::kManual: return "manual"; case GatewayKnxMappingMode::kFormula: default: return "formula"; } } GatewayKnxMappingMode GatewayKnxMappingModeFromString(const std::string& value) { const std::string normalized = NormalizeModeString(value); if (normalized == "gwreg1direct" || normalized == "gwreg1" || normalized == "gwreg1channel" || normalized == "channelindex") { return GatewayKnxMappingMode::kGwReg1Direct; } if (normalized == "manual" || normalized == "database" || normalized == "db") { return GatewayKnxMappingMode::kManual; } if (normalized == "etsdatabase" || normalized == "ets" || normalized == "openknx") { return GatewayKnxMappingMode::kEtsDatabase; } return GatewayKnxMappingMode::kFormula; } const char* GatewayKnxDataTypeToString(GatewayKnxDaliDataType data_type) { switch (data_type) { case GatewayKnxDaliDataType::kSwitch: return "switch"; case GatewayKnxDaliDataType::kBrightness: return "brightness"; case GatewayKnxDaliDataType::kBrightnessRelative: return "brightness_relative"; case GatewayKnxDaliDataType::kColorTemperature: return "color_temperature"; case GatewayKnxDaliDataType::kRgb: return "rgb"; case GatewayKnxDaliDataType::kScene: return "scene"; case GatewayKnxDaliDataType::kUnknown: default: return "unknown"; } } const char* GatewayKnxTargetKindToString(GatewayKnxDaliTargetKind kind) { switch (kind) { case GatewayKnxDaliTargetKind::kBroadcast: return "broadcast"; case GatewayKnxDaliTargetKind::kShortAddress: return "short_address"; case GatewayKnxDaliTargetKind::kGroup: return "group"; case GatewayKnxDaliTargetKind::kNone: default: return "none"; } } std::optional GatewayKnxDaliDataTypeForMiddleGroup( uint8_t middle_group) { switch (middle_group) { case 1: return GatewayKnxDaliDataType::kSwitch; case 2: return GatewayKnxDaliDataType::kBrightness; case 3: return GatewayKnxDaliDataType::kColorTemperature; case 4: return GatewayKnxDaliDataType::kRgb; default: return std::nullopt; } } std::optional GatewayKnxDaliTargetForSubgroup(uint8_t sub_group) { if (sub_group == 0) { return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kBroadcast, 127}; } if (sub_group >= 1 && sub_group <= 64) { return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kShortAddress, static_cast(sub_group - 1)}; } if (sub_group >= 65 && sub_group <= 80) { return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kGroup, static_cast(sub_group - 65)}; } return std::nullopt; } uint16_t GatewayKnxGroupAddress(uint8_t main_group, uint8_t middle_group, uint8_t sub_group) { return static_cast(((main_group & 0x1f) << 11) | ((middle_group & 0x07) << 8) | sub_group); } std::string GatewayKnxGroupAddressString(uint16_t group_address) { const int main = (group_address >> 11) & 0x1f; const int middle = (group_address >> 8) & 0x07; const int sub = group_address & 0xff; return std::to_string(main) + "/" + std::to_string(middle) + "/" + std::to_string(sub); } } // namespace gateway