#include "dt8.hpp" #include "dali_define.hpp" #include #include namespace { int colourTempLimitQuerySelector(int limitType) { switch (limitType & 0xFF) { case 0: return 128; case 1: return 130; case 2: return 129; case 3: return 131; default: return (limitType & 0x01) == 0 ? 128 : 130; } } std::optional kelvinFromMirek(std::optional mirek) { if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } } // namespace DaliDT8::DaliDT8(DaliBase& base) : base_(base) {} bool DaliDT8::enableDT8() { return base_.dtSelect(8); } std::optional DaliDT8::getColorTypeFeature(int a) { const auto addr = DaliComm::toCmdAddr(a); base_.dtSelect(8); const auto result = base_.queryCmd(addr, DALI_CMD_QUERY_COLOR_TYPE); if (!result.has_value()) return std::nullopt; return ColorTypeFeature(result.value()); } std::optional DaliDT8::getColorStatus(int a) { const auto addr = DaliComm::toCmdAddr(a); base_.dtSelect(8); const auto result = base_.queryCmd(addr, DALI_CMD_QUERY_COLOR_STATUS); if (!result.has_value()) return std::nullopt; return ColorStatus(result.value()); } std::optional DaliDT8::getColTempRaw(int a, int type) { int selector; switch (type) { case 0: selector = 128; break; case 1: selector = 130; break; case 3: selector = 129; break; case 4: selector = 131; break; case 2: default: selector = 2; break; } const auto features = getColorTypeFeature(a); if (!features.has_value() || !features->ctCapable()) return 0; const auto v = getColourRaw(a, selector); if (!v.has_value()) return 0; return v.value(); } bool DaliDT8::setColTempRaw(int a, int value) { int v = value; if (v < 0) v = 0; if (v > 65535) v = 65535; const int dtr = v & 0xFF; const int dtr1 = (v >> 8) & 0xFF; if (!base_.setDTR(dtr)) return false; if (!base_.setDTR1(dtr1)) return false; if (!base_.dtSelect(8)) return false; if (!base_.setDTRAsColourTemp(a)) return false; if (!base_.dtSelect(8)) return false; return base_.activate(a); } bool DaliDT8::setColorTemperature(int addr, int kelvin) { int v = kelvin == 0 ? 1 : kelvin; const int mirek = static_cast(std::floor(1000000.0 / static_cast(v))); return setColTempRaw(addr, mirek); } std::optional DaliDT8::getColorTemperature(int a) { const auto mirek = getColourRaw(a, 2); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; const int kelvin = static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); return kelvin; } std::optional DaliDT8::getMinColorTemperature(int a) { const auto mirek = getColTempRaw(a, 0); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getMaxColorTemperature(int a) { const auto mirek = getColTempRaw(a, 1); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getPhysicalMinColorTemperature(int a) { const auto mirek = getColTempRaw(a, 3); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getPhysicalMaxColorTemperature(int a) { const auto mirek = getColTempRaw(a, 4); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getColourTempLimitRaw(int address, int limitType) { return getColourRaw(address, colourTempLimitQuerySelector(limitType)); } std::optional DaliDT8::getColourTempLimit(int address, int limitType) { return kelvinFromMirek(getColourTempLimitRaw(address, limitType)); } bool DaliDT8::setColourRaw(int addr, int x1, int y1) { const int x1L = x1 & 0xFF; const int y1L = y1 & 0xFF; const int x1H = (x1 >> 8) & 0xFF; const int y1H = (y1 >> 8) & 0xFF; const int a = addr / 2; if (!base_.setDTR(x1L)) return false; if (!base_.setDTR1(x1H)) return false; if (!base_.dtSelect(8)) return false; if (!base_.setDTRAsColourX(a)) return false; if (!base_.setDTR(y1L)) return false; if (!base_.setDTR1(y1H)) return false; if (!base_.dtSelect(8)) return false; if (!base_.setDTRAsColourY(a)) return false; if (!base_.dtSelect(8)) return false; return base_.activate(a); } bool DaliDT8::setTemporaryColourXRaw(int addr, int x1) { const int x1L = x1 & 0xFF; const int x1H = (x1 >> 8) & 0xFF; const int a = addr / 2; return base_.setDTR(x1L) && base_.setDTR1(x1H) && base_.dtSelect(8) && base_.setDTRAsColourX(a); } bool DaliDT8::setTemporaryColourYRaw(int addr, int y1) { const int y1L = y1 & 0xFF; const int y1H = (y1 >> 8) & 0xFF; const int a = addr / 2; return base_.setDTR(y1L) && base_.setDTR1(y1H) && base_.dtSelect(8) && base_.setDTRAsColourY(a); } bool DaliDT8::setTemporaryColourXY(int a, double x, double y) { double xClamped = std::clamp(x, 0.0, 1.0); double yClamped = std::clamp(y, 0.0, 1.0); const int x1 = static_cast(std::round(xClamped * 65535.0)); const int y1 = static_cast(std::round(yClamped * 65535.0)); const int addr = a * 2 + 1; return setTemporaryColourXRaw(addr, x1) && setTemporaryColourYRaw(addr, y1); } bool DaliDT8::setTemporaryColourTemperature(int a, int kelvin) { int k = kelvin <= 0 ? 1 : kelvin; const int mirek = static_cast(std::floor(1000000.0 / static_cast(k))); const int addr = a * 2 + 1; const int dtr = mirek & 0xFF; const int dtr1 = (mirek >> 8) & 0xFF; const int dec = addr / 2; return base_.setDTR(dtr) && base_.setDTR1(dtr1) && base_.dtSelect(8) && base_.setDTRAsColourTemp(dec); } bool DaliDT8::setTemporaryPrimaryDimLevel(int a, int n, double level) { int idx = std::clamp(n, 0, 5); double v = std::clamp(level, 0.0, 1.0); int raw = static_cast(std::round(v * 65535.0)); if (raw > 65534) raw = 65534; const int lsb = raw & 0xFF; const int msb = (raw >> 8) & 0xFF; const int addr = a * 2 + 1; return base_.setDTR(lsb) && base_.setDTR1(msb) && base_.setDTR2(idx) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_SET_TEMPORARY_PRIMARY_DIM_LEVEL); } bool DaliDT8::setTemporaryRGBDimLevels(int a, int r, int g, int b) { const int R = std::clamp(r, 0, 255); const int G = std::clamp(g, 0, 255); const int B = std::clamp(b, 0, 255); const int addr = a * 2 + 1; return base_.setDTR(R) && base_.setDTR1(G) && base_.setDTR2(B) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_SET_TEMPORARY_RGB_DIM_LEVELS); } bool DaliDT8::setTemporaryWAFDimLevels(int a, int w, int amber, int freecolour) { const int W = std::clamp(w, 0, 255); const int A = std::clamp(amber, 0, 255); const int F = std::clamp(freecolour, 0, 255); const int addr = a * 2 + 1; return base_.setDTR(W) && base_.setDTR1(A) && base_.setDTR2(F) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_SET_TEMPORARY_WAF_DIM_LEVELS); } bool DaliDT8::setTemporaryRGBWAFControl(int a, int control) { const int addr = a * 2 + 1; return base_.setDTR(control & 0xFF) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_SET_TEMPORARY_RGBWAF_CONTROL); } bool DaliDT8::setTemporaryColourMask(int a) { if (setTemporaryRGBDimLevels(a, 255, 255, 255)) return true; if (base_.setDTR(0xFF) && base_.setDTR1(0xFF) && base_.dtSelect(8) && base_.setDTRAsColourTemp(a)) { return true; } const int addr = DaliComm::toCmdAddr(a); return setTemporaryColourXRaw(addr, 0xFFFF) && setTemporaryColourYRaw(addr, 0xFFFF); } bool DaliDT8::copyReportToTemporary(int a) { return base_.dtSelect(8) && base_.copyReportColourToTemp(a); } bool DaliDT8::stepXUp(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_UP_X_COORDINATE); } bool DaliDT8::stepXDown(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_DOWN_X_COORDINATE); } bool DaliDT8::stepYUp(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_UP_Y_COORDINATE); } bool DaliDT8::stepYDown(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_DOWN_Y_COORDINATE); } bool DaliDT8::stepTcCooler(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_UP_COLOR_TEMPERATURE); } bool DaliDT8::stepTcWarmer(int a) { return base_.dtSelect(8) && base_.sendExtCmd(a * 2 + 1, DALI_CMD_DT8_STEP_DOWN_COLOR_TEMPERATURE); } bool DaliDT8::setColourRGBRaw(int addr, int r, int g, int b) { const int a = addr / 2; return base_.setDTR(r) && base_.setDTR1(g) && base_.setDTR2(b) && base_.dtSelect(8) && base_.setDTRAsColourRGB(a) && base_.dtSelect(8) && base_.activate(a); } bool DaliDT8::setColour(int a, double x, double y) { double xClamped = std::clamp(x, 0.0, 1.0); double yClamped = std::clamp(y, 0.0, 1.0); const int x1 = static_cast(std::round(xClamped * 65535.0)); const int y1 = static_cast(std::round(yClamped * 65535.0)); const int addr = a * 2 + 1; return setColourRaw(addr, x1, y1); } std::optional DaliDT8::getColourRaw(int a, int type) { const uint8_t code = static_cast(type & 0xFF); const auto features = getColorTypeFeature(a); if (!features.has_value()) return std::nullopt; if ((code == 2 || code == 128 || code == 129 || code == 130 || code == 131) && !features->ctCapable()) { return std::nullopt; } bool is8bit = false; if (code == 82 || (code >= 9 && code <= 15) || (code >= 201 && code <= 207) || code == 208 || (code >= 233 && code <= 239) || code == 240) { is8bit = true; } if (!base_.setDTR(code)) return std::nullopt; if (!base_.dtSelect(8)) return std::nullopt; if (!base_.queryColourValue(a)) return std::nullopt; const auto dtr = base_.getDTR(a); const auto dtr1 = base_.getDTR1(a); if (!dtr.has_value() || !dtr1.has_value()) return std::nullopt; if (is8bit) { if (dtr.value() == 0xFF) return std::nullopt; return dtr.value() & 0xFF; } if (dtr.value() == 0xFF && dtr1.value() == 0xFF) return std::nullopt; return ((dtr1.value() & 0xFF) << 8) | (dtr.value() & 0xFF); } std::vector DaliDT8::getColour(int a) { const auto x = getColourRaw(a, 0); const auto y = getColourRaw(a, 1); if (!x.has_value() || !y.has_value()) return {}; return {x.value() / 65535.0, y.value() / 65535.0}; } bool DaliDT8::setColourRGB(int addr, int r, int g, int b) { int R = std::clamp(r, 0, 255); int G = std::clamp(g, 0, 255); int B = std::clamp(b, 0, 255); const auto xy = DaliColor::rgb2xy(static_cast(R) / 255.0, static_cast(G) / 255.0, static_cast(B) / 255.0); return setColour(addr, xy[0], xy[1]); } std::vector DaliDT8::getColourRGB(int a) { const auto xy = getColour(a); if (xy.empty()) return {}; const auto rgb = DaliColor::xy2rgb(xy[0], xy[1]); return {rgb[0], rgb[1], rgb[2]}; } bool DaliDT8::storeSceneSnapshot(int address, int scene, int brightness, Dt8SceneStoreColorMode colorMode, int colorTemperature, int red, int green, int blue, int /*gateway*/) { const int sceneBrightness = std::clamp(brightness, 0, 255); const int sceneIndex = std::clamp(scene, 0, 15); if (sceneBrightness != 255) { switch (colorMode) { case Dt8SceneStoreColorMode::colorTemperature: if (!setTemporaryColourTemperature(address, colorTemperature)) return false; break; case Dt8SceneStoreColorMode::rgb: { const int r = std::clamp(red, 0, 255); const int g = std::clamp(green, 0, 255); const int b = std::clamp(blue, 0, 255); const auto xy = DaliColor::rgb2xy(static_cast(r) / 255.0, static_cast(g) / 255.0, static_cast(b) / 255.0); if (!setTemporaryColourXY(address, xy[0], xy[1])) return false; break; } case Dt8SceneStoreColorMode::disabled: if (!setTemporaryColourMask(address)) return false; break; } } if (!base_.setDTR(sceneBrightness)) return false; // DT SELECT is one-shot and SET DTR consumes any earlier selection. return base_.dtSelect(8) && base_.storeDTRAsSceneBright(address, sceneIndex); } bool DaliDT8::storePowerOnLevelSnapshot(int address, int level) { const int storedLevel = std::clamp(level, 0, 254); // DT SELECT is one-shot and SET DTR consumes any earlier selection. return base_.setDTR(storedLevel) && base_.dtSelect(8) && base_.storeDTRAsPoweredBright(address); } bool DaliDT8::storeSystemFailureLevelSnapshot(int address, int level) { const int storedLevel = std::clamp(level, 0, 254); // DT SELECT is one-shot and SET DTR consumes any earlier selection. return base_.setDTR(storedLevel) && base_.dtSelect(8) && base_.storeDTRAsSystemFailureLevel(address); } bool DaliDT8::activateTemporaryColour(int a) { return base_.dtSelect(8) && base_.activate(a); } std::optional DaliDT8::getPrimaryDimLevel(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 3 + n); } std::optional DaliDT8::getRedDimLevel(int a) { return getColourRaw(a, 9); } std::optional DaliDT8::getGreenDimLevel(int a) { return getColourRaw(a, 10); } std::optional DaliDT8::getBlueDimLevel(int a) { return getColourRaw(a, 11); } std::optional DaliDT8::getWhiteDimLevel(int a) { return getColourRaw(a, 12); } std::optional DaliDT8::getAmberDimLevel(int a) { return getColourRaw(a, 13); } std::optional DaliDT8::getFreecolourDimLevel(int a) { return getColourRaw(a, 14); } std::optional DaliDT8::getRGBWAFControl(int a) { return getColourRaw(a, 15); } std::optional DaliDT8::getTemporaryXRaw(int a) { return getColourRaw(a, 192); } std::optional DaliDT8::getTemporaryYRaw(int a) { return getColourRaw(a, 193); } std::optional DaliDT8::getTemporaryColourTemperatureRaw(int a) { return getColourRaw(a, 194); } std::optional DaliDT8::getTemporaryPrimaryDimLevel(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 195 + n); } std::optional DaliDT8::getTemporaryRedDimLevel(int a) { return getColourRaw(a, 201); } std::optional DaliDT8::getTemporaryGreenDimLevel(int a) { return getColourRaw(a, 202); } std::optional DaliDT8::getTemporaryBlueDimLevel(int a) { return getColourRaw(a, 203); } std::optional DaliDT8::getTemporaryWhiteDimLevel(int a) { return getColourRaw(a, 204); } std::optional DaliDT8::getTemporaryAmberDimLevel(int a) { return getColourRaw(a, 205); } std::optional DaliDT8::getTemporaryFreecolourDimLevel(int a) { return getColourRaw(a, 206); } std::optional DaliDT8::getTemporaryRGBWAFControl(int a) { return getColourRaw(a, 207); } std::optional DaliDT8::getTemporaryColourType(int a) { return getColourRaw(a, 208); } std::vector DaliDT8::getTemporaryColour(int a) { const auto x = getTemporaryXRaw(a); const auto y = getTemporaryYRaw(a); if (!x.has_value() || !y.has_value()) return {}; return {x.value() / 65535.0, y.value() / 65535.0}; } std::optional DaliDT8::getTemporaryColorTemperature(int a) { const auto mirek = getTemporaryColourTemperatureRaw(a); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getReportXRaw(int a) { return getColourRaw(a, 224); } std::optional DaliDT8::getReportYRaw(int a) { return getColourRaw(a, 225); } std::optional DaliDT8::getReportColourTemperatureRaw(int a) { return getColourRaw(a, 226); } std::optional DaliDT8::getReportPrimaryDimLevel(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 227 + n); } std::optional DaliDT8::getReportRedDimLevel(int a) { return getColourRaw(a, 233); } std::optional DaliDT8::getReportGreenDimLevel(int a) { return getColourRaw(a, 234); } std::optional DaliDT8::getReportBlueDimLevel(int a) { return getColourRaw(a, 235); } std::optional DaliDT8::getReportWhiteDimLevel(int a) { return getColourRaw(a, 236); } std::optional DaliDT8::getReportAmberDimLevel(int a) { return getColourRaw(a, 237); } std::optional DaliDT8::getReportFreecolourDimLevel(int a) { return getColourRaw(a, 238); } std::optional DaliDT8::getReportRGBWAFControl(int a) { return getColourRaw(a, 239); } std::optional DaliDT8::getReportColourType(int a) { return getColourRaw(a, 240); } std::vector DaliDT8::getReportColour(int a) { const auto x = getReportXRaw(a); const auto y = getReportYRaw(a); if (!x.has_value() || !y.has_value()) return {}; return {x.value() / 65535.0, y.value() / 65535.0}; } std::optional DaliDT8::getReportColorTemperature(int a) { const auto mirek = getReportColourTemperatureRaw(a); if (!mirek.has_value() || mirek.value() == 0) return std::nullopt; return static_cast(std::floor(1000000.0 / static_cast(mirek.value()))); } std::optional DaliDT8::getNumberOfPrimaries(int a) { return getColourRaw(a, 82); } std::optional DaliDT8::getPrimaryXRaw(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 64 + 3 * n); } std::optional DaliDT8::getPrimaryYRaw(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 65 + 3 * n); } std::optional DaliDT8::getPrimaryTy(int a, int n) { if (n < 0 || n > 5) return std::nullopt; return getColourRaw(a, 66 + 3 * n); } std::optional DaliDT8::getSceneColorReport(int a, int sense, int gateway) { const auto brightness = base_.getScene(a, sense, gateway); if (!brightness.has_value() || brightness.value() == 255) return std::nullopt; const int colorTypeValue = getReportColourType(a).value_or(0); const ColorType colorType(colorTypeValue); SceneColorReport report; report.brightness = brightness.value(); report.colorTypeValue = colorTypeValue; if (colorType.xy()) { report.xy = getReportColour(a); return report; } if (colorType.ct()) { report.colorTemperature = getReportColorTemperature(a); return report; } return report; } std::vector DaliDT8::getSceneColor(int a, int sense, int gateway) { const auto report = getSceneColorReport(a, sense, gateway); if (!report.has_value() || !report->colorType().xy() || !report->hasXy()) return {}; return report->xy; } std::optional DaliDT8::buildLevelColorReport(int a, std::optional level) { if (!level.has_value() || level.value() == 255) return std::nullopt; Dt8LevelColorReport report; report.level = level.value(); report.colorTypeValue = getReportColourType(a).value_or(0); const ColorType colorType(report.colorTypeValue); if (colorType.xy()) { report.xy = getReportColour(a); return report; } if (colorType.ct()) { report.colorTemperature = getReportColorTemperature(a); return report; } return report; } std::optional DaliDT8::getPowerOnLevelColorReport(int a) { return buildLevelColorReport(a, base_.getPowerOnLevel(a)); } std::optional DaliDT8::getSystemFailureLevelColorReport(int a) { return buildLevelColorReport(a, base_.getSystemFailureLevel(a)); } bool DaliDT8::storePrimaryTy(int a, int n, int ty) { int idx = std::clamp(n, 0, 5); int t = std::clamp(ty, 0, 65535); const int lsb = t & 0xFF; const int msb = (t >> 8) & 0xFF; const int addr = a * 2 + 1; return base_.setDTR(lsb) && base_.setDTR1(msb) && base_.setDTR2(idx) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_STORE_PRIMARY_N_TY); } bool DaliDT8::storePrimaryXY(int a, int n, double x, double y) { int idx = std::clamp(n, 0, 5); if (!setTemporaryColourXY(a, x, y)) return false; const int addr = a * 2 + 1; return base_.setDTR2(idx) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_STORE_PRIMARY_N_XY); } bool DaliDT8::storeColourTempLimitRaw(int a, int limitType, int mirek) { int m = mirek; if (m < 1) m = 1; if (m > 65534) m = 65534; const int lsb = m & 0xFF; const int msb = (m >> 8) & 0xFF; const int addr = a * 2 + 1; return base_.setDTR(lsb) && base_.setDTR1(msb) && base_.setDTR2(limitType & 0xFF) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_STORE_COLOR_TEMPERATURE_LIMIT); } bool DaliDT8::storeColourTempLimit(int a, int limitType, int kelvin) { const int mirek = static_cast(std::floor(1000000.0 / static_cast(kelvin <= 0 ? 1 : kelvin))); return storeColourTempLimitRaw(a, limitType, mirek); } bool DaliDT8::setGearAutoActivate(int a, bool enable) { const int addr = a * 2 + 1; const int opts = enable ? 0x01 : 0x00; return base_.setDTR(opts) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_SET_GEAR_FEATURES); } bool DaliDT8::assignColourToLinkedChannels(int a, int colourId) { const int addr = a * 2 + 1; const int id = std::clamp(colourId, 0, 6); return base_.setDTR(id) && base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_ASSIGN_COLOR_TO_LINKED_CHANNEL); } bool DaliDT8::startAutoCalibration(int a) { const int addr = a * 2 + 1; return base_.dtSelect(8) && base_.sendExtCmd(addr, DALI_CMD_DT8_START_AUTO_CALIBRATION); } std::optional DaliDT8::getGearFeaturesStatus(int a) { const int addr = a * 2 + 1; base_.dtSelect(8); return base_.queryCmd(addr, DALI_CMD_DT8_QUERY_GEAR_FEATURES_STATUS); } std::optional DaliDT8::getRGBWAFControlDirect(int a) { const int addr = a * 2 + 1; base_.dtSelect(8); return base_.queryCmd(addr, DALI_CMD_DT8_QUERY_RGBWAF_CONTROL); } std::optional DaliDT8::getAssignedColourForChannel(int a, int channelId) { const int addr = a * 2 + 1; const int ch = channelId & 0xFF; if (!base_.setDTR(ch)) return std::nullopt; if (!base_.dtSelect(8)) return std::nullopt; return base_.queryCmd(addr, DALI_CMD_DT8_QUERY_ASSIGNED_COLOR); } std::optional DaliDT8::getExtendedVersion(int a) { const int addr = a * 2 + 1; base_.dtSelect(8); return base_.queryCmd(addr, DALI_CMD_DT8_QUERY_EXTENDED_VERSION); }