diff --git a/include/dt8.hpp b/include/dt8.hpp index d9ab60e..00dc9ed 100644 --- a/include/dt8.hpp +++ b/include/dt8.hpp @@ -96,6 +96,11 @@ enum class Dt8PreferredColorType { rgbwaf, }; +enum class Dt8ColorTemperatureWriteType { + colorTemperature, + rgbwaf, +}; + enum class Dt8RgbwafControlMode { channelControl = 0, colorControl = 1, @@ -162,9 +167,11 @@ class DaliDT8 { bool setColourRGBPreferred(int addr, int r, int g, int b, Dt8PreferredColorType preferredColorType = Dt8PreferredColorType::xy); bool setTemporaryColourRGBWAPreferred(int addr, int r, int g, int b, int w, int amber, - Dt8PreferredColorType preferredColorType = Dt8PreferredColorType::rgbwaf); + Dt8PreferredColorType preferredColorType = Dt8PreferredColorType::rgbwaf, + int freecolour = 254); bool setColourRGBWAPreferred(int addr, int r, int g, int b, int w, int amber, - Dt8PreferredColorType preferredColorType = Dt8PreferredColorType::rgbwaf); + Dt8PreferredColorType preferredColorType = Dt8PreferredColorType::rgbwaf, + int freecolour = 254); 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, @@ -255,6 +262,7 @@ class DaliDT8 { private: Dt8PreferredColorType resolvePreferredColorType(int address, Dt8PreferredColorType preferredColorType); + Dt8ColorTemperatureWriteType resolveColorTemperatureWriteType(int address); std::optional buildLevelColorReport(int a, std::optional level); std::vector getReportRGBWAF(int a); diff --git a/src/dt8.cpp b/src/dt8.cpp index 39fcff5..4cb4f71 100644 --- a/src/dt8.cpp +++ b/src/dt8.cpp @@ -11,6 +11,8 @@ constexpr int kRgbWControl = 0x0F; constexpr int kRgbWaControl = 0x1F; constexpr int kRgbCwControl = 0x1F; constexpr int kRgbWafControl = 0x3F; +constexpr int kCtFallbackWarmKelvin = 2700; +constexpr int kCtFallbackCoolKelvin = 6500; int colourTempLimitQuerySelector(int limitType) { switch (limitType & 0xFF) { @@ -31,6 +33,15 @@ 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()))); } + +std::vector rgbwafForColorTemperature(int kelvin) { + const int clamped = std::clamp(kelvin, kCtFallbackWarmKelvin, kCtFallbackCoolKelvin); + const double ratio = static_cast(clamped - kCtFallbackWarmKelvin) / + static_cast(kCtFallbackCoolKelvin - kCtFallbackWarmKelvin); + const int coolWhite = std::clamp(static_cast(std::round(ratio * 254.0)), 0, 254); + const int warmWhite = std::clamp(254 - coolWhite, 0, 254); + return {0, 0, 0, coolWhite, warmWhite, 0}; +} } // namespace DaliDT8::DaliDT8(DaliBase& base) : base_(base) {} @@ -61,6 +72,22 @@ Dt8PreferredColorType DaliDT8::resolvePreferredColorType( return preferredColorType; } +Dt8ColorTemperatureWriteType DaliDT8::resolveColorTemperatureWriteType(int address) { + if (address < 0 || address > 63) return Dt8ColorTemperatureWriteType::colorTemperature; + const auto discovery = base_.discoverDeviceTypes(address); + if (!discovery.has_value()) return Dt8ColorTemperatureWriteType::colorTemperature; + if (std::find(discovery->types.begin(), discovery->types.end(), 8) == + discovery->types.end()) { + return Dt8ColorTemperatureWriteType::colorTemperature; + } + + const auto features = getColorTypeFeature(address); + if (!features.has_value()) return Dt8ColorTemperatureWriteType::colorTemperature; + if (features->ctCapable()) return Dt8ColorTemperatureWriteType::colorTemperature; + if (features->rgbwafCapable()) return Dt8ColorTemperatureWriteType::rgbwaf; + return Dt8ColorTemperatureWriteType::colorTemperature; +} + bool DaliDT8::enableDT8() { return base_.dtSelect(8); } std::optional DaliDT8::getColorTypeFeature(int a) { @@ -122,9 +149,20 @@ bool DaliDT8::setColTempRaw(int a, int value) { } 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); + const int k = kelvin <= 0 ? 1 : kelvin; + switch (resolveColorTemperatureWriteType(addr)) { + case Dt8ColorTemperatureWriteType::colorTemperature: { + const int mirek = static_cast(std::floor(1000000.0 / static_cast(k))); + return setColTempRaw(addr, mirek); + } + case Dt8ColorTemperatureWriteType::rgbwaf: { + const auto rgbwaf = rgbwafForColorTemperature(k); + return setTemporaryRGBWAFDimLevels(addr, rgbwaf[0], rgbwaf[1], rgbwaf[2], rgbwaf[3], + rgbwaf[4], rgbwaf[5], kRgbWafControl) && + activateTemporaryColour(addr); + } + } + return false; } std::optional DaliDT8::getColorTemperature(int a) { @@ -210,12 +248,23 @@ bool DaliDT8::setTemporaryColourXY(int a, double x, double y) { 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); + switch (resolveColorTemperatureWriteType(a)) { + case Dt8ColorTemperatureWriteType::colorTemperature: { + 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); + } + case Dt8ColorTemperatureWriteType::rgbwaf: { + const auto rgbwaf = rgbwafForColorTemperature(k); + return setTemporaryRGBWAFDimLevels(a, rgbwaf[0], rgbwaf[1], rgbwaf[2], rgbwaf[3], + rgbwaf[4], rgbwaf[5], kRgbWafControl); + } + } + return false; } bool DaliDT8::setTemporaryPrimaryDimLevel(int a, int n, double level) { @@ -419,17 +468,18 @@ bool DaliDT8::setColourRGBPreferred(int addr, int r, int g, int b, } bool DaliDT8::setTemporaryColourRGBWAPreferred(int addr, int r, int g, int b, int w, int amber, - Dt8PreferredColorType preferredColorType) { + Dt8PreferredColorType preferredColorType, int freecolour) { 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 W = std::clamp(w, 0, 255); const int A = std::clamp(amber, 0, 255); + const int F = std::clamp(freecolour, 0, 255); const auto resolved = resolvePreferredColorType(addr, preferredColorType); switch (resolved) { case Dt8PreferredColorType::rgbwaf: - return setTemporaryRGBWAFDimLevels(addr, R, G, B, W, A, 254, kRgbWaControl); + return setTemporaryRGBWAFDimLevels(addr, R, G, B, W, A, F, kRgbWaControl); case Dt8PreferredColorType::xy: { const auto xy = DaliColor::rgb2xy(static_cast(R) / 255.0, static_cast(G) / 255.0, @@ -441,8 +491,8 @@ bool DaliDT8::setTemporaryColourRGBWAPreferred(int addr, int r, int g, int b, in } bool DaliDT8::setColourRGBWAPreferred(int addr, int r, int g, int b, int w, int amber, - Dt8PreferredColorType preferredColorType) { - return setTemporaryColourRGBWAPreferred(addr, r, g, b, w, amber, preferredColorType) && + Dt8PreferredColorType preferredColorType, int freecolour) { + return setTemporaryColourRGBWAPreferred(addr, r, g, b, w, amber, preferredColorType, freecolour) && activateTemporaryColour(addr); }