feat(gateway): enhance DALI and KNX settings management with instance support
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
menu "Gateway App"
|
||||
|
||||
menu "DALI Settings"
|
||||
|
||||
config GATEWAY_CHANNEL_COUNT
|
||||
int "Gateway channel count"
|
||||
range 1 2
|
||||
@@ -329,6 +331,8 @@ config GATEWAY_DALI_BAUDRATE
|
||||
help
|
||||
Runtime baudrate used when initializing the local DALI bus.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Gateway Startup Services"
|
||||
|
||||
config GATEWAY_BLE_SUPPORTED
|
||||
@@ -620,6 +624,17 @@ config GATEWAY_START_BACNET_BRIDGE_ENABLED
|
||||
help
|
||||
Starts configured BACnet/IP object bindings at boot. Disabled by default so the UDP BACnet/IP port is opened only after provisioning or explicit runtime start.
|
||||
|
||||
menu "KNX Settings"
|
||||
|
||||
config GATEWAY_KNX_INSTANCE_COUNT
|
||||
int "Enabled KNX instance count"
|
||||
range 1 16
|
||||
default 1
|
||||
help
|
||||
Number of enabled KNX logical instances exposed by management APIs.
|
||||
Instance 0 preserves the legacy serial/FDSK identity; each additional
|
||||
instance derives its serial/FDSK from the base identity plus instance id.
|
||||
|
||||
config GATEWAY_KNX_BRIDGE_SUPPORTED
|
||||
bool "KNX to DALI bridge is supported"
|
||||
depends on GATEWAY_BRIDGE_SUPPORTED && (GATEWAY_WIFI_SUPPORTED || GATEWAY_ETHERNET_SUPPORTED)
|
||||
@@ -956,16 +971,16 @@ config GATEWAY_KNX_TP_UART_9BIT_MODE
|
||||
mode commonly described as 19200 baud 9-bit UART. Disable only for
|
||||
hardware wired for 8N1 host UART mode.
|
||||
|
||||
config GATEWAY_KNX_TP_FULL_IP_FORWARD
|
||||
bool "Mirror all physical KNX TP telegrams to KNXnet/IP"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED && GATEWAY_KNX_TP_UART_PORT >= 0
|
||||
default n
|
||||
help
|
||||
Mirrors physical KNX TP telegrams received from the TP-UART line back
|
||||
out through KNXnet/IP tunnelling and multicast even when the gateway
|
||||
runs the single-interface ETS device runtime. Enable this when ETS must
|
||||
monitor or download other TP devices through the gateway's IP endpoint.
|
||||
Leave it disabled to preserve the narrower default forwarding behavior.
|
||||
config GATEWAY_KNX_TP_FULL_IP_FORWARD
|
||||
bool "Mirror all physical KNX TP telegrams to KNXnet/IP"
|
||||
depends on GATEWAY_KNX_BRIDGE_SUPPORTED && GATEWAY_KNX_TP_UART_PORT >= 0
|
||||
default n
|
||||
help
|
||||
Mirrors physical KNX TP telegrams received from the TP-UART line back
|
||||
out through KNXnet/IP tunnelling and multicast even when the gateway
|
||||
runs the single-interface ETS device runtime. Enable this when ETS must
|
||||
monitor or download other TP devices through the gateway's IP endpoint.
|
||||
Leave it disabled to preserve the narrower default forwarding behavior.
|
||||
|
||||
config GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
|
||||
int "KNX/IP bridge task stack bytes"
|
||||
@@ -979,6 +994,8 @@ config GATEWAY_BRIDGE_KNX_TASK_PRIORITY
|
||||
range 1 10
|
||||
default 5
|
||||
|
||||
endmenu
|
||||
|
||||
config GATEWAY_CLOUD_BRIDGE_SUPPORTED
|
||||
bool "MQTT cloud bridge is supported"
|
||||
depends on GATEWAY_BRIDGE_SUPPORTED && (GATEWAY_WIFI_SUPPORTED || GATEWAY_ETHERNET_SUPPORTED)
|
||||
|
||||
@@ -596,6 +596,10 @@ CONFIG_PARTITION_TABLE_MD5=y
|
||||
#
|
||||
# Gateway App
|
||||
#
|
||||
|
||||
#
|
||||
# DALI Settings
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL_COUNT=2
|
||||
|
||||
#
|
||||
@@ -640,6 +644,7 @@ CONFIG_GATEWAY_CACHE_OUTSIDE_BUS_FIRST=y
|
||||
# end of Gateway Cache
|
||||
|
||||
# CONFIG_GATEWAY_ENABLE_DALI_BUS is not set
|
||||
# end of DALI Settings
|
||||
|
||||
#
|
||||
# Gateway Startup Services
|
||||
@@ -683,6 +688,10 @@ CONFIG_GATEWAY_MODBUS_TCP_PORT=1502
|
||||
CONFIG_GATEWAY_MODBUS_UNIT_ID=1
|
||||
CONFIG_GATEWAY_BACNET_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_BACNET_BRIDGE_ENABLED is not set
|
||||
|
||||
#
|
||||
# KNX Settings
|
||||
#
|
||||
CONFIG_GATEWAY_KNX_BRIDGE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_START_KNX_BRIDGE_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED=y
|
||||
@@ -716,6 +725,8 @@ CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y
|
||||
CONFIG_GATEWAY_KNX_TP_FULL_IP_FORWARD=y
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
|
||||
# end of KNX Settings
|
||||
|
||||
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set
|
||||
CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE=6144
|
||||
|
||||
@@ -40,10 +40,14 @@ struct IpSecureCredentialStatus {
|
||||
|
||||
bool LoadFactoryFdsk(uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadFactoryFdskInfo();
|
||||
bool LoadFactoryFdskForInstance(uint32_t instance_id, uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadFactoryFdskInfoForInstance(uint32_t instance_id);
|
||||
bool GenerateFactoryFdsk(FactoryFdskInfo* info = nullptr);
|
||||
bool WriteFactoryFdskHex(const std::string& hex_key, FactoryFdskInfo* info = nullptr);
|
||||
bool ResetFactoryFdskCache(FactoryFdskInfo* info = nullptr);
|
||||
bool ResetFactorySecurityForInstance(uint32_t instance_id, FactoryFdskInfo* info = nullptr);
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayload();
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayloadForInstance(uint32_t instance_id);
|
||||
|
||||
bool LoadOamFactoryFdsk(uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadOamFactoryFdskInfo();
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "lwip/inet.h"
|
||||
@@ -64,6 +66,10 @@ constexpr uint32_t kBacnetReliabilityCommunicationFailure = 12;
|
||||
constexpr const char* kModbusManagementPrefix = "@DALIGW";
|
||||
constexpr uint8_t kDaliGroupRawMin = 0x80;
|
||||
constexpr uint8_t kDaliGroupRawMax = 0x9F;
|
||||
#ifndef CONFIG_GATEWAY_KNX_INSTANCE_COUNT
|
||||
#define CONFIG_GATEWAY_KNX_INSTANCE_COUNT 1
|
||||
#endif
|
||||
constexpr uint32_t kKnxEnabledInstanceCount = CONFIG_GATEWAY_KNX_INSTANCE_COUNT;
|
||||
|
||||
void ConfigureDaliCppLogging() {
|
||||
static bool configured = false;
|
||||
@@ -199,7 +205,7 @@ cJSON* FactoryCertificateToCjson(const openknx::FactoryCertificatePayload& certi
|
||||
return root;
|
||||
}
|
||||
|
||||
cJSON* IpSecureCredentialStatusToCjson(
|
||||
[[maybe_unused]] cJSON* IpSecureCredentialStatusToCjson(
|
||||
const openknx::IpSecureCredentialStatus& status) {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == nullptr) {
|
||||
@@ -393,6 +399,50 @@ std::optional<int> QueryInt(std::string_view query, std::string_view primary,
|
||||
return ParseInt(QueryValue(query, fallback));
|
||||
}
|
||||
|
||||
std::optional<uint32_t> ValidKnxInstanceId(int value) {
|
||||
if (value < 0 || static_cast<uint32_t>(value) >= kKnxEnabledInstanceCount) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
std::string KnxInstanceIdRangeMessage() {
|
||||
return "KNX instanceId must be in range 0.." +
|
||||
std::to_string(kKnxEnabledInstanceCount - 1);
|
||||
}
|
||||
|
||||
std::optional<uint32_t> QueryKnxInstanceId(std::string_view query) {
|
||||
return ValidKnxInstanceId(QueryInt(query, "instanceId", "instance").value_or(0));
|
||||
}
|
||||
|
||||
bool EnsureBridgeNvsReady() {
|
||||
const esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
if (nvs_flash_erase() != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
return nvs_flash_init() == ESP_OK;
|
||||
}
|
||||
return err == ESP_OK || err == ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
bool EraseOpenKnxEeprom(const std::string& nvs_namespace) {
|
||||
if (nvs_namespace.empty() || !EnsureBridgeNvsReady()) {
|
||||
return false;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
esp_err_t err = nvs_open(nvs_namespace.c_str(), NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err == ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
err = nvs_erase_key(handle, "eeprom");
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
return err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
std::optional<int> JsonIntAny(const cJSON* parent, const char* primary, const char* fallback) {
|
||||
auto value = JsonInt(parent, primary);
|
||||
if (value.has_value() || fallback == nullptr) {
|
||||
@@ -401,6 +451,10 @@ std::optional<int> JsonIntAny(const cJSON* parent, const char* primary, const ch
|
||||
return JsonInt(parent, fallback);
|
||||
}
|
||||
|
||||
std::optional<uint32_t> JsonKnxInstanceId(const cJSON* parent) {
|
||||
return ValidKnxInstanceId(JsonIntAny(parent, "instanceId", "instance").value_or(0));
|
||||
}
|
||||
|
||||
bool ValidDaliAddress(int address) {
|
||||
return address >= 0 && address <= 127;
|
||||
}
|
||||
@@ -2229,7 +2283,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
return JsonOk(BridgeResultToCjson(result));
|
||||
}
|
||||
|
||||
cJSON* knxStatusCjson() const {
|
||||
cJSON* knxStatusCjson(uint32_t instance_id = 0) const {
|
||||
cJSON* knx_json = cJSON_CreateObject();
|
||||
if (knx_json == nullptr) {
|
||||
return nullptr;
|
||||
@@ -2254,6 +2308,9 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
}
|
||||
const auto effective_knx =
|
||||
knx_config.has_value() ? knx_config : service_config.default_knx_config;
|
||||
cJSON_AddNumberToObject(knx_json, "instanceId", static_cast<double>(instance_id));
|
||||
cJSON_AddNumberToObject(knx_json, "enabledInstanceCount",
|
||||
static_cast<double>(kKnxEnabledInstanceCount));
|
||||
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);
|
||||
@@ -2307,13 +2364,14 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddStringToObject(security_json, "storage", "none");
|
||||
#endif
|
||||
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
|
||||
const auto fdsk_info = openknx::LoadFactoryFdskInfo();
|
||||
const auto fdsk_info = openknx::LoadFactoryFdskInfoForInstance(instance_id);
|
||||
cJSON* fdsk_json = FactoryFdskInfoToCjson(fdsk_info, true);
|
||||
if (fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(security_json, "factorySetupKey", fdsk_json);
|
||||
}
|
||||
cJSON* certificate_json =
|
||||
FactoryCertificateToCjson(openknx::BuildFactoryCertificatePayload(), false);
|
||||
FactoryCertificateToCjson(
|
||||
openknx::BuildFactoryCertificatePayloadForInstance(instance_id), false);
|
||||
if (certificate_json != nullptr) {
|
||||
cJSON_AddItemToObject(security_json, "factoryCertificate", certificate_json);
|
||||
}
|
||||
@@ -2322,27 +2380,6 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddItemToObject(security_json, "failures", failures_json);
|
||||
}
|
||||
#endif
|
||||
cJSON* oam_security_json = cJSON_CreateObject();
|
||||
if (oam_security_json != nullptr) {
|
||||
cJSON* oam_fdsk_json = FactoryFdskInfoToCjson(
|
||||
openknx::LoadOamFactoryFdskInfo(), false);
|
||||
if (oam_fdsk_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "factorySetupKey", oam_fdsk_json);
|
||||
}
|
||||
cJSON* oam_certificate_json = FactoryCertificateToCjson(
|
||||
openknx::BuildOamFactoryCertificatePayload(), false);
|
||||
if (oam_certificate_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "factoryCertificate",
|
||||
oam_certificate_json);
|
||||
}
|
||||
cJSON* oam_credentials_json = IpSecureCredentialStatusToCjson(
|
||||
openknx::LoadOamIpSecureCredentialStatus());
|
||||
if (oam_credentials_json != nullptr) {
|
||||
cJSON_AddItemToObject(oam_security_json, "ipSecureCredentials",
|
||||
oam_credentials_json);
|
||||
}
|
||||
cJSON_AddItemToObject(security_json, "oamRouter", oam_security_json);
|
||||
}
|
||||
cJSON_AddItemToObject(knx_json, "security", security_json);
|
||||
}
|
||||
if (effective_knx.has_value()) {
|
||||
@@ -2491,13 +2528,13 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
return root;
|
||||
}
|
||||
|
||||
GatewayBridgeHttpResponse knxStatusJson() const {
|
||||
GatewayBridgeHttpResponse knxStatusJson(uint32_t instance_id = 0) const {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate KNX status JSON");
|
||||
}
|
||||
cJSON_AddNumberToObject(root, "gatewayId", channel.gateway_id);
|
||||
cJSON* knx_json = knxStatusCjson();
|
||||
cJSON* knx_json = knxStatusCjson(instance_id);
|
||||
if (knx_json == nullptr) {
|
||||
cJSON_Delete(root);
|
||||
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate KNX status JSON");
|
||||
@@ -3035,14 +3072,17 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
switch (point.generated_kind) {
|
||||
case GatewayModbusGeneratedKind::kShortOn:
|
||||
case GatewayModbusGeneratedKind::kShortRecallMax:
|
||||
domain.markHostCommandFrame(channel.gateway_id, raw_command_address, DALI_CMD_RECALL_MAX);
|
||||
sent = domain.on(channel.gateway_id, point.short_address);
|
||||
mirrored_command = DALI_CMD_RECALL_MAX;
|
||||
break;
|
||||
case GatewayModbusGeneratedKind::kShortOff:
|
||||
domain.markHostCommandFrame(channel.gateway_id, raw_command_address, DALI_CMD_OFF);
|
||||
sent = domain.off(channel.gateway_id, point.short_address);
|
||||
mirrored_command = DALI_CMD_OFF;
|
||||
break;
|
||||
case GatewayModbusGeneratedKind::kShortRecallMin:
|
||||
domain.markHostCommandFrame(channel.gateway_id, raw_command_address, DALI_CMD_RECALL_MIN);
|
||||
sent = domain.sendRaw(channel.gateway_id, raw_command_address, DALI_CMD_RECALL_MIN);
|
||||
mirrored_command = DALI_CMD_RECALL_MIN;
|
||||
break;
|
||||
@@ -3065,6 +3105,8 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
if (value > 254) {
|
||||
return false;
|
||||
}
|
||||
domain.markHostCommandFrame(channel.gateway_id, RawArcAddressFromDec(point.short_address),
|
||||
static_cast<uint8_t>(value));
|
||||
if (domain.setBright(channel.gateway_id, point.short_address, value)) {
|
||||
cache.mirrorDaliCommand(channel.gateway_id, RawArcAddressFromDec(point.short_address),
|
||||
static_cast<uint8_t>(value));
|
||||
@@ -3072,8 +3114,10 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
}
|
||||
return false;
|
||||
case GatewayModbusGeneratedKind::kShortColorTemperature:
|
||||
domain.markHostActivity(channel.gateway_id);
|
||||
return domain.setColTemp(channel.gateway_id, point.short_address, value);
|
||||
case GatewayModbusGeneratedKind::kShortGroupMask:
|
||||
domain.markHostActivity(channel.gateway_id);
|
||||
if (domain.applyGroupMask(channel.gateway_id, point.short_address, value)) {
|
||||
cache.setDaliGroupMask(channel.gateway_id, static_cast<uint8_t>(point.short_address),
|
||||
value);
|
||||
@@ -3120,6 +3164,7 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
domain_settings.max_level = current.max_level;
|
||||
domain_settings.fade_time = current.fade_time;
|
||||
domain_settings.fade_rate = current.fade_rate;
|
||||
domain.markHostActivity(channel.gateway_id);
|
||||
if (domain.applyAddressSettings(channel.gateway_id, point.short_address, domain_settings)) {
|
||||
cache.setDaliSettings(channel.gateway_id, static_cast<uint8_t>(point.short_address),
|
||||
current);
|
||||
@@ -4415,7 +4460,12 @@ GatewayBridgeHttpResponse GatewayBridgeService::handleGet(
|
||||
return JsonOk(runtime->statusCjson());
|
||||
}
|
||||
if (action == "knx_status") {
|
||||
return runtime->knxStatusJson();
|
||||
const auto instance_id = QueryKnxInstanceId(query);
|
||||
if (!instance_id.has_value()) {
|
||||
const std::string message = KnxInstanceIdRangeMessage();
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, message.c_str());
|
||||
}
|
||||
return runtime->knxStatusJson(instance_id.value());
|
||||
}
|
||||
if (action == "config") {
|
||||
return runtime->configJson();
|
||||
@@ -4762,6 +4812,61 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
|
||||
}
|
||||
return handleGet("status", gateway_id.value());
|
||||
}
|
||||
if (action == "knx_factory_reset") {
|
||||
cJSON* body_root = body.empty() ? nullptr : cJSON_ParseWithLength(body.data(), body.size());
|
||||
if (body_root == nullptr) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "KNX factory reset confirmation JSON is required");
|
||||
}
|
||||
const char* confirm = JsonString(body_root, "confirm");
|
||||
const bool confirmed = confirm != nullptr &&
|
||||
std::string_view(confirm) == "reset-knx-factory-defaults";
|
||||
const auto instance_id = JsonKnxInstanceId(body_root);
|
||||
cJSON_Delete(body_root);
|
||||
if (!confirmed) {
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, "KNX factory reset confirmation is required");
|
||||
}
|
||||
if (!instance_id.has_value()) {
|
||||
const std::string message = KnxInstanceIdRangeMessage();
|
||||
return ErrorResponse(ESP_ERR_INVALID_ARG, message.c_str());
|
||||
}
|
||||
|
||||
const bool reset_runtime = instance_id.value() == 0;
|
||||
const bool restart_router = reset_runtime &&
|
||||
(runtime->knx_started ||
|
||||
(runtime->knx_router != nullptr && runtime->knx_router->started()));
|
||||
if (restart_router) {
|
||||
const esp_err_t stop_err = stopKnxEndpoint(runtime);
|
||||
if (stop_err != ESP_OK) {
|
||||
return ErrorResponse(stop_err, "failed to stop KNX/IP bridge before factory reset");
|
||||
}
|
||||
}
|
||||
|
||||
std::string nvs_namespace = runtime->openKnxNamespace();
|
||||
if (instance_id.value() != 0) {
|
||||
nvs_namespace += "_" + std::to_string(instance_id.value());
|
||||
}
|
||||
if (!EraseOpenKnxEeprom(nvs_namespace)) {
|
||||
return ErrorResponse(ESP_FAIL, "failed to erase KNX runtime data");
|
||||
}
|
||||
|
||||
openknx::FactoryFdskInfo info;
|
||||
if (!openknx::ResetFactorySecurityForInstance(instance_id.value(), &info)) {
|
||||
return ErrorResponse(ESP_FAIL, "failed to restore KNX factory security key");
|
||||
}
|
||||
|
||||
if (restart_router) {
|
||||
std::set<int> used_serial_uarts;
|
||||
collectUsedRuntimeResources(runtime->channel.gateway_id, nullptr, nullptr, &used_serial_uarts);
|
||||
const esp_err_t start_err = startKnxEndpoint(runtime, &used_serial_uarts);
|
||||
if (start_err != ESP_OK) {
|
||||
return ErrorResponse(start_err, runtime->knx_last_error.empty()
|
||||
? "failed to restart KNX/IP bridge after factory reset"
|
||||
: runtime->knx_last_error.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return runtime->knxStatusJson(instance_id.value());
|
||||
}
|
||||
if (action == "knx_security_read_factory_key") {
|
||||
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS) && \
|
||||
defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
|
||||
|
||||
@@ -45,7 +45,7 @@ constexpr char kBase32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
constexpr char kHexAlphabet[] = "0123456789ABCDEF";
|
||||
|
||||
struct FactoryFdskContext {
|
||||
const char* nvsNamespace;
|
||||
std::string nvsNamespace;
|
||||
const char* productIdentity;
|
||||
const char* derivationLabel;
|
||||
uint16_t manufacturerId;
|
||||
@@ -55,7 +55,7 @@ struct FactoryFdskContext {
|
||||
bool clearOpenKnxCache;
|
||||
};
|
||||
|
||||
constexpr FactoryFdskContext kReg1Context{
|
||||
const FactoryFdskContext kReg1Context{
|
||||
kNamespace,
|
||||
kProductIdentity,
|
||||
kFdskDerivationLabel,
|
||||
@@ -65,7 +65,7 @@ constexpr FactoryFdskContext kReg1Context{
|
||||
gateway::knx_internal::kReg1DaliSerialMacIncrement,
|
||||
true};
|
||||
|
||||
constexpr FactoryFdskContext kOamContext{
|
||||
const FactoryFdskContext kOamContext{
|
||||
kOamNamespace,
|
||||
kOamProductIdentity,
|
||||
kOamFdskDerivationLabel,
|
||||
@@ -77,6 +77,16 @@ constexpr FactoryFdskContext kOamContext{
|
||||
|
||||
extern "C" void knx_platform_clear_cached_fdsk() __attribute__((weak));
|
||||
|
||||
FactoryFdskContext reg1ContextForInstance(uint32_t instance_id) {
|
||||
FactoryFdskContext context = kReg1Context;
|
||||
context.serialMacIncrement = gateway::knx_internal::kReg1DaliSerialMacIncrement + instance_id;
|
||||
context.clearOpenKnxCache = instance_id == 0;
|
||||
if (instance_id != 0) {
|
||||
context.nvsNamespace = std::string(kNamespace) + "_" + std::to_string(instance_id);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
std::string hexValue(uint32_t value, int width) {
|
||||
std::array<char, 9> buffer{};
|
||||
std::snprintf(buffer.data(), buffer.size(), "%0*" PRIX32, width, value);
|
||||
@@ -213,7 +223,7 @@ void syncFactoryFdskToNvs(const FactoryFdskContext& context, const uint8_t* data
|
||||
size_t stored_size = stored.size();
|
||||
|
||||
nvs_handle_t handle = 0;
|
||||
esp_err_t err = nvs_open(context.nvsNamespace, NVS_READWRITE, &handle);
|
||||
esp_err_t err = nvs_open(context.nvsNamespace.c_str(), NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to open KNX security NVS namespace: %s", esp_err_to_name(err));
|
||||
return;
|
||||
@@ -483,6 +493,16 @@ FactoryFdskInfo LoadFactoryFdskInfo() {
|
||||
return LoadFactoryFdskInfoForContext(kReg1Context);
|
||||
}
|
||||
|
||||
bool LoadFactoryFdskForInstance(uint32_t instance_id, uint8_t* data, size_t len) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return LoadFactoryFdskForContext(context, data, len);
|
||||
}
|
||||
|
||||
FactoryFdskInfo LoadFactoryFdskInfoForInstance(uint32_t instance_id) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return LoadFactoryFdskInfoForContext(context);
|
||||
}
|
||||
|
||||
bool GenerateFactoryFdsk(FactoryFdskInfo* info) {
|
||||
return GenerateFactoryFdskForContext(kReg1Context, info);
|
||||
}
|
||||
@@ -495,10 +515,28 @@ bool ResetFactoryFdskCache(FactoryFdskInfo* info) {
|
||||
return ResetFactoryFdskCacheForContext(kReg1Context, info);
|
||||
}
|
||||
|
||||
bool ResetFactorySecurityForInstance(uint32_t instance_id, FactoryFdskInfo* info) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
if (ensureNvsReady()) {
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(context.nvsNamespace.c_str(), NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_erase_key(handle, kFactoryFdskKey);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
}
|
||||
return ResetFactoryFdskCacheForContext(context, info);
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayload() {
|
||||
return BuildFactoryCertificatePayloadForContext(kReg1Context);
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayloadForInstance(uint32_t instance_id) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return BuildFactoryCertificatePayloadForContext(context);
|
||||
}
|
||||
|
||||
bool LoadOamFactoryFdsk(uint8_t* data, size_t len) {
|
||||
return LoadFactoryFdskForContext(kOamContext, data, len);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user