feat: add application controller handling and DALI control device commands

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-06-11 18:46:32 +08:00
parent 1342ab6aaf
commit 6ffca719d9
6 changed files with 379 additions and 11 deletions
+1 -1
View File
@@ -22,6 +22,7 @@ Dali_msg_t dali_msg_new_generic(uint8_t bit_length, uint8_t address, uint8_t cmd
// create standard DALI message: 16,24,32 bits // create standard DALI message: 16,24,32 bits
Dali_msg_t dali_msg_new(uint8_t address, uint8_t cmd1); Dali_msg_t dali_msg_new(uint8_t address, uint8_t cmd1);
Dali_msg_t dali_msg_new_1B(uint8_t data);
Dali_msg_t dali_msg_new_3B(uint8_t address, uint8_t cmd1, uint8_t cmd2); Dali_msg_t dali_msg_new_3B(uint8_t address, uint8_t cmd1, uint8_t cmd2);
Dali_msg_t dali_msg_new_4B(uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3); Dali_msg_t dali_msg_new_4B(uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3);
@@ -86,4 +87,3 @@ void dali_change_short_address(int addr1, int addr2);
#endif #endif
@@ -140,6 +140,11 @@ class DaliDomainService {
bool sendRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const; bool sendRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const;
bool sendExtRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const; bool sendExtRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const;
std::optional<uint8_t> queryRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const; std::optional<uint8_t> queryRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const;
bool sendControlDeviceRaw(uint8_t gateway_id, uint8_t byte0, uint8_t byte1, uint8_t byte2,
bool send_twice = false) const;
std::optional<uint8_t> queryControlDeviceRaw(uint8_t gateway_id, uint8_t byte0,
uint8_t byte1, uint8_t byte2) const;
bool sendBackwardFrame(uint8_t gateway_id, uint8_t value) const;
std::optional<DaliDomainSnapshot> discoverDeviceTypes( std::optional<DaliDomainSnapshot> discoverDeviceTypes(
uint8_t gateway_id, int short_address, const std::vector<int>& fallback_types = {}, uint8_t gateway_id, int short_address, const std::vector<int>& fallback_types = {},
int max_next_types = 16) const; int max_next_types = 16) const;
+152 -4
View File
@@ -25,6 +25,9 @@ namespace {
constexpr size_t kSerialRxPacketMaxBytes = 8; constexpr size_t kSerialRxPacketMaxBytes = 8;
constexpr UBaseType_t kSerialRxQueueDepth = 8; constexpr UBaseType_t kSerialRxQueueDepth = 8;
constexpr uint32_t kHardwareQueryRawPostSuppressMs = 10; constexpr uint32_t kHardwareQueryRawPostSuppressMs = 10;
constexpr uint8_t kControlDeviceSendOpcode = 0x60;
constexpr uint8_t kControlDeviceSendTwiceOpcode = 0x61;
constexpr uint8_t kControlDeviceQueryOpcode = 0x62;
portMUX_TYPE s_query_raw_suppress_lock = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE s_query_raw_suppress_lock = portMUX_INITIALIZER_UNLOCKED;
uint8_t s_query_raw_suppress_inflight[DALI_PHY_COUNT] = {}; uint8_t s_query_raw_suppress_inflight[DALI_PHY_COUNT] = {};
@@ -146,6 +149,13 @@ std::vector<uint8_t> LegacyQueryResponse(uint8_t status, uint8_t value = 0x00) {
return {status, value}; return {status, value};
} }
std::optional<uint8_t> ParseLegacyQueryValue(const std::vector<uint8_t>& packet) {
if (packet.size() >= 2 && packet[0] == 0xFF) {
return packet[1];
}
return std::nullopt;
}
void LogQueryRxPacket(const char* transport, int id, const std::vector<uint8_t>& packet, void LogQueryRxPacket(const char* transport, int id, const std::vector<uint8_t>& packet,
const char* note = nullptr) { const char* note = nullptr) {
const unsigned first = packet.size() > 0 ? packet[0] : 0; const unsigned first = packet.size() > 0 ? packet[0] : 0;
@@ -156,11 +166,80 @@ void LogQueryRxPacket(const char* transport, int id, const std::vector<uint8_t>&
note == nullptr ? "" : " note=", note == nullptr ? "" : note); note == nullptr ? "" : " note=", note == nullptr ? "" : note);
} }
bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) { bool SendHardwareControlDeviceFrame(uint8_t bus_id, const uint8_t* data, size_t len,
bool send_twice) {
if (data == nullptr || len != 3) { if (data == nullptr || len != 3) {
return false; return false;
} }
Dali_msg_t tx = dali_msg_new_3B(data[0], data[1], data[2]);
tx.id = bus_id;
ESP_LOGD(TAG, "sending hardware control-device frame for bus=%u data=%02x %02x %02x",
bus_id, data[0], data[1], data[2]);
if (send_twice) {
dali_send_double(&tx);
} else {
dali_send(&tx);
}
return tx.status == DALI_FRAME_OK;
}
std::vector<uint8_t> TransactHardwareControlDeviceFrame(uint8_t bus_id, const uint8_t* data,
size_t len) {
if (data == nullptr || len != 3) {
const auto packet = LegacyQueryResponse(0xFD);
LogQueryRxPacket("hardware-cd", bus_id, packet, "invalid-query-len");
return packet;
}
Dali_msg_t tx = dali_msg_new_3B(data[0], data[1], data[2]);
tx.id = bus_id;
Dali_msg_t rx = {};
ESP_LOGD(TAG, "received hardware control-device query for bus=%u data=%02x %02x %02x",
bus_id, data[0], data[1], data[2]);
BeginHardwareQueryRawSuppress(bus_id);
if (dali_query(&tx, &rx) == pdTRUE) {
ClearHardwareQueryRawSuppress(bus_id);
if (rx.status != DALI_FRAME_OK || rx.length != 8) {
ESP_LOGW(TAG, "hardware control-device query response for bus=%u has invalid status or length",
bus_id);
const auto packet = LegacyQueryResponse(0xFD);
LogQueryRxPacket("hardware-cd", bus_id, packet, "invalid-status-or-length");
return packet;
}
const std::vector<uint8_t> packet{0xFF, rx.data[0]};
LogQueryRxPacket("hardware-cd", bus_id, packet, "ok");
return packet;
}
ClearHardwareQueryRawSuppress(bus_id);
const auto packet = LegacyQueryResponse(0xFE);
LogQueryRxPacket("hardware-cd", bus_id, packet, "no-response");
return packet;
}
bool SendHardwareBackwardFrame(uint8_t bus_id, uint8_t value) {
Dali_msg_t tx = dali_msg_new_1B(value);
tx.id = bus_id;
ESP_LOGD(TAG, "sending hardware backward frame for bus=%u data=%02x", bus_id, value);
dali_send(&tx);
return tx.status == DALI_FRAME_OK;
}
bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
if (data == nullptr) {
return false;
}
if (len == 4 && (data[0] == kControlDeviceSendOpcode ||
data[0] == kControlDeviceSendTwiceOpcode)) {
return SendHardwareControlDeviceFrame(bus_id, data + 1, 3,
data[0] == kControlDeviceSendTwiceOpcode);
}
if (len != 3) {
return false;
}
if (data[0] == 0x00) { if (data[0] == 0x00) {
return true; return true;
} }
@@ -186,6 +265,9 @@ std::vector<uint8_t> TransactHardwareFrame(uint8_t bus_id, const uint8_t* data,
if (data == nullptr) { if (data == nullptr) {
return {}; return {};
} }
if (len == 4 && data[0] == kControlDeviceQueryOpcode) {
return TransactHardwareControlDeviceFrame(bus_id, data + 1, 3);
}
if (len != 3) { if (len != 3) {
if (len > 0 && data[0] == 0x12) { if (len > 0 && data[0] == 0x12) {
const auto packet = LegacyQueryResponse(0xFD); const auto packet = LegacyQueryResponse(0xFD);
@@ -276,12 +358,14 @@ std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
LogQueryRxPacket("serial", uart_port, packet, "empty-query"); LogQueryRxPacket("serial", uart_port, packet, "empty-query");
return packet; return packet;
} }
if (data[0] == 0x12 && len != 3) { const bool query_frame = data[0] == 0x12 || data[0] == kControlDeviceQueryOpcode;
const size_t expected_query_len = data[0] == kControlDeviceQueryOpcode ? 4 : 3;
if (query_frame && len != expected_query_len) {
const auto packet = LegacyQueryResponse(0xFD); const auto packet = LegacyQueryResponse(0xFD);
LogQueryRxPacket("serial", uart_port, packet, "invalid-query-len"); LogQueryRxPacket("serial", uart_port, packet, "invalid-query-len");
return packet; return packet;
} }
if (data != nullptr && len > 0 && data[0] == 0x12) { if (query_frame) {
DrainSerialQueue(queue); DrainSerialQueue(queue);
} }
if (!WriteSerialFrame(uart_port, data, len)) { if (!WriteSerialFrame(uart_port, data, len)) {
@@ -289,7 +373,7 @@ std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
LogQueryRxPacket("serial", uart_port, packet, "write-failed"); LogQueryRxPacket("serial", uart_port, packet, "write-failed");
return packet; return packet;
} }
if (data[0] != 0x12) { if (!query_frame) {
return {0xFF}; return {0xFF};
} }
@@ -721,6 +805,70 @@ std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t r
return channel->comm->queryRawNew(raw_addr, command); return channel->comm->queryRawNew(raw_addr, command);
} }
bool DaliDomainService::sendControlDeviceRaw(uint8_t gateway_id, uint8_t byte0, uint8_t byte1,
uint8_t byte2, bool send_twice) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr) {
return false;
}
const uint8_t frame[3] = {byte0, byte1, byte2};
markHostActivity(gateway_id);
markBusActivity(gateway_id);
if (channel->hardware_bus.has_value()) {
return SendHardwareControlDeviceFrame(channel->hardware_bus->bus_id, frame, sizeof(frame),
send_twice);
}
if (!channel->hooks.send) {
return false;
}
const uint8_t bridge_frame[4] = {
static_cast<uint8_t>(send_twice ? kControlDeviceSendTwiceOpcode : kControlDeviceSendOpcode),
byte0,
byte1,
byte2,
};
return channel->hooks.send(bridge_frame, sizeof(bridge_frame));
}
std::optional<uint8_t> DaliDomainService::queryControlDeviceRaw(uint8_t gateway_id, uint8_t byte0,
uint8_t byte1,
uint8_t byte2) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr) {
return std::nullopt;
}
const uint8_t frame[3] = {byte0, byte1, byte2};
markHostActivity(gateway_id);
markBusActivity(gateway_id);
if (channel->hardware_bus.has_value()) {
return ParseLegacyQueryValue(
TransactHardwareControlDeviceFrame(channel->hardware_bus->bus_id, frame, sizeof(frame)));
}
if (!channel->hooks.transact) {
return std::nullopt;
}
const uint8_t bridge_frame[4] = {kControlDeviceQueryOpcode, byte0, byte1, byte2};
return ParseLegacyQueryValue(channel->hooks.transact(bridge_frame, sizeof(bridge_frame)));
}
bool DaliDomainService::sendBackwardFrame(uint8_t gateway_id, uint8_t value) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr) {
return false;
}
markBusActivity(gateway_id);
if (channel->hardware_bus.has_value()) {
return SendHardwareBackwardFrame(channel->hardware_bus->bus_id, value);
}
if (!channel->hooks.send) {
return false;
}
return channel->hooks.send(&value, 1);
}
std::optional<DaliDomainSnapshot> DaliDomainService::discoverDeviceTypes( std::optional<DaliDomainSnapshot> DaliDomainService::discoverDeviceTypes(
uint8_t gateway_id, int short_address, const std::vector<int>& fallback_types, uint8_t gateway_id, int short_address, const std::vector<int>& fallback_types,
int max_next_types) const { int max_next_types) const {
@@ -144,6 +144,9 @@ class GatewayController {
void publishPayload(uint8_t gateway_id, const std::vector<uint8_t>& payload); void publishPayload(uint8_t gateway_id, const std::vector<uint8_t>& payload);
void publishFrame(const std::vector<uint8_t>& frame); void publishFrame(const std::vector<uint8_t>& frame);
void handleDaliRawFrame(const DaliRawFrame& frame); void handleDaliRawFrame(const DaliRawFrame& frame);
bool handleApplicationControllerFrame(const DaliRawFrame& frame);
std::optional<uint8_t> applicationControllerResponse(uint8_t gateway_id, uint8_t first,
uint8_t instance, uint8_t opcode) const;
bool sendRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); bool sendRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command);
bool sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); bool sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command);
@@ -207,6 +210,14 @@ class GatewayController {
bool ble_enabled_{false}; bool ble_enabled_{false};
bool wifi_enabled_{false}; bool wifi_enabled_{false};
bool ip_router_enabled_{true}; bool ip_router_enabled_{true};
bool application_controller_enabled_{true};
bool application_controller_power_cycle_notification_{true};
bool application_controller_power_cycle_seen_{true};
bool application_controller_reset_state_{false};
uint8_t application_controller_operating_mode_{0};
uint8_t application_controller_dtr0_{0};
uint8_t application_controller_dtr1_{0};
uint8_t application_controller_dtr2_{0};
}; };
} // namespace gateway } // namespace gateway
@@ -28,6 +28,12 @@ constexpr uint8_t kBridgeTransportRequestOpcode = 0xB0;
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1; constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
constexpr uint8_t kBridgeTransportVersion = 1; constexpr uint8_t kBridgeTransportVersion = 1;
constexpr size_t kBridgeTransportMaxChunkBytes = 120; constexpr size_t kBridgeTransportMaxChunkBytes = 120;
constexpr uint8_t kDali103SendOpcode = 0x60;
constexpr uint8_t kDali103SendTwiceOpcode = 0x61;
constexpr uint8_t kDali103QueryOpcode = 0x62;
constexpr uint8_t kDali103QueryResponseOpcode = 0x63;
constexpr uint8_t kDali103NoResponseOpcode = 0x64;
constexpr uint8_t kDali103RawFrameOpcode = 0x65;
constexpr uint8_t kGatewayFeatureCache = 0x40; constexpr uint8_t kGatewayFeatureCache = 0x40;
constexpr uint8_t kGatewayCacheOpcode = 0x39; constexpr uint8_t kGatewayCacheOpcode = 0x39;
constexpr uint8_t kGatewayCacheProtocolVersion = 1; constexpr uint8_t kGatewayCacheProtocolVersion = 1;
@@ -106,6 +112,9 @@ bool IsDaliHostCommandOpcode(uint8_t opcode) {
case 0x32: case 0x32:
case 0x37: case 0x37:
case 0x38: case 0x38:
case kDali103SendOpcode:
case kDali103SendTwiceOpcode:
case kDali103QueryOpcode:
case 0xA0: case 0xA0:
case 0xA2: case 0xA2:
case kBridgeTransportRequestOpcode: case kBridgeTransportRequestOpcode:
@@ -933,6 +942,24 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
} }
} }
break; break;
case kDali103SendOpcode:
case kDali103SendTwiceOpcode:
if (command.size() >= 8) {
dali_domain_.sendControlDeviceRaw(gateway_id, command[4], command[5], command[6],
opcode == kDali103SendTwiceOpcode);
}
break;
case kDali103QueryOpcode:
if (command.size() >= 8) {
const auto result =
dali_domain_.queryControlDeviceRaw(gateway_id, command[4], command[5], command[6]);
if (result.has_value()) {
publishPayload(gateway_id, {kDali103QueryResponseOpcode, gateway_id, result.value()});
} else {
publishPayload(gateway_id, {kDali103NoResponseOpcode, gateway_id, 0x00});
}
}
break;
case kGatewayCacheOpcode: case kGatewayCacheOpcode:
handleGatewayCacheCommand(gateway_id, command); handleGatewayCacheCommand(gateway_id, command);
break; break;
@@ -1036,6 +1063,160 @@ void GatewayController::publishFrame(const std::vector<uint8_t>& frame) {
} }
} }
std::optional<uint8_t> GatewayController::applicationControllerResponse(
uint8_t gateway_id, uint8_t first, uint8_t instance, uint8_t opcode) const {
const uint8_t gateway_short =
static_cast<uint8_t>(((gateway_id & 0x3F) << 1) | 0x01);
const bool addressed_to_gateway = first == 0xFF || first == gateway_short;
if (!addressed_to_gateway || instance != 0xFE) {
return std::nullopt;
}
switch (opcode) {
case 0x30: {
uint8_t status = 0;
if (application_controller_enabled_) {
status |= 0x08;
}
if (application_controller_power_cycle_seen_) {
status |= 0x20;
}
if (application_controller_reset_state_) {
status |= 0x40;
}
return status;
}
case 0x31:
case 0x32:
case 0x33:
return 0x00;
case 0x34:
return 0x02;
case 0x35:
return 0x00;
case 0x36:
return application_controller_dtr0_;
case 0x37:
return application_controller_dtr1_;
case 0x38:
return application_controller_dtr2_;
case 0x39:
case 0x3A:
case 0x3B:
return 0x00;
case 0x3C:
return 0xFF;
case 0x3D:
return static_cast<uint8_t>(application_controller_enabled_ ? 1 : 0);
case 0x3E:
return application_controller_operating_mode_;
case 0x3F:
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
return 0x00;
case 0x45:
return static_cast<uint8_t>(application_controller_power_cycle_notification_ ? 1 : 0);
case 0x46:
return 0x01;
case 0x47:
return 0x01;
case 0x48:
return static_cast<uint8_t>(application_controller_reset_state_ ? 1 : 0);
default:
return std::nullopt;
}
}
bool GatewayController::handleApplicationControllerFrame(const DaliRawFrame& frame) {
if (frame.data.size() != 3) {
return false;
}
const uint8_t first = frame.data[0];
const uint8_t instance = frame.data[1];
const uint8_t opcode = frame.data[2];
if (first == 0xC1) {
switch (instance) {
case 0x30:
application_controller_dtr0_ = opcode;
break;
case 0x31:
application_controller_dtr1_ = opcode;
break;
case 0x32:
application_controller_dtr2_ = opcode;
break;
default:
break;
}
return false;
}
if ((first & 0x01) == 0) {
return false;
}
const uint8_t gateway_short =
static_cast<uint8_t>(((frame.gateway_id & 0x3F) << 1) | 0x01);
if (first != 0xFF && first != gateway_short) {
return false;
}
if (instance != 0xFE) {
return false;
}
switch (opcode) {
case 0x01:
application_controller_power_cycle_seen_ = false;
application_controller_reset_state_ = false;
break;
case 0x10:
application_controller_enabled_ = true;
application_controller_power_cycle_notification_ = true;
application_controller_reset_state_ = true;
application_controller_operating_mode_ = 0;
application_controller_dtr0_ = 0;
application_controller_dtr1_ = 0;
application_controller_dtr2_ = 0;
break;
case 0x16:
application_controller_enabled_ = true;
application_controller_reset_state_ = false;
break;
case 0x17:
application_controller_enabled_ = false;
application_controller_reset_state_ = false;
break;
case 0x18:
application_controller_operating_mode_ = application_controller_dtr0_;
application_controller_reset_state_ = false;
break;
case 0x1F:
application_controller_power_cycle_notification_ = true;
application_controller_reset_state_ = false;
break;
case 0x20:
application_controller_power_cycle_notification_ = false;
application_controller_reset_state_ = false;
break;
case 0x21:
application_controller_reset_state_ = false;
break;
default:
break;
}
const auto response = applicationControllerResponse(frame.gateway_id, first, instance, opcode);
if (!response.has_value()) {
return false;
}
return dali_domain_.sendBackwardFrame(frame.gateway_id, response.value());
}
void GatewayController::handleBridgeTransportCommand(uint8_t gateway_id, void GatewayController::handleBridgeTransportCommand(uint8_t gateway_id,
const std::vector<uint8_t>& command) { const std::vector<uint8_t>& command) {
const uint8_t version = command.size() > 4 ? command[4] : kBridgeTransportVersion; const uint8_t version = command.size() > 4 ? command[4] : kBridgeTransportVersion;
@@ -1119,6 +1300,20 @@ void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) {
return; return;
} }
if (frame.data.size() == 3 &&
(((frame.data[0] & 0x01) != 0) || frame.data[0] == 0xC1)) {
handleApplicationControllerFrame(frame);
const bool maintenance_activity = maintenance_activity_gateway_.load() == frame.gateway_id;
if (setup_mode_ || dali_domain_.isAllocAddr(frame.gateway_id) || maintenance_activity ||
runtime_.hasActiveQueryCommand(frame.gateway_id)) {
return;
}
publishPayload(frame.gateway_id,
{kDali103RawFrameOpcode, frame.gateway_id, frame.data[0], frame.data[1],
frame.data[2]});
return;
}
uint8_t addr = 0; uint8_t addr = 0;
uint8_t data = 0; uint8_t data = 0;
if (frame.data.size() == 2) { if (frame.data.size() == 2) {
@@ -324,7 +324,7 @@ GatewayRuntime::CommandPriority GatewayRuntime::classifyCommandPriority(
if (opcode == 0x00 || opcode == 0x01 || opcode == 0x03 || opcode == 0x04 || opcode == 0x07 || if (opcode == 0x00 || opcode == 0x01 || opcode == 0x03 || opcode == 0x04 || opcode == 0x07 ||
opcode == 0x08 || opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || opcode == 0x13 || opcode == 0x08 || opcode == 0x10 || opcode == 0x11 || opcode == 0x12 || opcode == 0x13 ||
opcode == 0x17 || opcode == 0x18 || opcode == 0x37 || opcode == 0x38 || opcode == 0x17 || opcode == 0x18 || opcode == 0x37 || opcode == 0x38 ||
(opcode == 0x30 && addr == 0)) { opcode == 0x60 || opcode == 0x61 || opcode == 0x62 || (opcode == 0x30 && addr == 0)) {
return CommandPriority::kControl; return CommandPriority::kControl;
} }
return CommandPriority::kNormal; return CommandPriority::kNormal;
@@ -597,8 +597,8 @@ std::string GatewayRuntime::defaultBleGatewayName() const {
} }
bool GatewayRuntime::isQueryCommand(const std::vector<uint8_t>& command) const { bool GatewayRuntime::isQueryCommand(const std::vector<uint8_t>& command) const {
return command.size() >= 6 && isGatewayCommandFrame(command) && command[3] >= 0x14 && return command.size() >= 6 && isGatewayCommandFrame(command) &&
command[3] <= 0x16; ((command[3] >= 0x14 && command[3] <= 0x16) || command[3] == 0x62);
} }
size_t GatewayRuntime::pendingCommandCountLocked() const { size_t GatewayRuntime::pendingCommandCountLocked() const {
@@ -639,6 +639,15 @@ std::optional<std::string> GatewayRuntime::queryCommandKey(
const auto gw = command[2]; const auto gw = command[2];
const auto cmd = command[3]; const auto cmd = command[3];
if (cmd == 0x62) {
if (command.size() < 8) {
return std::nullopt;
}
char key[40] = {0};
std::snprintf(key, sizeof(key), "%u:%u:%u:%u:%u", gw, cmd, command[4], command[5],
command[6]);
return std::string(key);
}
if (cmd == 0x16) { if (cmd == 0x16) {
char key[16] = {0}; char key[16] = {0};
std::snprintf(key, sizeof(key), "%u:%u", gw, cmd); std::snprintf(key, sizeof(key), "%u:%u", gw, cmd);