From d1cc27ae2a1d63ad7913a65ff417f66326947cb6 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 1 Jun 2026 03:06:25 +0800 Subject: [PATCH] Enhance DaliDT8 and Bridge: add support for RGBW, RGBCW, and RGBWAF operations, including temporary dim levels and control handling Signed-off-by: Tony --- include/bridge_model.hpp | 4 +++ include/dt8.hpp | 17 ++++++++++ src/bridge.cpp | 70 +++++++++++++++++++++++++++++++++++++++- src/bridge_model.cpp | 12 +++++++ src/dt8.cpp | 65 ++++++++++++++++++++++++++++++++++++- 5 files changed, 166 insertions(+), 2 deletions(-) diff --git a/include/bridge_model.hpp b/include/bridge_model.hpp index 893a775..2ea412d 100644 --- a/include/bridge_model.hpp +++ b/include/bridge_model.hpp @@ -117,6 +117,10 @@ enum class BridgeOperation { dt1GetRatedDuration = 77, dt1GetExtendedVersion = 78, dt1GetEmergencyDeviceType = 79, + setColourRGBW = 80, + setColourRGBCW = 81, + setColourRGBWAF = 82, + setTemporaryRGBWAFDimLevels = 83, }; enum class BridgeValueEncoding { diff --git a/include/dt8.hpp b/include/dt8.hpp index ea2f400..e8c32fb 100644 --- a/include/dt8.hpp +++ b/include/dt8.hpp @@ -60,10 +60,13 @@ struct SceneColorReport { int colorTypeValue = 0; std::vector xy; std::optional colorTemperature; + std::vector rgbwaf; + std::optional rgbwafControl; ColorType colorType() const { return ColorType(colorTypeValue); } bool hasXy() const { return xy.size() == 2; } bool hasColorTemperature() const { return colorTemperature.has_value(); } + bool hasRgbwaf() const { return rgbwaf.size() == 6; } }; struct Dt8LevelColorReport { @@ -71,16 +74,21 @@ struct Dt8LevelColorReport { int colorTypeValue = 0; std::vector xy; std::optional colorTemperature; + std::vector rgbwaf; + std::optional rgbwafControl; ColorType colorType() const { return ColorType(colorTypeValue); } bool hasXy() const { return xy.size() == 2; } bool hasColorTemperature() const { return colorTemperature.has_value(); } + bool hasRgbwaf() const { return rgbwaf.size() == 6; } }; enum class Dt8SceneStoreColorMode { disabled, colorTemperature, rgb, + rgbw, + rgbcw, }; class DaliDT8 { @@ -113,6 +121,8 @@ class DaliDT8 { bool setTemporaryRGBDimLevels(int a, int r, int g, int b); bool setTemporaryWAFDimLevels(int a, int w, int amber, int freecolour); bool setTemporaryRGBWAFControl(int a, int control); + bool setTemporaryRGBWAFDimLevels(int a, int r, int g, int b, int w, int amber, + int freecolour, int control); bool setTemporaryColourMask(int a); bool copyReportToTemporary(int a); @@ -129,6 +139,10 @@ class DaliDT8 { std::optional getColourRaw(int a, int type); std::vector getColour(int a); bool setColourRGB(int addr, int r, int g, int b); + bool setColourRGBW(int addr, int r, int g, int b, int w); + bool setColourRGBCW(int addr, int r, int g, int b, int coolWhite, int warmWhite); + bool setColourRGBWAF(int addr, int r, int g, int b, int w, int amber, int freecolour, + int control = -1); std::vector getColourRGB(int a); bool activateTemporaryColour(int a); @@ -189,6 +203,8 @@ class DaliDT8 { bool storeSceneSnapshot(int address, int scene, int brightness, Dt8SceneStoreColorMode colorMode = Dt8SceneStoreColorMode::disabled, int colorTemperature = 0, int red = 0, int green = 0, int blue = 0, + int white = 0, int amber = 0, int freecolour = 255, + int rgbwafControl = -1, int gateway = -1); bool storePowerOnLevelSnapshot(int address, int level); bool storeSystemFailureLevelSnapshot(int address, int level); @@ -208,6 +224,7 @@ class DaliDT8 { private: std::optional buildLevelColorReport(int a, std::optional level); + std::vector getReportRGBWAF(int a); DaliBase& base_; }; diff --git a/src/bridge.cpp b/src/bridge.cpp index 7a6b90c..6b650ba 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -680,9 +680,13 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ case BridgeOperation::setColorTemperatureRaw: case BridgeOperation::setColourXY: case BridgeOperation::setColourRGB: + case BridgeOperation::setColourRGBW: + case BridgeOperation::setColourRGBCW: + case BridgeOperation::setColourRGBWAF: case BridgeOperation::setTemporaryColourTemperature: case BridgeOperation::setTemporaryColourXY: case BridgeOperation::setTemporaryRGBDimLevels: + case BridgeOperation::setTemporaryRGBWAFDimLevels: case BridgeOperation::setTemporaryColourMask: case BridgeOperation::activateTemporaryColour: case BridgeOperation::copyReportToTemporary: { @@ -724,6 +728,43 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ return result; } result.ok = dt8_.setColourRGB(address.value(), r.value(), g.value(), b.value()); + } else if (operation == BridgeOperation::setColourRGBW) { + const auto r = intParam(request, {"red", "r"}); + const auto g = intParam(request, {"green", "g"}); + const auto b = intParam(request, {"blue", "b"}); + const auto w = intParam(request, {"white", "w"}); + if (!r.has_value() || !g.has_value() || !b.has_value() || !w.has_value()) { + result.error = "missing rgbw"; + return result; + } + result.ok = dt8_.setColourRGBW(address.value(), r.value(), g.value(), b.value(), w.value()); + } else if (operation == BridgeOperation::setColourRGBCW) { + const auto r = intParam(request, {"red", "r"}); + const auto g = intParam(request, {"green", "g"}); + const auto b = intParam(request, {"blue", "b"}); + const auto cw = intParam(request, {"coolWhite", "cool_white", "cw", "white", "w"}); + const auto ww = intParam(request, {"warmWhite", "warm_white", "ww", "amber", "a"}); + if (!r.has_value() || !g.has_value() || !b.has_value() || !cw.has_value() || + !ww.has_value()) { + result.error = "missing rgbcw"; + return result; + } + result.ok = dt8_.setColourRGBCW(address.value(), r.value(), g.value(), b.value(), + cw.value(), ww.value()); + } else if (operation == BridgeOperation::setColourRGBWAF) { + const auto r = intParam(request, {"red", "r"}); + const auto g = intParam(request, {"green", "g"}); + const auto b = intParam(request, {"blue", "b"}); + const auto w = intParam(request, {"white", "w"}); + if (!r.has_value() || !g.has_value() || !b.has_value() || !w.has_value()) { + result.error = "missing rgbwaf"; + return result; + } + result.ok = dt8_.setColourRGBWAF( + address.value(), r.value(), g.value(), b.value(), w.value(), + intParam(request, {"amber", "a"}).value_or(255), + intParam(request, {"freecolour", "freeColor", "f"}).value_or(255), + intParam(request, {"control", "rgbwafControl", "rgbwaf_control"}).value_or(-1)); } else if (operation == BridgeOperation::setTemporaryColourTemperature) { const auto value = intParam(request, {"colorTemperature", "colourTemperature", "kelvin", "value"}); if (!value.has_value()) { @@ -749,6 +790,20 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ return result; } result.ok = dt8_.setTemporaryRGBDimLevels(address.value(), r.value(), g.value(), b.value()); + } else if (operation == BridgeOperation::setTemporaryRGBWAFDimLevels) { + const auto r = intParam(request, {"red", "r"}); + const auto g = intParam(request, {"green", "g"}); + const auto b = intParam(request, {"blue", "b"}); + const auto w = intParam(request, {"white", "w"}); + if (!r.has_value() || !g.has_value() || !b.has_value() || !w.has_value()) { + result.error = "missing rgbwaf"; + return result; + } + result.ok = dt8_.setTemporaryRGBWAFDimLevels( + address.value(), r.value(), g.value(), b.value(), w.value(), + intParam(request, {"amber", "a"}).value_or(255), + intParam(request, {"freecolour", "freeColor", "f"}).value_or(255), + intParam(request, {"control", "rgbwafControl", "rgbwaf_control"}).value_or(0x3F)); } else if (operation == BridgeOperation::setTemporaryColourMask) { result.ok = dt8_.setTemporaryColourMask(address.value()); } else if (operation == BridgeOperation::activateTemporaryColour) { @@ -773,8 +828,17 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ mode = Dt8SceneStoreColorMode::colorTemperature; } else if (modeName == "rgb") { mode = Dt8SceneStoreColorMode::rgb; + } else if (modeName == "rgbw") { + mode = Dt8SceneStoreColorMode::rgbw; + } else if (modeName == "rgbcw" || modeName == "rgb_cw") { + mode = Dt8SceneStoreColorMode::rgbcw; } else if (intParam(request, {"colorTemperature", "colourTemperature"}).has_value()) { mode = Dt8SceneStoreColorMode::colorTemperature; + } else if (intParam(request, {"coolWhite", "cool_white", "cw"}).has_value() || + intParam(request, {"warmWhite", "warm_white", "ww", "amber"}).has_value()) { + mode = Dt8SceneStoreColorMode::rgbcw; + } else if (intParam(request, {"white", "w"}).has_value()) { + mode = Dt8SceneStoreColorMode::rgbw; } else if (intParam(request, {"red", "r"}).has_value()) { mode = Dt8SceneStoreColorMode::rgb; } @@ -782,7 +846,11 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ address.value(), scene.value(), brightness.value(), mode, intParam(request, {"colorTemperature", "colourTemperature"}).value_or(0), intParam(request, {"red", "r"}).value_or(0), intParam(request, {"green", "g"}).value_or(0), - intParam(request, {"blue", "b"}).value_or(0)); + intParam(request, {"blue", "b"}).value_or(0), + intParam(request, {"white", "w", "coolWhite", "cool_white", "cw"}).value_or(0), + intParam(request, {"amber", "a", "warmWhite", "warm_white", "ww"}).value_or(0), + intParam(request, {"freecolour", "freeColor", "f"}).value_or(255), + intParam(request, {"control", "rgbwafControl", "rgbwaf_control"}).value_or(-1)); break; } case BridgeOperation::storeDt8PowerOnLevelSnapshot: diff --git a/src/bridge_model.cpp b/src/bridge_model.cpp index 1597591..ebc144a 100644 --- a/src/bridge_model.cpp +++ b/src/bridge_model.cpp @@ -358,12 +358,20 @@ const char* bridgeOperationToString(BridgeOperation operation) { return "set_colour_xy"; case BridgeOperation::setColourRGB: return "set_colour_rgb"; + case BridgeOperation::setColourRGBW: + return "set_colour_rgbw"; + case BridgeOperation::setColourRGBCW: + return "set_colour_rgbcw"; + case BridgeOperation::setColourRGBWAF: + return "set_colour_rgbwaf"; case BridgeOperation::setTemporaryColourTemperature: return "set_temporary_colour_temperature"; case BridgeOperation::setTemporaryColourXY: return "set_temporary_colour_xy"; case BridgeOperation::setTemporaryRGBDimLevels: return "set_temporary_rgb_dim_levels"; + case BridgeOperation::setTemporaryRGBWAFDimLevels: + return "set_temporary_rgbwaf_dim_levels"; case BridgeOperation::setTemporaryColourMask: return "set_temporary_colour_mask"; case BridgeOperation::activateTemporaryColour: @@ -505,6 +513,9 @@ BridgeOperation bridgeOperationFromString(const std::string& value) { if (normalized == "set_color_temperature_raw") return BridgeOperation::setColorTemperatureRaw; if (normalized == "set_colour_xy" || normalized == "set_color_xy") return BridgeOperation::setColourXY; if (normalized == "set_colour_rgb" || normalized == "set_color_rgb") return BridgeOperation::setColourRGB; + if (normalized == "set_colour_rgbw" || normalized == "set_color_rgbw") return BridgeOperation::setColourRGBW; + if (normalized == "set_colour_rgbcw" || normalized == "set_color_rgbcw") return BridgeOperation::setColourRGBCW; + if (normalized == "set_colour_rgbwaf" || normalized == "set_color_rgbwaf") return BridgeOperation::setColourRGBWAF; if (normalized == "set_temporary_colour_temperature" || normalized == "set_temporary_color_temperature") { return BridgeOperation::setTemporaryColourTemperature; } @@ -512,6 +523,7 @@ BridgeOperation bridgeOperationFromString(const std::string& value) { return BridgeOperation::setTemporaryColourXY; } if (normalized == "set_temporary_rgb_dim_levels") return BridgeOperation::setTemporaryRGBDimLevels; + if (normalized == "set_temporary_rgbwaf_dim_levels") return BridgeOperation::setTemporaryRGBWAFDimLevels; if (normalized == "set_temporary_colour_mask" || normalized == "set_temporary_color_mask") { return BridgeOperation::setTemporaryColourMask; } diff --git a/src/dt8.cpp b/src/dt8.cpp index e56590b..656e61f 100644 --- a/src/dt8.cpp +++ b/src/dt8.cpp @@ -6,6 +6,10 @@ #include namespace { +constexpr int kRgbWControl = 0x0F; +constexpr int kRgbCwControl = 0x1F; +constexpr int kRgbWafControl = 0x3F; + int colourTempLimitQuerySelector(int limitType) { switch (limitType & 0xFF) { case 0: @@ -222,6 +226,12 @@ bool DaliDT8::setTemporaryRGBWAFControl(int a, int control) { base_.sendExtCmd(addr, DALI_CMD_DT8_SET_TEMPORARY_RGBWAF_CONTROL); } +bool DaliDT8::setTemporaryRGBWAFDimLevels(int a, int r, int g, int b, int w, int amber, + int freecolour, int control) { + return setTemporaryRGBDimLevels(a, r, g, b) && setTemporaryWAFDimLevels(a, w, amber, freecolour) && + setTemporaryRGBWAFControl(a, 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)) { @@ -335,6 +345,21 @@ bool DaliDT8::setColourRGB(int addr, int r, int g, int b) { return setColour(addr, xy[0], xy[1]); } +bool DaliDT8::setColourRGBW(int addr, int r, int g, int b, int w) { + return setColourRGBWAF(addr, r, g, b, w, 255, 255, kRgbWControl); +} + +bool DaliDT8::setColourRGBCW(int addr, int r, int g, int b, int coolWhite, int warmWhite) { + return setColourRGBWAF(addr, r, g, b, coolWhite, warmWhite, 255, kRgbCwControl); +} + +bool DaliDT8::setColourRGBWAF(int addr, int r, int g, int b, int w, int amber, int freecolour, + int control) { + const int rgbwafControl = control < 0 ? kRgbWafControl : control; + return setTemporaryRGBWAFDimLevels(addr, r, g, b, w, amber, freecolour, rgbwafControl) && + activateTemporaryColour(addr); +} + std::vector DaliDT8::getColourRGB(int a) { const auto xy = getColour(a); if (xy.empty()) return {}; @@ -344,7 +369,8 @@ std::vector DaliDT8::getColourRGB(int a) { bool DaliDT8::storeSceneSnapshot(int address, int scene, int brightness, Dt8SceneStoreColorMode colorMode, int colorTemperature, int red, - int green, int blue, int /*gateway*/) { + int green, int blue, int white, int amber, int freecolour, + int rgbwafControl, int /*gateway*/) { const int sceneBrightness = std::clamp(brightness, 0, 255); const int sceneIndex = std::clamp(scene, 0, 15); @@ -363,6 +389,16 @@ bool DaliDT8::storeSceneSnapshot(int address, int scene, int brightness, if (!setTemporaryColourXY(address, xy[0], xy[1])) return false; break; } + case Dt8SceneStoreColorMode::rgbw: { + const int control = rgbwafControl < 0 ? kRgbWControl : rgbwafControl; + if (!setTemporaryRGBWAFDimLevels(address, red, green, blue, white, 255, 255, control)) return false; + break; + } + case Dt8SceneStoreColorMode::rgbcw: { + const int control = rgbwafControl < 0 ? kRgbCwControl : rgbwafControl; + if (!setTemporaryRGBWAFDimLevels(address, red, green, blue, white, amber, freecolour, control)) return false; + break; + } case Dt8SceneStoreColorMode::disabled: if (!setTemporaryColourMask(address)) return false; break; @@ -451,6 +487,21 @@ std::optional DaliDT8::getReportFreecolourDimLevel(int a) { return getColou std::optional DaliDT8::getReportRGBWAFControl(int a) { return getColourRaw(a, 239); } std::optional DaliDT8::getReportColourType(int a) { return getColourRaw(a, 240); } +std::vector DaliDT8::getReportRGBWAF(int a) { + const auto red = getReportRedDimLevel(a); + const auto green = getReportGreenDimLevel(a); + const auto blue = getReportBlueDimLevel(a); + const auto white = getReportWhiteDimLevel(a); + const auto amber = getReportAmberDimLevel(a); + const auto freecolour = getReportFreecolourDimLevel(a); + if (!red.has_value() && !green.has_value() && !blue.has_value() && !white.has_value() && + !amber.has_value() && !freecolour.has_value()) { + return {}; + } + return {red.value_or(255), green.value_or(255), blue.value_or(255), + white.value_or(255), amber.value_or(255), freecolour.value_or(255)}; +} + std::vector DaliDT8::getReportColour(int a) { const auto x = getReportXRaw(a); const auto y = getReportYRaw(a); @@ -502,6 +553,12 @@ std::optional DaliDT8::getSceneColorReport(int a, int sense, i return report; } + if (colorType.rgbWaf()) { + report.rgbwaf = getReportRGBWAF(a); + report.rgbwafControl = getReportRGBWAFControl(a); + return report; + } + return report; } @@ -529,6 +586,12 @@ std::optional DaliDT8::buildLevelColorReport(int a, std::op return report; } + if (colorType.rgbWaf()) { + report.rgbwaf = getReportRGBWAF(a); + report.rgbwafControl = getReportRGBWAFControl(a); + return report; + } + return report; }