diff --git a/include/dali_comm.hpp b/include/dali_comm.hpp index 8b7c256..e1c5274 100644 --- a/include/dali_comm.hpp +++ b/include/dali_comm.hpp @@ -9,7 +9,7 @@ // Lightweight communicator for DALI gateway type 1 (USB UART new frame format). // Frames: // - Send: [0x10, addr, cmd] -// - Ext: [0x11, addr, cmd] (sent twice with a small delay) +// - Ext: [0x11, addr, cmd] (one logical send; gateway/transceiver handles any required repeat timing) // - Query: [0x12, addr, cmd] -> expects [0xFF, data] on success // COMPARE may also return [0xFD, data] when multiple devices reply together // Callers provide UART callbacks; this class only builds frames and parses basic responses. @@ -49,7 +49,7 @@ class DaliComm { // Send standard command frame (0x10). bool sendCmd(uint8_t addr, uint8_t cmd) const; - // Send extended command frame (0x11). + // Send one logical extended command frame (0x11). bool sendExtCmd(uint8_t addr, uint8_t cmd) const; // Send query frame (0x12) and parse single-byte response. Returns nullopt on no/invalid response, // except COMPARE where 0xFD collisions are treated as a positive match. diff --git a/include/dt5.hpp b/include/dt5.hpp index 74445b9..c5ad73d 100644 --- a/include/dt5.hpp +++ b/include/dt5.hpp @@ -73,6 +73,6 @@ class DaliDT5 { bool enable(); static int addrOf(int a); - bool send(int a, int code, bool twice = false); + bool send(int a, int code); std::optional query(int a, int code); }; \ No newline at end of file diff --git a/include/dt6.hpp b/include/dt6.hpp index adf1d49..880be3a 100644 --- a/include/dt6.hpp +++ b/include/dt6.hpp @@ -124,7 +124,7 @@ class DaliDT6 { bool enable(); static int addrOf(int a); - bool send(int a, int code, bool twice = false); + bool send(int a, int code); std::optional query(int a, int code); std::optional queryYesNo(int a, int code); }; \ No newline at end of file diff --git a/include/dt8.hpp b/include/dt8.hpp index cd33d02..376c50e 100644 --- a/include/dt8.hpp +++ b/include/dt8.hpp @@ -25,6 +25,20 @@ class ColorStatus { int status_ = 0; }; +class ColorType { + public: + explicit ColorType(int type) : type_(type) {} + + int raw() const { return type_; } + bool xy() const { return (type_ & 0x10) != 0; } + bool ct() const { return (type_ & 0x20) != 0; } + bool primaryN() const { return (type_ & 0x40) != 0; } + bool rgbWaf() const { return (type_ & 0x80) != 0; } + + private: + int type_ = 0; +}; + class ColorTypeFeature { public: explicit ColorTypeFeature(int features) : features_(features) {} @@ -41,6 +55,17 @@ class ColorTypeFeature { int features_ = 0; }; +struct SceneColorReport { + int brightness = 0; + int colorTypeValue = 0; + std::vector xy; + std::optional colorTemperature; + + ColorType colorType() const { return ColorType(colorTypeValue); } + bool hasXy() const { return xy.size() == 2; } + bool hasColorTemperature() const { return colorTemperature.has_value(); } +}; + class DaliDT8 { public: explicit DaliDT8(DaliBase& base); @@ -135,6 +160,7 @@ class DaliDT8 { std::optional getPrimaryYRaw(int a, int n); std::optional getPrimaryTy(int a, int n); + std::optional getSceneColorReport(int a, int sense); std::vector getSceneColor(int a, int sense); // Store / config diff --git a/src/base.cpp b/src/base.cpp index fb25091..6cfcbf0 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -266,19 +266,21 @@ std::optional DaliBase::getGradualChangeSpeed(int a) { bool DaliBase::setPowerOnLevel(int a, int value) { return setDTR(value) && storeDTRAsPoweredBright(a); } -std::optional DaliBase::getPowerOnLevel(int a) { return query(a, DALI_CMD_QUERY_MAX_LEVEL); } +std::optional DaliBase::getPowerOnLevel(int a) { return query(a, DALI_CMD_QUERY_POWER_ON_LEVEL); } bool DaliBase::setSystemFailureLevel(int a, int value) { return setDTR(value) && storeDTRAsSystemFailureLevel(a); } -std::optional DaliBase::getSystemFailureLevel(int a) { return query(a, DALI_CMD_QUERY_MIN_LEVEL); } +std::optional DaliBase::getSystemFailureLevel(int a) { + return query(a, DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL); +} bool DaliBase::setMinLevel(int a, int value) { return setDTR(value) && storeDTRAsMinLevel(a); } -std::optional DaliBase::getMinLevel(int a) { return query(a, DALI_CMD_QUERY_POWER_ON_LEVEL); } +std::optional DaliBase::getMinLevel(int a) { return query(a, DALI_CMD_QUERY_MIN_LEVEL); } bool DaliBase::setMaxLevel(int a, int value) { return setDTR(value) && storeDTRAsMaxLevel(a); } -std::optional DaliBase::getMaxLevel(int a) { return query(a, DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL); } +std::optional DaliBase::getMaxLevel(int a) { return query(a, DALI_CMD_QUERY_MAX_LEVEL); } bool DaliBase::setFadeTime(int a, int value) { int v = value; diff --git a/src/dali_comm.cpp b/src/dali_comm.cpp index a703fad..fe8382f 100644 --- a/src/dali_comm.cpp +++ b/src/dali_comm.cpp @@ -201,6 +201,7 @@ bool DaliComm::sendCmd(uint8_t addr, uint8_t cmd) const { return writeFrame(frame); } +// Send extended command frame (0x11). Gateway will send twice for 0x11, but we only need to send once. bool DaliComm::sendExtCmd(uint8_t addr, uint8_t cmd) const { const std::vector frame{0x11, addr, cmd}; const bool ret = writeFrame(frame); diff --git a/src/dt4.cpp b/src/dt4.cpp index ad6745a..565c978 100644 --- a/src/dt4.cpp +++ b/src/dt4.cpp @@ -3,6 +3,7 @@ #include "dali_define.hpp" #include +#include DaliDT4::DaliDT4(DaliBase& base) : base_(base) {} @@ -24,14 +25,12 @@ std::optional DaliDT4::queryYesNo(int a, int code) { bool DaliDT4::enableDT4() { return enable(); } bool DaliDT4::referenceSystemPower(int a) { - return enable() && base_.sendExtCmd(addrOf(a), DALI_CMD_DT4_REFERENCE_SYSTEM_POWER) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT4_REFERENCE_SYSTEM_POWER); + return enable() && base_.sendExtCmd(addrOf(a), DALI_CMD_DT4_REFERENCE_SYSTEM_POWER); } bool DaliDT4::selectDimmingCurve(int a, int curve) { const int value = std::clamp(curve, 0, 255); - return enable() && base_.setDTR(value) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT4_SELECT_DIMMING_CURVE) && + return base_.setDTR(value) && enable() && base_.sendExtCmd(addrOf(a), DALI_CMD_DT4_SELECT_DIMMING_CURVE); } diff --git a/src/dt5.cpp b/src/dt5.cpp index 74b689e..ce32171 100644 --- a/src/dt5.cpp +++ b/src/dt5.cpp @@ -3,6 +3,7 @@ #include "dali_define.hpp" #include +#include DaliDT5::DaliDT5(DaliBase& base) : base_(base) {} @@ -10,10 +11,9 @@ bool DaliDT5::enable() { return base_.dtSelect(5); } int DaliDT5::addrOf(int a) { return a * 2 + 1; } -bool DaliDT5::send(int a, int code, bool twice) { +bool DaliDT5::send(int a, int code) { if (!enable()) return false; if (!base_.sendExtCmd(addrOf(a), code)) return false; - if (twice && !base_.sendExtCmd(addrOf(a), code)) return false; return true; } @@ -25,37 +25,35 @@ std::optional DaliDT5::query(int a, int code) { bool DaliDT5::enableDT5() { return enable(); } bool DaliDT5::setOutputRange1To10V(int a) { - return send(a, DALI_CMD_DT5_SET_OUTPUT_RANGE_1_TO_10V, true); + return send(a, DALI_CMD_DT5_SET_OUTPUT_RANGE_1_TO_10V); } bool DaliDT5::setOutputRange0To10V(int a) { - return send(a, DALI_CMD_DT5_SET_OUTPUT_RANGE_0_TO_10V, true); + return send(a, DALI_CMD_DT5_SET_OUTPUT_RANGE_0_TO_10V); } bool DaliDT5::switchOnInternalPullUp(int a) { - return send(a, DALI_CMD_DT5_SWITCH_ON_INTERNAL_PULL_UP, true); + return send(a, DALI_CMD_DT5_SWITCH_ON_INTERNAL_PULL_UP); } bool DaliDT5::switchOffInternalPullUp(int a) { - return send(a, DALI_CMD_DT5_SWITCH_OFF_INTERNAL_PULL_UP, true); + return send(a, DALI_CMD_DT5_SWITCH_OFF_INTERNAL_PULL_UP); } bool DaliDT5::storePhysicalMinimum(int a, int level) { const int value = std::clamp(level, 0, 254); return enable() && base_.setDTR(value) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_STORE_DTR_AS_PHYSICAL_MINIMUM) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_STORE_DTR_AS_PHYSICAL_MINIMUM); + base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_STORE_DTR_AS_PHYSICAL_MINIMUM); } bool DaliDT5::selectDimmingCurve(int a, int curve) { const int value = std::clamp(curve, 0, 255); - return enable() && base_.setDTR(value) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_SELECT_DIMMING_CURVE) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_SELECT_DIMMING_CURVE); + return base_.setDTR(value) && enable() && + base_.sendExtCmd(addrOf(a), DALI_CMD_DT5_SELECT_DIMMING_CURVE); } bool DaliDT5::resetConverterSettings(int a) { - return send(a, DALI_CMD_DT5_RESET_CONVERTER_SETTINGS, true); + return send(a, DALI_CMD_DT5_RESET_CONVERTER_SETTINGS); } std::optional DaliDT5::getDimmingCurve(int a) { return query(a, DALI_CMD_DT5_QUERY_DIMMING_CURVE); } diff --git a/src/dt6.cpp b/src/dt6.cpp index df1f527..ac881ca 100644 --- a/src/dt6.cpp +++ b/src/dt6.cpp @@ -10,10 +10,9 @@ bool DaliDT6::enable() { return base_.dtSelect(6); } int DaliDT6::addrOf(int a) { return a * 2 + 1; } -bool DaliDT6::send(int a, int code, bool twice) { +bool DaliDT6::send(int a, int code) { if (!enable()) return false; if (!base_.sendExtCmd(addrOf(a), code)) return false; - if (twice && !base_.sendExtCmd(addrOf(a), code)) return false; return true; } @@ -31,29 +30,27 @@ std::optional DaliDT6::queryYesNo(int a, int code) { bool DaliDT6::enableDT6() { return enable(); } bool DaliDT6::referenceSystemPower(int a) { - return send(a, DALI_CMD_DT6_REFERENCE_SYSTEM_POWER, true); + return send(a, DALI_CMD_DT6_REFERENCE_SYSTEM_POWER); } bool DaliDT6::enableCurrentProtector(int a) { - return send(a, DALI_CMD_DT6_ENABLE_CURRENT_PROTECTOR, true); + return send(a, DALI_CMD_DT6_ENABLE_CURRENT_PROTECTOR); } bool DaliDT6::disableCurrentProtector(int a) { - return send(a, DALI_CMD_DT6_DISABLE_CURRENT_PROTECTOR, true); + return send(a, DALI_CMD_DT6_DISABLE_CURRENT_PROTECTOR); } bool DaliDT6::selectDimmingCurve(int a, int curve) { const int value = std::clamp(curve, 0, 255); - return enable() && base_.setDTR(value) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_SELECT_DIMMING_CURVE) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_SELECT_DIMMING_CURVE); + return base_.setDTR(value) && enable() && + base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_SELECT_DIMMING_CURVE); } bool DaliDT6::storeFastFadeTime(int a, int value) { const int clamped = std::clamp(value, 0, 27); return enable() && base_.setDTR(clamped) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_STORE_DTR_AS_FAST_FADE_TIME) && - base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_STORE_DTR_AS_FAST_FADE_TIME); + base_.sendExtCmd(addrOf(a), DALI_CMD_DT6_STORE_DTR_AS_FAST_FADE_TIME); } std::optional DaliDT6::getGearType(int a) { diff --git a/src/dt8.cpp b/src/dt8.cpp index eb4999c..cf03707 100644 --- a/src/dt8.cpp +++ b/src/dt8.cpp @@ -381,11 +381,34 @@ std::optional DaliDT8::getPrimaryTy(int a, int n) { return getColourRaw(a, 66 + 3 * n); } +std::optional DaliDT8::getSceneColorReport(int a, int sense) { + const auto brightness = base_.getScene(a, sense); + 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) { - const auto bright = base_.getScene(a, sense); - if (!bright.has_value() || bright.value() == 255) return {}; - base_.copyReportColourToTemp(a); - return getColour(a); + const auto report = getSceneColorReport(a, sense); + if (!report.has_value() || !report->colorType().xy() || !report->hasXy()) return {}; + return report->xy; } bool DaliDT8::storePrimaryTy(int a, int n, int ty) {