Enhance DaliDecode: add support for device type decoding and context management
- Introduced new structures and methods for handling device type decoding, including DaliDecodeContext and DeviceTypeDecodeTable. - Implemented decoding functions for device types DT1, DT4, DT5, DT6, and DT8, with specific command and query handling. - Updated existing decode methods to incorporate gateway and source parameters for better context tracking. - Added detailed formatting for device type commands and queries, enhancing the decoding process for various device types. - Refactored the decode logic to utilize the new context management for maintaining state across different device types. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -7,6 +7,12 @@ idf_component_register(
|
||||
"src/bridge_model.cpp"
|
||||
"src/bridge_provisioning.cpp"
|
||||
"src/decode.cpp"
|
||||
"src/decode_device_type.cpp"
|
||||
"src/decode_dt1.cpp"
|
||||
"src/decode_dt4.cpp"
|
||||
"src/decode_dt5.cpp"
|
||||
"src/decode_dt6.cpp"
|
||||
"src/decode_dt8.cpp"
|
||||
"src/device.cpp"
|
||||
"src/dt1.cpp"
|
||||
"src/dt4.cpp"
|
||||
|
||||
+17
-6
@@ -4,15 +4,20 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class BusDir { front, back };
|
||||
enum class BusFrameSource { local, external };
|
||||
|
||||
struct BusFrame {
|
||||
BusDir dir = BusDir::front;
|
||||
int proto = 0x10;
|
||||
int b1 = 0;
|
||||
int b2 = 0;
|
||||
int b3 = -1;
|
||||
int gateway = -1;
|
||||
BusFrameSource source = BusFrameSource::local;
|
||||
};
|
||||
|
||||
class BusMonitor {
|
||||
@@ -27,22 +32,24 @@ class BusMonitor {
|
||||
void setRawSink(std::function<void(const BusFrame&)> sink) { rawSink_ = std::move(sink); }
|
||||
void setDecodedSink(std::function<void(const DecodedRecord&)> sink) { decodedSink_ = std::move(sink); }
|
||||
|
||||
void emitFront(int proto, int addr, int cmd) {
|
||||
BusFrame f{BusDir::front, proto, addr & 0xFF, cmd & 0xFF};
|
||||
void emitFront(int proto, int addr, int cmd, int gateway = -1,
|
||||
BusFrameSource source = BusFrameSource::local) {
|
||||
BusFrame f{BusDir::front, proto, addr & 0xFF, cmd & 0xFF, -1, gateway, source};
|
||||
rawFrames.push_back(f);
|
||||
if (rawSink_) rawSink_(f);
|
||||
|
||||
auto rec = decoder.decode(addr & 0xFF, cmd & 0xFF, proto);
|
||||
auto rec = decoder.decode(addr & 0xFF, cmd & 0xFF, proto, gateway, sourceName(source));
|
||||
records.push_back(rec);
|
||||
if (decodedSink_) decodedSink_(rec);
|
||||
}
|
||||
|
||||
void emitBack(int value, int prefix = 0xFF) {
|
||||
BusFrame f{BusDir::back, 0xFF, prefix & 0xFF, value & 0xFF};
|
||||
void emitBack(int value, int prefix = 0xFF, int gateway = -1,
|
||||
BusFrameSource source = BusFrameSource::local) {
|
||||
BusFrame f{BusDir::back, 0xFF, prefix & 0xFF, value & 0xFF, -1, gateway, source};
|
||||
rawFrames.push_back(f);
|
||||
if (rawSink_) rawSink_(f);
|
||||
|
||||
auto rec = decoder.decodeCmdResponse(value & 0xFF, prefix & 0xFF);
|
||||
auto rec = decoder.decodeCmdResponse(value & 0xFF, prefix & 0xFF, gateway, sourceName(source));
|
||||
records.push_back(rec);
|
||||
if (decodedSink_) decodedSink_(rec);
|
||||
}
|
||||
@@ -66,6 +73,10 @@ class BusMonitor {
|
||||
private:
|
||||
BusMonitor() = default;
|
||||
|
||||
static std::string sourceName(BusFrameSource source) {
|
||||
return source == BusFrameSource::external ? "external" : "local";
|
||||
}
|
||||
|
||||
std::function<void(const BusFrame&)> rawSink_;
|
||||
std::function<void(const DecodedRecord&)> decodedSink_;
|
||||
};
|
||||
|
||||
+23
-12
@@ -5,12 +5,16 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
struct DecodedRecord {
|
||||
std::string text;
|
||||
std::string type;
|
||||
int addr = -1;
|
||||
int cmd = -1;
|
||||
int proto = -1;
|
||||
int gateway = -1;
|
||||
std::string source;
|
||||
};
|
||||
|
||||
class DaliDecode {
|
||||
@@ -26,24 +30,26 @@ class DaliDecode {
|
||||
|
||||
int isQueryCmd(int cmd) const;
|
||||
|
||||
DecodedRecord decodeBright(int addr, int level);
|
||||
DecodedRecord decodeScene(int addr, int sceneCmd);
|
||||
DecodedRecord decodeCmd(int addr, int c);
|
||||
DecodedRecord decodeSpCmd(int addr, int c);
|
||||
DecodedRecord querySpCMD(int cmdByte, int dataByte);
|
||||
DecodedRecord decodeQuery(int addr, int c);
|
||||
DecodedRecord decodeCmdResponse(int value, int gwPrefix = 0xFF);
|
||||
DecodedRecord decode(int addr, int c, int proto = 0x10);
|
||||
DecodedRecord decodeBright(int addr, int level, int gateway = -1, const std::string& source = "");
|
||||
DecodedRecord decodeScene(int addr, int sceneCmd, int gateway = -1,
|
||||
const std::string& source = "");
|
||||
DecodedRecord decodeCmd(int addr, int c, int gateway = -1, const std::string& source = "");
|
||||
DecodedRecord decodeSpCmd(int addr, int c, int gateway = -1, const std::string& source = "");
|
||||
DecodedRecord querySpCMD(int cmdByte, int dataByte, int gateway = -1,
|
||||
const std::string& source = "");
|
||||
DecodedRecord decodeQuery(int addr, int c, int gateway = -1, const std::string& source = "",
|
||||
int deviceType = -1);
|
||||
DecodedRecord decodeCmdResponse(int value, int gwPrefix = 0xFF, int gateway = -1,
|
||||
const std::string& source = "");
|
||||
DecodedRecord decode(int addr, int c, int proto = 0x10, int gateway = -1,
|
||||
const std::string& source = "");
|
||||
|
||||
private:
|
||||
std::map<int, std::string> cmd_;
|
||||
std::map<int, std::string> sCMD_;
|
||||
std::vector<int> queryCmd_;
|
||||
|
||||
int lastQueryCmd_ = 0;
|
||||
int64_t lastQueryAtMs_ = 0;
|
||||
int pendingColourType_ = -1;
|
||||
int64_t lastColourQueryAtMs_ = 0;
|
||||
std::map<int, DaliDecodeContext> contexts_;
|
||||
|
||||
static int64_t nowMs();
|
||||
static std::string hex(int v);
|
||||
@@ -56,5 +62,10 @@ class DaliDecode {
|
||||
static std::string booleanQueryLabel(int c);
|
||||
static std::string fadeTimeLabel(int code);
|
||||
static std::string fadeRateLabel(int code);
|
||||
DaliDecodeContext& contextFor(int gateway);
|
||||
void syncDefaultDtrFromContext();
|
||||
void syncDefaultDtrToContext();
|
||||
DecodedRecord decodeDeviceTypeCommand(int addr, int c, const DtCommandSpec& spec, int proto,
|
||||
int gateway, const std::string& source);
|
||||
DecodedRecord withRaw(const DecodedRecord& r) const;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
struct DaliDecodeContext {
|
||||
int lastQueryCmd = 0;
|
||||
int lastQueryDeviceType = -1;
|
||||
int64_t lastQueryAtMs = 0;
|
||||
int dtr0 = 0;
|
||||
int dtr1 = 0;
|
||||
int dtr2 = 0;
|
||||
int pendingColourType = -1;
|
||||
int64_t lastColourQueryAtMs = 0;
|
||||
int pendingDeviceType = -1;
|
||||
};
|
||||
|
||||
struct DtCommandSpec {
|
||||
std::string name;
|
||||
std::string type = "cmd";
|
||||
bool isQuery = false;
|
||||
std::function<std::string(const DaliDecodeContext&)> detail;
|
||||
|
||||
std::string format(const DaliDecodeContext& context) const;
|
||||
};
|
||||
|
||||
struct DeviceTypeDecodeTable {
|
||||
int deviceType = -1;
|
||||
std::map<int, DtCommandSpec> commands;
|
||||
std::map<int, DtCommandSpec> queries;
|
||||
|
||||
const DtCommandSpec* command(int code) const;
|
||||
const DtCommandSpec* query(int code) const;
|
||||
};
|
||||
|
||||
const DeviceTypeDecodeTable* deviceTypeDecodeTable(int deviceType);
|
||||
|
||||
std::string dtr0Detail(const DaliDecodeContext& context);
|
||||
std::string dtr1Detail(const DaliDecodeContext& context);
|
||||
std::string dtr2Detail(const DaliDecodeContext& context);
|
||||
std::string dtr10WordDetail(const DaliDecodeContext& context, const std::string& label);
|
||||
std::string dt8CoordinateDetail(const DaliDecodeContext& context, const std::string& label);
|
||||
std::string dt8MirekDetail(const DaliDecodeContext& context);
|
||||
std::string dt8RgbDetail(const DaliDecodeContext& context);
|
||||
std::string dt8WafDetail(const DaliDecodeContext& context);
|
||||
std::string dt8PrimaryDetail(const DaliDecodeContext& context);
|
||||
std::string dt8ControlDetail(const DaliDecodeContext& context);
|
||||
|
||||
const DeviceTypeDecodeTable& dt1DecodeTable();
|
||||
const DeviceTypeDecodeTable& dt4DecodeTable();
|
||||
const DeviceTypeDecodeTable& dt5DecodeTable();
|
||||
const DeviceTypeDecodeTable& dt6DecodeTable();
|
||||
const DeviceTypeDecodeTable& dt8DecodeTable();
|
||||
+135
-56
@@ -50,15 +50,6 @@ DaliDecode::DaliDecode() {
|
||||
{DALI_CMD_QUERY_RANDOM_ADDRESS_M, "QUERY_RANDOM_ADDRESS_(M)"},
|
||||
{DALI_CMD_QUERY_RANDOM_ADDRESS_L, "QUERY_RANDOM_ADDRESS_(L)"},
|
||||
{DALI_CMD_READ_MEMORY_LOCATION, "READ_MEMORY_LOCATION"},
|
||||
{DALI_CMD_DT8_STORE_DTR_AS_COLORX, "STORE_DTR_AS_COLORX"},
|
||||
{DALI_CMD_DT8_STORE_DTR_AS_COLORY, "STORE_DTR_AS_COLORY"},
|
||||
{DALI_CMD_DT8_ACTIVATE, "ACTIVATE"},
|
||||
{DALI_CMD_DT8_SET_COLOR_TEMPERATURE, "SET_COLOR_TEMPERATURE"},
|
||||
{DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE, "STEP_UP_COLOR_TEMPERATURE"},
|
||||
{DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE, "STEP_DOWN_COLOR_TEMPERATURE"},
|
||||
{DALI_CMD_QUERY_COLOR_STATUS, "QUERY_COLOR_STATUS"},
|
||||
{DALI_CMD_QUERY_COLOR_TYPE, "QUERY_COLOR_TYPE"},
|
||||
{DALI_CMD_QUERY_COLOR_VALUE, "QUERY_COLOR_VALUE"},
|
||||
};
|
||||
|
||||
sCMD_ = {
|
||||
@@ -111,12 +102,7 @@ DaliDecode::DaliDecode() {
|
||||
DALI_CMD_QUERY_RANDOM_ADDRESS_H,
|
||||
DALI_CMD_QUERY_RANDOM_ADDRESS_M,
|
||||
DALI_CMD_QUERY_RANDOM_ADDRESS_L,
|
||||
DALI_CMD_READ_MEMORY_LOCATION,
|
||||
DALI_CMD_DT8_ACTIVATE,
|
||||
DALI_CMD_DT8_SET_COLOR_TEMPERATURE,
|
||||
DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE,
|
||||
DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE,
|
||||
DALI_CMD_QUERY_COLOR_VALUE};
|
||||
DALI_CMD_READ_MEMORY_LOCATION};
|
||||
for (int c = DALI_CMD_QUERY_SCENE_LEVEL_MIN; c <= DALI_CMD_QUERY_SCENE_LEVEL_MAX; c++) {
|
||||
queryCmd_.push_back(c);
|
||||
}
|
||||
@@ -126,17 +112,39 @@ int DaliDecode::isQueryCmd(int cmd) const {
|
||||
return std::find(queryCmd_.begin(), queryCmd_.end(), cmd) != queryCmd_.end() ? 1 : 0;
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeBright(int addr, int level) {
|
||||
DaliDecodeContext& DaliDecode::contextFor(int gateway) {
|
||||
return contexts_[gateway];
|
||||
}
|
||||
|
||||
void DaliDecode::syncDefaultDtrFromContext() {
|
||||
const auto it = contexts_.find(-1);
|
||||
if (it == contexts_.end()) return;
|
||||
dtr0 = it->second.dtr0;
|
||||
dtr1 = it->second.dtr1;
|
||||
dtr2 = it->second.dtr2;
|
||||
}
|
||||
|
||||
void DaliDecode::syncDefaultDtrToContext() {
|
||||
auto& context = contexts_[-1];
|
||||
context.dtr0 = dtr0 & 0xFF;
|
||||
context.dtr1 = dtr1 & 0xFF;
|
||||
context.dtr2 = dtr2 & 0xFF;
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeBright(int addr, int level, int gateway, const std::string& source) {
|
||||
DecodedRecord r;
|
||||
r.text = whoLabel(addr, true) + " DAPC level=" + std::to_string(level);
|
||||
r.type = "brightness";
|
||||
r.addr = addr;
|
||||
r.cmd = level;
|
||||
r.proto = 0x10;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd) {
|
||||
DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd, int gateway,
|
||||
const std::string& source) {
|
||||
const int sc = sceneCmd - DALI_CMD_GO_TO_SCENE_MIN;
|
||||
DecodedRecord r;
|
||||
r.text = whoLabel(addr, false) + " GO_TO_SCENE " + std::to_string(sc);
|
||||
@@ -144,10 +152,12 @@ DecodedRecord DaliDecode::decodeScene(int addr, int sceneCmd) {
|
||||
r.addr = addr;
|
||||
r.cmd = sceneCmd;
|
||||
r.proto = 0x10;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeCmd(int addr, int c) {
|
||||
DecodedRecord DaliDecode::decodeCmd(int addr, int c, int gateway, const std::string& source) {
|
||||
std::string name = "CMD 0x" + hex(c);
|
||||
const auto it = cmd_.find(c);
|
||||
if (it != cmd_.end()) name = it->second;
|
||||
@@ -166,16 +176,21 @@ DecodedRecord DaliDecode::decodeCmd(int addr, int c) {
|
||||
r.addr = addr;
|
||||
r.cmd = c;
|
||||
r.proto = 0x10;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
|
||||
DecodedRecord DaliDecode::decodeSpCmd(int addr, int c, int gateway, const std::string& source) {
|
||||
auto& context = contextFor(gateway);
|
||||
if (gateway == -1) syncDefaultDtrToContext();
|
||||
const int cmdByte = addr & 0xFF;
|
||||
const int dataByte = c & 0xFF;
|
||||
|
||||
if (cmdByte == DALI_CMD_SPECIAL_COMPARE || cmdByte == DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS ||
|
||||
cmdByte == DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS) {
|
||||
return querySpCMD(cmdByte, dataByte);
|
||||
context.pendingDeviceType = -1;
|
||||
return querySpCMD(cmdByte, dataByte, gateway, source);
|
||||
}
|
||||
|
||||
std::string name = "SPECIAL_CMD 0x" + hex(cmdByte);
|
||||
@@ -183,12 +198,20 @@ DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
|
||||
if (it != sCMD_.end()) name = it->second;
|
||||
|
||||
if (cmdByte == DALI_CMD_SPECIAL_SET_DTR0) {
|
||||
dtr0 = dataByte;
|
||||
context.pendingDeviceType = -1;
|
||||
context.dtr0 = dataByte;
|
||||
} else if (cmdByte == DALI_CMD_SPECIAL_SET_DTR_1) {
|
||||
dtr1 = dataByte;
|
||||
context.pendingDeviceType = -1;
|
||||
context.dtr1 = dataByte;
|
||||
} else if (cmdByte == DALI_CMD_SPECIAL_SET_DTR_2) {
|
||||
dtr2 = dataByte;
|
||||
context.pendingDeviceType = -1;
|
||||
context.dtr2 = dataByte;
|
||||
} else if (cmdByte == DALI_CMD_SPECIAL_DT_SELECT) {
|
||||
context.pendingDeviceType = dataByte;
|
||||
} else {
|
||||
context.pendingDeviceType = -1;
|
||||
}
|
||||
if (gateway == -1) syncDefaultDtrFromContext();
|
||||
|
||||
DecodedRecord r;
|
||||
r.text = name + " data=0x" + hex(dataByte) + " (" + std::to_string(dataByte) + ")";
|
||||
@@ -196,12 +219,17 @@ DecodedRecord DaliDecode::decodeSpCmd(int addr, int c) {
|
||||
r.addr = addr;
|
||||
r.cmd = c;
|
||||
r.proto = 0x10;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte) {
|
||||
lastQueryCmd_ = cmdByte;
|
||||
lastQueryAtMs_ = nowMs();
|
||||
DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte, int gateway,
|
||||
const std::string& source) {
|
||||
auto& context = contextFor(gateway);
|
||||
context.lastQueryCmd = cmdByte;
|
||||
context.lastQueryDeviceType = -1;
|
||||
context.lastQueryAtMs = nowMs();
|
||||
|
||||
std::string name = "SPECIAL_CMD 0x" + hex(cmdByte);
|
||||
const auto it = sCMD_.find(cmdByte);
|
||||
@@ -213,30 +241,41 @@ DecodedRecord DaliDecode::querySpCMD(int cmdByte, int dataByte) {
|
||||
r.addr = cmdByte;
|
||||
r.cmd = dataByte;
|
||||
r.proto = 0x10;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeQuery(int addr, int c) {
|
||||
lastQueryCmd_ = c;
|
||||
lastQueryAtMs_ = nowMs();
|
||||
DecodedRecord DaliDecode::decodeQuery(int addr, int c, int gateway, const std::string& source,
|
||||
int deviceType) {
|
||||
auto& context = contextFor(gateway);
|
||||
if (gateway == -1) syncDefaultDtrToContext();
|
||||
context.lastQueryCmd = c;
|
||||
context.lastQueryDeviceType = deviceType;
|
||||
context.lastQueryAtMs = nowMs();
|
||||
|
||||
std::string name = "QUERY 0x" + hex(c);
|
||||
const auto it = cmd_.find(c);
|
||||
if (it != cmd_.end()) name = it->second;
|
||||
const auto* table = deviceTypeDecodeTable(deviceType);
|
||||
if (table != nullptr) {
|
||||
const auto* spec = table->query(c);
|
||||
if (spec != nullptr) name = spec->format(context);
|
||||
}
|
||||
|
||||
if (c >= DALI_CMD_QUERY_SCENE_LEVEL_MIN && c <= DALI_CMD_QUERY_SCENE_LEVEL_MAX) {
|
||||
name = "QUERY_SCENE_LEVEL " + std::to_string(c - DALI_CMD_QUERY_SCENE_LEVEL_MIN);
|
||||
}
|
||||
|
||||
if (c == DALI_CMD_QUERY_COLOR_VALUE) {
|
||||
lastColourQueryAtMs_ = nowMs();
|
||||
int t = dtr0;
|
||||
context.lastColourQueryAtMs = nowMs();
|
||||
int t = context.dtr0;
|
||||
if (t == 128) t = 0;
|
||||
if (t == 130) t = 1;
|
||||
pendingColourType_ = (t == 0 || t == 1 || t == 2) ? t : -1;
|
||||
if (pendingColourType_ == 0) name = "QUERY_COLOUR_VALUE(x)";
|
||||
if (pendingColourType_ == 1) name = "QUERY_COLOUR_VALUE(y)";
|
||||
if (pendingColourType_ == 2) name = "QUERY_COLOUR_VALUE(ct)";
|
||||
context.pendingColourType = (t == 0 || t == 1 || t == 2) ? t : -1;
|
||||
if (context.pendingColourType == 0) name = "QUERY_COLOUR_VALUE(x)";
|
||||
if (context.pendingColourType == 1) name = "QUERY_COLOUR_VALUE(y)";
|
||||
if (context.pendingColourType == 2) name = "QUERY_COLOUR_VALUE(ct)";
|
||||
}
|
||||
|
||||
DecodedRecord r;
|
||||
@@ -245,24 +284,33 @@ DecodedRecord DaliDecode::decodeQuery(int addr, int c) {
|
||||
r.addr = addr;
|
||||
r.cmd = c;
|
||||
r.proto = 0x12;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
|
||||
DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/, int gateway,
|
||||
const std::string& source) {
|
||||
auto& context = contextFor(gateway);
|
||||
if (gateway == -1) syncDefaultDtrToContext();
|
||||
const int64_t now = nowMs();
|
||||
if ((now - lastQueryAtMs_) > responseWindowMs || lastQueryCmd_ == 0) {
|
||||
if ((now - context.lastQueryAtMs) > responseWindowMs || context.lastQueryCmd == 0) {
|
||||
DecodedRecord r{"unknown back 0x" + hex(value) + " / " + std::to_string(value), "unknown", -1,
|
||||
-1, 0xFF};
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
const int c = lastQueryCmd_;
|
||||
lastQueryCmd_ = 0;
|
||||
lastQueryAtMs_ = 0;
|
||||
const int c = context.lastQueryCmd;
|
||||
context.lastQueryCmd = 0;
|
||||
context.lastQueryDeviceType = -1;
|
||||
context.lastQueryAtMs = 0;
|
||||
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR) dtr0 = value & 0xFF;
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR1) dtr1 = value & 0xFF;
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR2) dtr2 = value & 0xFF;
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR) context.dtr0 = value & 0xFF;
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR1) context.dtr1 = value & 0xFF;
|
||||
if (c == DALI_CMD_QUERY_CONTENT_DTR2) context.dtr2 = value & 0xFF;
|
||||
if (gateway == -1) syncDefaultDtrFromContext();
|
||||
|
||||
if (c == DALI_CMD_QUERY_STATUS) {
|
||||
std::ostringstream oss;
|
||||
@@ -407,14 +455,14 @@ DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
|
||||
"response", -1, c, 0xFF});
|
||||
}
|
||||
|
||||
if (pendingColourType_ >= 0 && (now - lastColourQueryAtMs_) <= 1000) {
|
||||
const int combined = dtr1 * 256 + dtr0;
|
||||
if (context.pendingColourType >= 0 && (now - context.lastColourQueryAtMs) <= 1000) {
|
||||
const int combined = context.dtr1 * 256 + context.dtr0;
|
||||
std::string pretty;
|
||||
if (pendingColourType_ == 2) {
|
||||
if (context.pendingColourType == 2) {
|
||||
const int kelvin = combined == 0 ? 0 : (1000000 / combined);
|
||||
pretty = "ct mirek=" + std::to_string(combined) + " kelvin=" + std::to_string(kelvin);
|
||||
} else {
|
||||
const char key = pendingColourType_ == 0 ? 'x' : 'y';
|
||||
const char key = context.pendingColourType == 0 ? 'x' : 'y';
|
||||
std::ostringstream oss;
|
||||
oss << key << "=" << std::fixed << std::setprecision(4)
|
||||
<< (static_cast<double>(combined) / 65535.0) << " (" << combined << ")";
|
||||
@@ -427,30 +475,61 @@ DecodedRecord DaliDecode::decodeCmdResponse(int value, int /*gwPrefix*/) {
|
||||
-1, c, 0xFF});
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decode(int addr, int c, int proto) {
|
||||
DecodedRecord DaliDecode::decode(int addr, int c, int proto, int gateway, const std::string& source) {
|
||||
auto& context = contextFor(gateway);
|
||||
if (gateway == -1) syncDefaultDtrToContext();
|
||||
if (proto == 0x12) {
|
||||
return decodeQuery(addr, c);
|
||||
const int selectedDeviceType = context.pendingDeviceType;
|
||||
context.pendingDeviceType = -1;
|
||||
return decodeQuery(addr, c, gateway, source, selectedDeviceType);
|
||||
}
|
||||
if (addr >= DALI_CMD_SPECIAL_RANGE_MIN && addr <= DALI_CMD_SPECIAL_RANGE_MAX) {
|
||||
return decodeSpCmd(addr, c);
|
||||
return decodeSpCmd(addr, c, gateway, source);
|
||||
}
|
||||
const int selectedDeviceType = context.pendingDeviceType;
|
||||
context.pendingDeviceType = -1;
|
||||
const auto* table = deviceTypeDecodeTable(selectedDeviceType);
|
||||
if (table != nullptr) {
|
||||
const auto* querySpec = table->query(c);
|
||||
if (querySpec != nullptr) {
|
||||
return decodeQuery(addr, c, gateway, source, selectedDeviceType);
|
||||
}
|
||||
const auto* commandSpec = table->command(c);
|
||||
if (commandSpec != nullptr) {
|
||||
return decodeDeviceTypeCommand(addr, c, *commandSpec, proto, gateway, source);
|
||||
}
|
||||
}
|
||||
if (isQueryCmd(c) == 1) {
|
||||
return decodeQuery(addr, c);
|
||||
return decodeQuery(addr, c, gateway, source);
|
||||
}
|
||||
if ((addr & 1) == 0) {
|
||||
return decodeBright(addr, c);
|
||||
return decodeBright(addr, c, gateway, source);
|
||||
}
|
||||
if (c >= DALI_CMD_GO_TO_SCENE_MIN && c <= DALI_CMD_GO_TO_SCENE_MAX) {
|
||||
return decodeScene(addr, c);
|
||||
return decodeScene(addr, c, gateway, source);
|
||||
}
|
||||
if (proto == 0x11) {
|
||||
if (c == DALI_CMD_QUERY_COLOR_VALUE) return decodeQuery(addr, c);
|
||||
std::string name = "EXT 0x" + hex(c);
|
||||
const auto it = cmd_.find(c);
|
||||
if (it != cmd_.end()) name = it->second;
|
||||
return withRaw({whoLabel(addr, false) + " " + name, "ext", addr, c, proto});
|
||||
return withRaw({whoLabel(addr, false) + " " + name, "ext", addr, c, proto, gateway, source});
|
||||
}
|
||||
return decodeCmd(addr, c);
|
||||
return decodeCmd(addr, c, gateway, source);
|
||||
}
|
||||
|
||||
DecodedRecord DaliDecode::decodeDeviceTypeCommand(int addr, int c, const DtCommandSpec& spec,
|
||||
int proto, int gateway,
|
||||
const std::string& source) {
|
||||
auto& context = contextFor(gateway);
|
||||
DecodedRecord r;
|
||||
r.text = whoLabel(addr, false) + " " + spec.format(context);
|
||||
r.type = proto == 0x11 ? "ext" : spec.type;
|
||||
r.addr = addr;
|
||||
r.cmd = c;
|
||||
r.proto = proto;
|
||||
r.gateway = gateway;
|
||||
r.source = source;
|
||||
return withRaw(r);
|
||||
}
|
||||
|
||||
int64_t DaliDecode::nowMs() {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
std::string byteHex(int value) {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (value & 0xFF);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string wordHex(int value) {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << (value & 0xFFFF);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string DtCommandSpec::format(const DaliDecodeContext& context) const {
|
||||
if (!detail) return name;
|
||||
const auto extra = detail(context);
|
||||
if (extra.empty()) return name;
|
||||
return name + " (" + extra + ")";
|
||||
}
|
||||
|
||||
const DtCommandSpec* DeviceTypeDecodeTable::command(int code) const {
|
||||
const auto it = commands.find(code & 0xFF);
|
||||
return it == commands.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
const DtCommandSpec* DeviceTypeDecodeTable::query(int code) const {
|
||||
const auto it = queries.find(code & 0xFF);
|
||||
return it == queries.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
const DeviceTypeDecodeTable* deviceTypeDecodeTable(int deviceType) {
|
||||
switch (deviceType) {
|
||||
case 1:
|
||||
return &dt1DecodeTable();
|
||||
case 4:
|
||||
return &dt4DecodeTable();
|
||||
case 5:
|
||||
return &dt5DecodeTable();
|
||||
case 6:
|
||||
return &dt6DecodeTable();
|
||||
case 8:
|
||||
return &dt8DecodeTable();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string dtr0Detail(const DaliDecodeContext& context) {
|
||||
return "DTR0=0x" + byteHex(context.dtr0) + " / " + std::to_string(context.dtr0);
|
||||
}
|
||||
|
||||
std::string dtr1Detail(const DaliDecodeContext& context) {
|
||||
return "DTR1=0x" + byteHex(context.dtr1) + " / " + std::to_string(context.dtr1);
|
||||
}
|
||||
|
||||
std::string dtr2Detail(const DaliDecodeContext& context) {
|
||||
return "DTR2=0x" + byteHex(context.dtr2) + " / " + std::to_string(context.dtr2);
|
||||
}
|
||||
|
||||
std::string dtr10WordDetail(const DaliDecodeContext& context, const std::string& label) {
|
||||
const int value = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
|
||||
return label + "=0x" + wordHex(value) + " / " + std::to_string(value) + ", DTR1=0x" +
|
||||
byteHex(context.dtr1) + ", DTR0=0x" + byteHex(context.dtr0);
|
||||
}
|
||||
|
||||
std::string dt8CoordinateDetail(const DaliDecodeContext& context, const std::string& label) {
|
||||
const int value = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
|
||||
std::ostringstream oss;
|
||||
oss << label << "=0x" << wordHex(value) << " / " << value << ", normalized=" << std::fixed
|
||||
<< std::setprecision(4) << (static_cast<double>(value) / 65535.0) << ", DTR1=0x"
|
||||
<< byteHex(context.dtr1) << ", DTR0=0x" << byteHex(context.dtr0);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string dt8MirekDetail(const DaliDecodeContext& context) {
|
||||
const int mirek = ((context.dtr1 & 0xFF) << 8) | (context.dtr0 & 0xFF);
|
||||
const int kelvin = mirek > 0 ? 1000000 / mirek : 0;
|
||||
return "mirek=0x" + wordHex(mirek) + " / " + std::to_string(mirek) +
|
||||
", kelvin=" + std::to_string(kelvin) + ", DTR1=0x" + byteHex(context.dtr1) +
|
||||
", DTR0=0x" + byteHex(context.dtr0);
|
||||
}
|
||||
|
||||
std::string dt8RgbDetail(const DaliDecodeContext& context) {
|
||||
return "R=" + std::to_string(context.dtr0) + ", G=" + std::to_string(context.dtr1) +
|
||||
", B=" + std::to_string(context.dtr2);
|
||||
}
|
||||
|
||||
std::string dt8WafDetail(const DaliDecodeContext& context) {
|
||||
return "W=" + std::to_string(context.dtr0) + ", A=" + std::to_string(context.dtr1) +
|
||||
", F=" + std::to_string(context.dtr2);
|
||||
}
|
||||
|
||||
std::string dt8PrimaryDetail(const DaliDecodeContext& context) {
|
||||
return dtr10WordDetail(context, "value") + ", primary=" + std::to_string(context.dtr2 & 0xFF);
|
||||
}
|
||||
|
||||
std::string dt8ControlDetail(const DaliDecodeContext& context) {
|
||||
return "control=0x" + byteHex(context.dtr0) + " / " + std::to_string(context.dtr0);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
const DeviceTypeDecodeTable& dt1DecodeTable() {
|
||||
static const DeviceTypeDecodeTable table = {
|
||||
1,
|
||||
{
|
||||
{0xE0, {"REST"}},
|
||||
{0xE1, {"INHIBIT"}},
|
||||
{0xE2, {"RE_LIGHT_OR_RESET_INHIBIT"}},
|
||||
{0xE3, {"START_FUNCTION_TEST"}},
|
||||
{0xE4, {"START_DURATION_TEST"}},
|
||||
{0xE5, {"STOP_TEST"}},
|
||||
{0xE6, {"RESET_FUNCTION_TEST_DONE_FLAG"}},
|
||||
{0xE7, {"RESET_DURATION_TEST_DONE_FLAG"}},
|
||||
{0xE8, {"RESET_LAMP_TIME"}},
|
||||
{0xE9, {"STORE_EMERGENCY_LEVEL", "cmd", false, dtr0Detail}},
|
||||
{0xEA, {"STORE_TEST_DELAY_TIME_HIGH_BYTE", "cmd", false, dtr0Detail}},
|
||||
{0xEB, {"STORE_TEST_DELAY_TIME_LOW_BYTE", "cmd", false, dtr0Detail}},
|
||||
{0xEC, {"STORE_FUNCTION_TEST_INTERVAL", "cmd", false, dtr0Detail}},
|
||||
{0xED, {"STORE_DURATION_TEST_INTERVAL", "cmd", false, dtr0Detail}},
|
||||
{0xEE, {"STORE_TEST_EXECUTION_TIMEOUT", "cmd", false, dtr0Detail}},
|
||||
{0xEF, {"STORE_PROLONG_TIME", "cmd", false, dtr0Detail}},
|
||||
{0xF0, {"START_IDENTIFICATION"}},
|
||||
{0xFE, {"PERFORM_DTR_SELECTED_FUNCTION", "cmd", false, dtr0Detail}},
|
||||
},
|
||||
{
|
||||
{0xF1, {"QUERY_BATTERY_CHARGE_LEVEL", "query", true}},
|
||||
{0xF2, {"QUERY_TEST_TIMING", "query", true, dtr0Detail}},
|
||||
{0xF3, {"QUERY_DURATION_TEST_RESULT", "query", true}},
|
||||
{0xF4, {"QUERY_LAMP_EMERGENCY_TIME", "query", true}},
|
||||
{0xF5, {"QUERY_LAMP_TOTAL_OPERATION_TIME", "query", true}},
|
||||
{0xF6, {"QUERY_EMERGENCY_LEVEL", "query", true}},
|
||||
{0xF7, {"QUERY_EMERGENCY_MIN_LEVEL", "query", true}},
|
||||
{0xF8, {"QUERY_EMERGENCY_MAX_LEVEL", "query", true}},
|
||||
{0xF9, {"QUERY_RATED_DURATION", "query", true}},
|
||||
{0xFA, {"QUERY_EMERGENCY_MODE", "query", true}},
|
||||
{0xFB, {"QUERY_FEATURES", "query", true}},
|
||||
{0xFC, {"QUERY_FAILURE_STATUS", "query", true}},
|
||||
{0xFD, {"QUERY_EMERGENCY_STATUS", "query", true}},
|
||||
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
|
||||
}};
|
||||
return table;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
const DeviceTypeDecodeTable& dt4DecodeTable() {
|
||||
static const DeviceTypeDecodeTable table = {
|
||||
4,
|
||||
{
|
||||
{0xE0, {"REFERENCE_SYSTEM_POWER"}},
|
||||
{0xE1, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
|
||||
},
|
||||
{
|
||||
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
|
||||
{0xEF, {"QUERY_RATED_MINIMUM_LEVEL", "query", true}},
|
||||
{0xF0, {"QUERY_RATED_MAXIMUM_LEVEL", "query", true}},
|
||||
{0xF1, {"QUERY_SYSTEM_POWER", "query", true}},
|
||||
{0xF2, {"QUERY_DIMMER_TEMPERATURE", "query", true}},
|
||||
{0xF3, {"QUERY_RMS_SUPPLY_VOLTAGE", "query", true}},
|
||||
{0xF4, {"QUERY_SUPPLY_FREQUENCY", "query", true}},
|
||||
{0xF5, {"QUERY_RMS_LOAD_VOLTAGE", "query", true}},
|
||||
{0xF6, {"QUERY_RMS_LOAD_CURRENT", "query", true}},
|
||||
{0xF7, {"QUERY_REAL_LOAD_POWER", "query", true}},
|
||||
{0xF8, {"QUERY_LOAD_RATING", "query", true}},
|
||||
{0xF9, {"QUERY_REFERENCE_RUNNING", "query", true}},
|
||||
{0xFA, {"QUERY_REFERENCE_MEASUREMENT_FAILED", "query", true}},
|
||||
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
|
||||
}};
|
||||
return table;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
const DeviceTypeDecodeTable& dt5DecodeTable() {
|
||||
static const DeviceTypeDecodeTable table = {
|
||||
5,
|
||||
{
|
||||
{0xE0, {"SET_OUTPUT_RANGE_1_TO_10V"}},
|
||||
{0xE1, {"SET_OUTPUT_RANGE_0_TO_10V"}},
|
||||
{0xE2, {"SWITCH_ON_INTERNAL_PULL_UP"}},
|
||||
{0xE3, {"SWITCH_OFF_INTERNAL_PULL_UP"}},
|
||||
{0xE4, {"STORE_DTR_AS_PHYSICAL_MINIMUM_LEVEL", "cmd", false, dtr0Detail}},
|
||||
{0xE5, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
|
||||
{0xE6, {"RESET_CONVERTER_SETTINGS"}},
|
||||
},
|
||||
{
|
||||
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
|
||||
{0xEF, {"QUERY_OUTPUT_LEVEL", "query", true}},
|
||||
{0xF0, {"QUERY_CONVERTER_STATUS", "query", true}},
|
||||
{0xF1, {"QUERY_OUTPUT_RANGE", "query", true}},
|
||||
{0xF2, {"QUERY_FEATURES", "query", true}},
|
||||
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
|
||||
}};
|
||||
return table;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
const DeviceTypeDecodeTable& dt6DecodeTable() {
|
||||
static const DeviceTypeDecodeTable table = {
|
||||
6,
|
||||
{
|
||||
{0xE0, {"REFERENCE_SYSTEM_POWER"}},
|
||||
{0xE1, {"ENABLE_CURRENT_PROTECTOR"}},
|
||||
{0xE2, {"DISABLE_CURRENT_PROTECTOR"}},
|
||||
{0xE3, {"SELECT_DIMMING_CURVE", "cmd", false, dtr0Detail}},
|
||||
{0xE4, {"STORE_DTR_AS_FAST_FADE_TIME", "cmd", false, dtr0Detail}},
|
||||
},
|
||||
{
|
||||
{0xED, {"QUERY_GEAR_TYPE", "query", true}},
|
||||
{0xEE, {"QUERY_DIMMING_CURVE", "query", true}},
|
||||
{0xEF, {"QUERY_POSSIBLE_OPERATING_MODES", "query", true}},
|
||||
{0xF0, {"QUERY_FEATURES", "query", true}},
|
||||
{0xF1, {"QUERY_FAILURE_STATUS", "query", true}},
|
||||
{0xF2, {"QUERY_SHORT_CIRCUIT", "query", true}},
|
||||
{0xF3, {"QUERY_OPEN_CIRCUIT", "query", true}},
|
||||
{0xF4, {"QUERY_LOAD_DECREASE", "query", true}},
|
||||
{0xF5, {"QUERY_LOAD_INCREASE", "query", true}},
|
||||
{0xF6, {"QUERY_CURRENT_PROTECTOR_ACTIVE", "query", true}},
|
||||
{0xF7, {"QUERY_THERMAL_SHUTDOWN", "query", true}},
|
||||
{0xF8, {"QUERY_THERMAL_OVERLOAD", "query", true}},
|
||||
{0xF9, {"QUERY_REFERENCE_RUNNING", "query", true}},
|
||||
{0xFA, {"QUERY_REFERENCE_MEASUREMENT_FAILED", "query", true}},
|
||||
{0xFB, {"QUERY_CURRENT_PROTECTOR_ENABLED", "query", true}},
|
||||
{0xFC, {"QUERY_OPERATING_MODE", "query", true}},
|
||||
{0xFD, {"QUERY_FAST_FADE_TIME", "query", true}},
|
||||
{0xFE, {"QUERY_MIN_FAST_FADE_TIME", "query", true}},
|
||||
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
|
||||
}};
|
||||
return table;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "decode_device_type.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string colorXDetail(const DaliDecodeContext& context) {
|
||||
return dt8CoordinateDetail(context, "x");
|
||||
}
|
||||
|
||||
std::string colorYDetail(const DaliDecodeContext& context) {
|
||||
return dt8CoordinateDetail(context, "y");
|
||||
}
|
||||
|
||||
std::string mirekLimitDetail(const DaliDecodeContext& context) {
|
||||
return dt8MirekDetail(context) + ", limit=" + std::to_string(context.dtr2 & 0xFF);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const DeviceTypeDecodeTable& dt8DecodeTable() {
|
||||
static const DeviceTypeDecodeTable table = {
|
||||
8,
|
||||
{
|
||||
{0xE0, {"STORE_DTR_AS_COLORX", "cmd", false, colorXDetail}},
|
||||
{0xE1, {"STORE_DTR_AS_COLORY", "cmd", false, colorYDetail}},
|
||||
{0xE2, {"ACTIVATE"}},
|
||||
{0xE3, {"STEP_UP_COLORX"}},
|
||||
{0xE4, {"STEP_DOWN_COLORX"}},
|
||||
{0xE5, {"STEP_UP_COLORY"}},
|
||||
{0xE6, {"STEP_DOWN_COLORY"}},
|
||||
{0xE7, {"SET_COLOR_TEMPERATURE", "cmd", false, dt8MirekDetail}},
|
||||
{0xE8, {"STEP_UP_COLOR_TEMPERATURE"}},
|
||||
{0xE9, {"STEP_DOWN_COLOR_TEMPERATURE"}},
|
||||
{0xEA, {"SET_TEMPORARY_PRIMARY_N_DIM_LEVEL", "cmd", false, dt8PrimaryDetail}},
|
||||
{0xEB, {"SET_TEMPORARY_RGB_DIM_LEVELS", "cmd", false, dt8RgbDetail}},
|
||||
{0xEC, {"SET_TEMPORARY_WAF_DIM_LEVELS", "cmd", false, dt8WafDetail}},
|
||||
{0xED, {"SET_TEMPORARY_RGBWAF_CONTROL", "cmd", false, dt8ControlDetail}},
|
||||
{0xEE, {"COPY_REPORT_TO_TEMPORARY"}},
|
||||
{0xF0, {"STORE_XY_PRIMARY_N", "cmd", false, dt8PrimaryDetail}},
|
||||
{0xF1, {"STORE_TEMPORARY_XY_AS_PRIMARY_N", "cmd", false, dtr2Detail}},
|
||||
{0xF2, {"STORE_COLOR_TEMPERATURE_LIMIT", "cmd", false, mirekLimitDetail}},
|
||||
{0xF3, {"STORE_GEAR_FEATURES_STATUS", "cmd", false, dtr0Detail}},
|
||||
{0xF5, {"ASSIGN_COLOR_TO_LINKED_CHANNELS", "cmd", false, dtr0Detail}},
|
||||
{0xF6, {"START_AUTO_CALIBRATION"}},
|
||||
},
|
||||
{
|
||||
{0xF7, {"QUERY_GEAR_FEATURES_STATUS", "query", true}},
|
||||
{0xF8, {"QUERY_COLOR_STATUS", "query", true}},
|
||||
{0xF9, {"QUERY_COLOR_TYPE_FEATURES", "query", true}},
|
||||
{0xFA, {"QUERY_COLOUR_VALUE", "query", true, dtr0Detail}},
|
||||
{0xFB, {"QUERY_RGBWAF_CONTROL", "query", true}},
|
||||
{0xFC, {"QUERY_ASSIGNED_COLOR_FOR_CHANNEL", "query", true, dtr0Detail}},
|
||||
{0xFF, {"QUERY_EXTENDED_VERSION_NUMBER", "query", true}},
|
||||
}};
|
||||
return table;
|
||||
}
|
||||
Reference in New Issue
Block a user