feat(gateway): implement commissioning scan functionality with options for new, randomize, delete, and assign
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -125,6 +125,13 @@ struct GatewayKnxCommissioningBallast {
|
|||||||
uint8_t short_address{0xff};
|
uint8_t short_address{0xff};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GatewayKnxReg1ScanOptions {
|
||||||
|
bool only_new{false};
|
||||||
|
bool randomize{false};
|
||||||
|
bool delete_all{false};
|
||||||
|
bool assign{false};
|
||||||
|
};
|
||||||
|
|
||||||
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
|
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
|
||||||
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
|
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
|
||||||
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
|
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
|
||||||
@@ -143,6 +150,7 @@ std::string GatewayKnxGroupAddressString(uint16_t group_address);
|
|||||||
class GatewayKnxBridge {
|
class GatewayKnxBridge {
|
||||||
public:
|
public:
|
||||||
explicit GatewayKnxBridge(DaliBridgeEngine& engine);
|
explicit GatewayKnxBridge(DaliBridgeEngine& engine);
|
||||||
|
~GatewayKnxBridge();
|
||||||
|
|
||||||
void setConfig(const GatewayKnxConfig& config);
|
void setConfig(const GatewayKnxConfig& config);
|
||||||
void setRuntimeContext(const openknx::EtsDeviceRuntime* runtime);
|
void setRuntimeContext(const openknx::EtsDeviceRuntime* runtime);
|
||||||
@@ -196,11 +204,17 @@ class GatewayKnxBridge {
|
|||||||
std::vector<uint8_t>* response);
|
std::vector<uint8_t>* response);
|
||||||
bool handleReg1FoundEvgsState(const uint8_t* data, size_t len,
|
bool handleReg1FoundEvgsState(const uint8_t* data, size_t len,
|
||||||
std::vector<uint8_t>* response);
|
std::vector<uint8_t>* response);
|
||||||
|
static void CommissioningScanTaskEntry(void* arg);
|
||||||
|
void runCommissioningScanTask();
|
||||||
|
|
||||||
DaliBridgeEngine& engine_;
|
DaliBridgeEngine& engine_;
|
||||||
GatewayKnxConfig config_;
|
GatewayKnxConfig config_;
|
||||||
const openknx::EtsDeviceRuntime* runtime_{nullptr};
|
const openknx::EtsDeviceRuntime* runtime_{nullptr};
|
||||||
std::map<uint16_t, std::vector<GatewayKnxDaliBinding>> ets_bindings_by_group_address_;
|
std::map<uint16_t, std::vector<GatewayKnxDaliBinding>> ets_bindings_by_group_address_;
|
||||||
|
SemaphoreHandle_t commissioning_lock_{nullptr};
|
||||||
|
TaskHandle_t commissioning_scan_task_{nullptr};
|
||||||
|
std::atomic_bool commissioning_scan_cancel_requested_{false};
|
||||||
|
GatewayKnxReg1ScanOptions commissioning_scan_options_;
|
||||||
bool commissioning_scan_done_{true};
|
bool commissioning_scan_done_{true};
|
||||||
bool commissioning_assign_done_{true};
|
bool commissioning_assign_done_{true};
|
||||||
std::vector<GatewayKnxCommissioningBallast> commissioning_found_ballasts_;
|
std::vector<GatewayKnxCommissioningBallast> commissioning_found_ballasts_;
|
||||||
|
|||||||
@@ -141,10 +141,12 @@ constexpr uint8_t kReg1DeviceTypeDt8 = 8;
|
|||||||
constexpr uint8_t kReg1ColorTypeTw = 1;
|
constexpr uint8_t kReg1ColorTypeTw = 1;
|
||||||
constexpr uint8_t kDaliDeviceTypeNone = 0xfe;
|
constexpr uint8_t kDaliDeviceTypeNone = 0xfe;
|
||||||
constexpr uint8_t kDaliDeviceTypeMultiple = 0xff;
|
constexpr uint8_t kDaliDeviceTypeMultiple = 0xff;
|
||||||
|
constexpr uint32_t kCommissioningScanTaskStackSize = 8192;
|
||||||
constexpr uint16_t kGroupObjectTableObjectType = OT_GRP_OBJ_TABLE;
|
constexpr uint16_t kGroupObjectTableObjectType = OT_GRP_OBJ_TABLE;
|
||||||
constexpr uint8_t kPidGoDiagnostics = 0x42;
|
constexpr uint8_t kPidGoDiagnostics = 0x42;
|
||||||
constexpr uint8_t kGoDiagnosticsReservedByte = 0x00;
|
constexpr uint8_t kGoDiagnosticsReservedByte = 0x00;
|
||||||
constexpr uint8_t kGoDiagnosticsGroupWriteService = 0x01;
|
constexpr uint8_t kGoDiagnosticsGroupWriteService = 0x01;
|
||||||
|
constexpr uint8_t kGoDiagnosticsCompactPayloadFlag = 0x80;
|
||||||
|
|
||||||
struct DecodedGroupWrite {
|
struct DecodedGroupWrite {
|
||||||
uint16_t group_address{0};
|
uint16_t group_address{0};
|
||||||
@@ -569,15 +571,25 @@ std::optional<DecodedGoDiagnosticsGroupWrite> DecodeGoDiagnosticsGroupWrite(
|
|||||||
if (data[0] != kGoDiagnosticsReservedByte || data[1] != kGoDiagnosticsGroupWriteService) {
|
if (data[0] != kGoDiagnosticsReservedByte || data[1] != kGoDiagnosticsGroupWriteService) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
const size_t encoded_length = data[2];
|
const uint8_t encoded_length = data[2];
|
||||||
if (encoded_length < 2 || len != encoded_length + 3) {
|
size_t payload_len = 0;
|
||||||
|
if ((encoded_length & kGoDiagnosticsCompactPayloadFlag) != 0) {
|
||||||
|
payload_len = static_cast<size_t>(encoded_length & ~kGoDiagnosticsCompactPayloadFlag);
|
||||||
|
if (payload_len == 0 || len != payload_len + 5) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const size_t expanded_length = encoded_length;
|
||||||
|
if (expanded_length < 2 || len != expanded_length + 3) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
payload_len = expanded_length - 2;
|
||||||
|
}
|
||||||
|
|
||||||
DecodedGoDiagnosticsGroupWrite out;
|
DecodedGoDiagnosticsGroupWrite out;
|
||||||
out.group_address = ReadBe16(data + 3);
|
out.group_address = ReadBe16(data + 3);
|
||||||
out.payload = data + 5;
|
out.payload = data + 5;
|
||||||
out.payload_len = encoded_length - 2;
|
out.payload_len = payload_len;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1469,7 +1481,213 @@ std::optional<GatewayKnxDaliBinding> EtsBindingForAssociation(uint8_t main_group
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
GatewayKnxBridge::GatewayKnxBridge(DaliBridgeEngine& engine) : engine_(engine) {}
|
GatewayKnxBridge::GatewayKnxBridge(DaliBridgeEngine& engine) : engine_(engine) {
|
||||||
|
commissioning_lock_ = xSemaphoreCreateMutex();
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
ESP_LOGE(kTag, "Failed to create REG1-Dali commissioning mutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GatewayKnxBridge::~GatewayKnxBridge() {
|
||||||
|
commissioning_scan_cancel_requested_.store(true, std::memory_order_release);
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
TaskHandle_t task_handle = nullptr;
|
||||||
|
{
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
task_handle = commissioning_scan_task_;
|
||||||
|
}
|
||||||
|
if (task_handle == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
vSemaphoreDelete(commissioning_lock_);
|
||||||
|
commissioning_lock_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayKnxBridge::CommissioningScanTaskEntry(void* arg) {
|
||||||
|
auto* bridge = static_cast<GatewayKnxBridge*>(arg);
|
||||||
|
if (bridge != nullptr) {
|
||||||
|
bridge->runCommissioningScanTask();
|
||||||
|
}
|
||||||
|
vTaskDelete(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayKnxBridge::runCommissioningScanTask() {
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GatewayKnxReg1ScanOptions options;
|
||||||
|
{
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
options = commissioning_scan_options_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_cancelled = [this]() {
|
||||||
|
return commissioning_scan_cancel_requested_.load(std::memory_order_acquire);
|
||||||
|
};
|
||||||
|
auto record_ballast = [this](const GatewayKnxCommissioningBallast& ballast) {
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
if (commissioning_scan_cancel_requested_.load(std::memory_order_acquire)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
commissioning_found_ballasts_.push_back(ballast);
|
||||||
|
};
|
||||||
|
auto finish_scan = [this](bool clear_results) {
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
if (clear_results) {
|
||||||
|
commissioning_found_ballasts_.clear();
|
||||||
|
}
|
||||||
|
commissioning_scan_done_ = true;
|
||||||
|
commissioning_scan_cancel_requested_.store(false, std::memory_order_release);
|
||||||
|
commissioning_scan_task_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(kTag, "REG1-Dali scan start onlyNew=%d randomize=%d deleteAll=%d assign=%d",
|
||||||
|
options.only_new, options.randomize, options.delete_all, options.assign);
|
||||||
|
|
||||||
|
bool clear_results = false;
|
||||||
|
std::array<bool, 64> used_addresses{};
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.assign && !options.delete_all) {
|
||||||
|
used_addresses = QueryUsedShortAddresses(engine_);
|
||||||
|
}
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool initialized = SendRaw(engine_, DALI_CMD_SPECIAL_TERMINATE, DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-terminate-prev") &&
|
||||||
|
SendRawExt(engine_, DALI_CMD_SPECIAL_INITIALIZE,
|
||||||
|
options.only_new ? DALI_CMD_STOP_FADE : DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-init") &&
|
||||||
|
SendRawExt(engine_, DALI_CMD_SPECIAL_INITIALIZE,
|
||||||
|
options.only_new ? DALI_CMD_STOP_FADE : DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-init-repeat");
|
||||||
|
if (!initialized) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan failed during initialize");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.delete_all) {
|
||||||
|
const bool removed = SendRaw(engine_, DALI_CMD_SPECIAL_SET_DTR0, 0xff,
|
||||||
|
"knx-function-scan-clear-short-dtr") &&
|
||||||
|
SendRawExt(engine_, 0xff, DALI_CMD_STORE_DTR_AS_SHORT_ADDRESS,
|
||||||
|
"knx-function-scan-clear-short");
|
||||||
|
if (!removed) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan failed while clearing short addresses");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.randomize) {
|
||||||
|
const bool randomized = SendRawExt(engine_, DALI_CMD_SPECIAL_RANDOMIZE, DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-randomize") &&
|
||||||
|
SendRawExt(engine_, DALI_CMD_SPECIAL_RANDOMIZE, DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-randomize-repeat");
|
||||||
|
if (!randomized) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan failed while randomizing addresses");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!is_cancelled()) {
|
||||||
|
const auto random_address = FindLowestSelectedRandomAddress(engine_);
|
||||||
|
if (!random_address.has_value()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GatewayKnxCommissioningBallast ballast;
|
||||||
|
ballast.high = static_cast<uint8_t>((random_address.value() >> 16) & 0xff);
|
||||||
|
ballast.middle = static_cast<uint8_t>((random_address.value() >> 8) & 0xff);
|
||||||
|
ballast.low = static_cast<uint8_t>(random_address.value() & 0xff);
|
||||||
|
ballast.short_address = 0xff;
|
||||||
|
|
||||||
|
if (options.assign) {
|
||||||
|
const auto next_address = NextFreeShortAddress(used_addresses);
|
||||||
|
if (!next_address.has_value()) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan has no free short address left for 0x%06x",
|
||||||
|
static_cast<unsigned>(random_address.value()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!SendRaw(engine_, DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS,
|
||||||
|
DaliComm::toCmdAddr(next_address.value()),
|
||||||
|
"knx-function-scan-program-short") ||
|
||||||
|
!VerifyShortAddress(engine_, next_address.value())) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan failed to program short address %u",
|
||||||
|
static_cast<unsigned>(next_address.value()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
used_addresses[next_address.value()] = true;
|
||||||
|
ballast.short_address = next_address.value();
|
||||||
|
} else {
|
||||||
|
ballast.short_address = QuerySelectedShortAddress(engine_).value_or(0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
record_ballast(ballast);
|
||||||
|
ESP_LOGI(kTag, "REG1-Dali scan found random=0x%02X%02X%02X short=%u",
|
||||||
|
ballast.high, ballast.middle, ballast.low,
|
||||||
|
static_cast<unsigned>(ballast.short_address));
|
||||||
|
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!SendRaw(engine_, DALI_CMD_SPECIAL_WITHDRAW, DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-withdraw")) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan failed while withdrawing matched device");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cancelled()) {
|
||||||
|
clear_results = true;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
SendRaw(engine_, DALI_CMD_SPECIAL_TERMINATE, DALI_CMD_OFF,
|
||||||
|
"knx-function-scan-terminate");
|
||||||
|
finish_scan(clear_results);
|
||||||
|
|
||||||
|
if (clear_results) {
|
||||||
|
ESP_LOGI(kTag, "REG1-Dali scan cancelled");
|
||||||
|
} else {
|
||||||
|
size_t found_count = 0;
|
||||||
|
{
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
found_count = commissioning_found_ballasts_.size();
|
||||||
|
}
|
||||||
|
ESP_LOGI(kTag, "REG1-Dali scan completed count=%u",
|
||||||
|
static_cast<unsigned>(found_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GatewayKnxBridge::setConfig(const GatewayKnxConfig& config) {
|
void GatewayKnxBridge::setConfig(const GatewayKnxConfig& config) {
|
||||||
config_ = config;
|
config_ = config;
|
||||||
@@ -1775,112 +1993,34 @@ bool GatewayKnxBridge::handleReg1ScanCommand(const uint8_t* data, size_t len,
|
|||||||
if (len < 5 || response == nullptr) {
|
if (len < 5 || response == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (commissioning_lock_ == nullptr) {
|
||||||
|
ESP_LOGE(kTag, "REG1-Dali scan unavailable: commissioning mutex missing");
|
||||||
|
response->clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
if (commissioning_scan_task_ != nullptr) {
|
||||||
|
ESP_LOGW(kTag, "REG1-Dali scan request ignored while a scan is already running");
|
||||||
|
response->clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
commissioning_scan_options_.only_new = data[1] == 1;
|
||||||
|
commissioning_scan_options_.randomize = data[2] == 1;
|
||||||
|
commissioning_scan_options_.delete_all = data[3] == 1;
|
||||||
|
commissioning_scan_options_.assign = data[4] == 1;
|
||||||
|
commissioning_scan_cancel_requested_.store(false, std::memory_order_release);
|
||||||
commissioning_scan_done_ = false;
|
commissioning_scan_done_ = false;
|
||||||
commissioning_found_ballasts_.clear();
|
commissioning_found_ballasts_.clear();
|
||||||
|
|
||||||
const bool only_new = data[1] == 1;
|
if (xTaskCreate(&GatewayKnxBridge::CommissioningScanTaskEntry, "gw_knx_scan",
|
||||||
const bool randomize = data[2] == 1;
|
kCommissioningScanTaskStackSize, this, tskIDLE_PRIORITY + 1,
|
||||||
const bool delete_all = data[3] == 1;
|
&commissioning_scan_task_) != pdPASS) {
|
||||||
const bool assign = data[4] == 1;
|
|
||||||
ESP_LOGI(kTag, "REG1-Dali scan start onlyNew=%d randomize=%d deleteAll=%d assign=%d",
|
|
||||||
only_new, randomize, delete_all, assign);
|
|
||||||
|
|
||||||
std::array<bool, 64> used_addresses{};
|
|
||||||
if (assign && !delete_all) {
|
|
||||||
used_addresses = QueryUsedShortAddresses(engine_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool initialized = SendRaw(engine_, DALI_CMD_SPECIAL_TERMINATE, DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-terminate-prev") &&
|
|
||||||
SendRawExt(engine_, DALI_CMD_SPECIAL_INITIALIZE,
|
|
||||||
only_new ? DALI_CMD_STOP_FADE : DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-init") &&
|
|
||||||
SendRawExt(engine_, DALI_CMD_SPECIAL_INITIALIZE,
|
|
||||||
only_new ? DALI_CMD_STOP_FADE : DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-init-repeat");
|
|
||||||
if (!initialized) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan failed during initialize");
|
|
||||||
commissioning_scan_done_ = true;
|
commissioning_scan_done_ = true;
|
||||||
response->clear();
|
ESP_LOGE(kTag, "Failed to start REG1-Dali commissioning scan task");
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete_all) {
|
|
||||||
const bool removed = SendRaw(engine_, DALI_CMD_SPECIAL_SET_DTR0, 0xff,
|
|
||||||
"knx-function-scan-clear-short-dtr") &&
|
|
||||||
SendRawExt(engine_, 0xff, DALI_CMD_STORE_DTR_AS_SHORT_ADDRESS,
|
|
||||||
"knx-function-scan-clear-short");
|
|
||||||
if (!removed) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan failed while clearing short addresses");
|
|
||||||
commissioning_scan_done_ = true;
|
|
||||||
response->clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomize) {
|
|
||||||
const bool randomized = SendRawExt(engine_, DALI_CMD_SPECIAL_RANDOMIZE, DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-randomize") &&
|
|
||||||
SendRawExt(engine_, DALI_CMD_SPECIAL_RANDOMIZE, DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-randomize-repeat");
|
|
||||||
if (!randomized) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan failed while randomizing addresses");
|
|
||||||
commissioning_scan_done_ = true;
|
|
||||||
response->clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const auto random_address = FindLowestSelectedRandomAddress(engine_);
|
|
||||||
if (!random_address.has_value()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GatewayKnxCommissioningBallast ballast;
|
|
||||||
ballast.high = static_cast<uint8_t>((random_address.value() >> 16) & 0xff);
|
|
||||||
ballast.middle = static_cast<uint8_t>((random_address.value() >> 8) & 0xff);
|
|
||||||
ballast.low = static_cast<uint8_t>(random_address.value() & 0xff);
|
|
||||||
ballast.short_address = 0xff;
|
|
||||||
|
|
||||||
if (assign) {
|
|
||||||
const auto next_address = NextFreeShortAddress(used_addresses);
|
|
||||||
if (!next_address.has_value()) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan has no free short address left for 0x%06x",
|
|
||||||
static_cast<unsigned>(random_address.value()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!SendRaw(engine_, DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS,
|
|
||||||
DaliComm::toCmdAddr(next_address.value()),
|
|
||||||
"knx-function-scan-program-short") ||
|
|
||||||
!VerifyShortAddress(engine_, next_address.value())) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan failed to program short address %u",
|
|
||||||
static_cast<unsigned>(next_address.value()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
used_addresses[next_address.value()] = true;
|
|
||||||
ballast.short_address = next_address.value();
|
|
||||||
} else {
|
|
||||||
ballast.short_address = QuerySelectedShortAddress(engine_).value_or(0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
commissioning_found_ballasts_.push_back(ballast);
|
|
||||||
ESP_LOGI(kTag, "REG1-Dali scan found random=0x%02X%02X%02X short=%u",
|
|
||||||
ballast.high, ballast.middle, ballast.low,
|
|
||||||
static_cast<unsigned>(ballast.short_address));
|
|
||||||
|
|
||||||
if (!SendRaw(engine_, DALI_CMD_SPECIAL_WITHDRAW, DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-withdraw")) {
|
|
||||||
ESP_LOGW(kTag, "REG1-Dali scan failed while withdrawing matched device");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendRaw(engine_, DALI_CMD_SPECIAL_TERMINATE, DALI_CMD_OFF,
|
|
||||||
"knx-function-scan-terminate");
|
|
||||||
commissioning_scan_done_ = true;
|
|
||||||
ESP_LOGI(kTag, "REG1-Dali scan completed count=%u",
|
|
||||||
static_cast<unsigned>(commissioning_found_ballasts_.size()));
|
|
||||||
response->clear();
|
response->clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2110,6 +2250,15 @@ bool GatewayKnxBridge::handleReg1ScanState(const uint8_t* data, size_t len,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
response->clear();
|
response->clear();
|
||||||
|
if (commissioning_lock_ != nullptr) {
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
response->push_back(commissioning_scan_done_ ? 1 : 0);
|
||||||
|
if (data[0] == kReg1FunctionScan) {
|
||||||
|
response->push_back(static_cast<uint8_t>(
|
||||||
|
std::min<size_t>(commissioning_found_ballasts_.size(), 0xff)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
response->push_back(commissioning_scan_done_ ? 1 : 0);
|
response->push_back(commissioning_scan_done_ ? 1 : 0);
|
||||||
if (data[0] == kReg1FunctionScan) {
|
if (data[0] == kReg1FunctionScan) {
|
||||||
response->push_back(static_cast<uint8_t>(
|
response->push_back(static_cast<uint8_t>(
|
||||||
@@ -2132,6 +2281,27 @@ bool GatewayKnxBridge::handleReg1FoundEvgsState(const uint8_t* data, size_t len,
|
|||||||
if (len < 2 || response == nullptr) {
|
if (len < 2 || response == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (commissioning_lock_ != nullptr) {
|
||||||
|
SemaphoreGuard guard(commissioning_lock_);
|
||||||
|
if (data[1] == 254) {
|
||||||
|
commissioning_scan_cancel_requested_.store(true, std::memory_order_release);
|
||||||
|
commissioning_scan_done_ = true;
|
||||||
|
commissioning_found_ballasts_.clear();
|
||||||
|
response->clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const size_t index = data[1];
|
||||||
|
response->clear();
|
||||||
|
response->push_back(index < commissioning_found_ballasts_.size() ? 1 : 0);
|
||||||
|
if (index < commissioning_found_ballasts_.size()) {
|
||||||
|
const auto& ballast = commissioning_found_ballasts_[index];
|
||||||
|
response->push_back(ballast.high);
|
||||||
|
response->push_back(ballast.middle);
|
||||||
|
response->push_back(ballast.low);
|
||||||
|
response->push_back(ballast.short_address);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (data[1] == 254) {
|
if (data[1] == 254) {
|
||||||
commissioning_found_ballasts_.clear();
|
commissioning_found_ballasts_.clear();
|
||||||
response->clear();
|
response->clear();
|
||||||
|
|||||||
+1
-1
Submodule knx updated: af9be62529...135f109061
Reference in New Issue
Block a user