#include "dali_gateway_bridge.h" #include "dali_define.hpp" #include "dali_comm.hpp" #include #include namespace gateway { namespace knx_dali_gw { namespace { constexpr double kArcMax = 254.0; constexpr double kLogFactor = 3.0; // DALI address encoding helpers (mirroring lib/dali/comm.dart). // Short address 0-63 → (addr << 1) | 0x01 for commands. constexpr uint8_t kShortAddrCmdBase = 0x01; constexpr uint8_t kShortAddrArcBase = 0x00; constexpr uint8_t kGroupAddrBase = 0x80; constexpr uint8_t kBroadcastCmd = 0xFE; constexpr uint8_t kBroadcastArc = 0xFF; uint8_t makeShortCmdAddr(int addr) { return static_cast((addr << 1) | kShortAddrCmdBase); } uint8_t makeShortArcAddr(int addr) { return static_cast((addr << 1) | kShortAddrArcBase); } uint8_t makeGroupCmdAddr(int group) { return static_cast(kGroupAddrBase | (group << 1) | 0x01); } uint8_t makeBroadcastCmdAddr() { return kBroadcastCmd; } uint8_t makeBroadcastArcAddr() { return kBroadcastArc; } bool isBroadcastAddr(uint8_t raw) { return raw == kBroadcastCmd || raw == kBroadcastArc; } bool isGroupAddr(uint8_t raw) { return (raw & 0x80) != 0 && !isBroadcastAddr(raw); } int extractGroupAddr(uint8_t raw) { return (raw >> 1) & 0x0F; } int extractShortAddr(uint8_t raw) { return (raw >> 1) & 0x3F; } uint8_t encodeDaliRawAddr(DaliTarget target) { switch (target.kind) { case DaliTargetKind::kShortAddress: return makeShortCmdAddr(target.address); case DaliTargetKind::kGroup: return makeGroupCmdAddr(target.address); case DaliTargetKind::kBroadcast: default: return makeBroadcastCmdAddr(); } } } // namespace // ============================================================================= // DaliTarget ↔ raw address encoding (public API) // ============================================================================= uint8_t EncodeDaliRawAddr(DaliTarget target) { return encodeDaliRawAddr(target); } DaliTarget DecodeDaliRawAddr(uint8_t raw_addr, int default_short_address) { if (isBroadcastAddr(raw_addr)) { return {DaliTargetKind::kBroadcast, 0}; } if (isGroupAddr(raw_addr)) { return {DaliTargetKind::kGroup, extractGroupAddr(raw_addr)}; } int sa = extractShortAddr(raw_addr); if (sa < 0 && default_short_address >= 0) { sa = default_short_address; } return {DaliTargetKind::kShortAddress, sa}; } // ============================================================================= // Arc power ↔ percentage conversion // ============================================================================= double ArcToPercent(uint8_t arc) { if (arc == 0) return 0.0; return 100.0 * std::pow(static_cast(arc) / kArcMax, kLogFactor); } uint8_t PercentToArc(double percent) { if (percent <= 0.0) return 0; if (percent >= 100.0) return 254; return static_cast( std::round(kArcMax * std::pow(percent / 100.0, 1.0 / kLogFactor))); } // ============================================================================= // DaliGatewayBridge // ============================================================================= DaliGatewayBridge::DaliGatewayBridge(DaliDomainService& dali, uint8_t gateway_id) : dali_(dali), gateway_id_(gateway_id) {} bool DaliGatewayBridge::sendRaw(DaliTarget target, uint8_t command) const { return dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target), command); } bool DaliGatewayBridge::sendExtRaw(DaliTarget target, uint8_t command) const { return dali_.sendExtRaw(gateway_id_, encodeDaliRawAddr(target), command); } std::optional DaliGatewayBridge::queryRaw(DaliTarget target, uint8_t command) const { return dali_.queryRaw(gateway_id_, encodeDaliRawAddr(target), command); } bool DaliGatewayBridge::setArc(DaliTarget target, uint8_t arc) const { return sendRaw(target, arc); } std::optional DaliGatewayBridge::queryActualLevel(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_ACTUAL_LEVEL); } bool DaliGatewayBridge::on(DaliTarget target) const { return sendRaw(target, DALI_CMD_RECALL_MAX); } bool DaliGatewayBridge::off(DaliTarget target) const { return sendRaw(target, DALI_CMD_OFF); } bool DaliGatewayBridge::stepUp(DaliTarget target) const { return sendRaw(target, DALI_CMD_RECALL_MAX); } bool DaliGatewayBridge::stepDown(DaliTarget target) const { return sendRaw(target, DALI_CMD_OFF); } bool DaliGatewayBridge::recallMax(DaliTarget target) const { return sendRaw(target, DALI_CMD_RECALL_MAX); } bool DaliGatewayBridge::recallMin(DaliTarget target) const { return sendRaw(target, DALI_CMD_RECALL_MIN); } bool DaliGatewayBridge::goToScene(DaliTarget target, uint8_t scene) const { return sendRaw(target, DALI_CMD_GO_TO_SCENE(scene & 0x0F)); } std::optional DaliGatewayBridge::queryStatus(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_STATUS); } std::optional DaliGatewayBridge::queryDeviceType(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_DEVICE_TYPE); } std::optional DaliGatewayBridge::queryMinLevel(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_MIN_LEVEL); } std::optional DaliGatewayBridge::queryMaxLevel(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_MAX_LEVEL); } std::optional DaliGatewayBridge::queryPowerOnLevel(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_POWER_ON_LEVEL); } std::optional DaliGatewayBridge::querySystemFailureLevel(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL); } std::optional DaliGatewayBridge::queryFadeTimeRate(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_FADE_TIME_FADE_RATE); } std::optional DaliGatewayBridge::queryFadeTime(int short_address) const { return dali_.queryRaw(gateway_id_, makeShortCmdAddr(short_address), DALI_CMD_QUERY_EXTENDED_FADE_TIME); } std::optional DaliGatewayBridge::queryGroups(int short_address) const { return dali_.queryGroupMask(gateway_id_, short_address); } std::optional DaliGatewayBridge::querySceneLevel(int short_address, uint8_t scene) const { return dali_.querySceneLevel(gateway_id_, short_address, scene & 0x0F); } // ---- DT8 ---- bool DaliGatewayBridge::setColourTemperature(int short_address, int kelvin) const { return dali_.setColTemp(gateway_id_, short_address, kelvin); } bool DaliGatewayBridge::setColourRGB(int short_address, uint8_t r, uint8_t g, uint8_t b) const { return dali_.setColourRGB(gateway_id_, short_address, r, g, b); } std::optional DaliGatewayBridge::dt8StatusSnapshot( int short_address) const { return dali_.dt8StatusSnapshot(gateway_id_, short_address); } std::optional DaliGatewayBridge::dt8SceneColorReport( int short_address, int scene) const { return dali_.dt8SceneColorReport(gateway_id_, short_address, scene); } // ---- Scenes & groups ---- bool DaliGatewayBridge::setDtr(uint8_t value) const { return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), DALI_CMD_SPECIAL_SET_DTR0) && dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), value); } bool DaliGatewayBridge::setDtrAsScene(DaliTarget target, uint8_t scene) const { return sendRaw(target, DALI_CMD_SET_SCENE(scene & 0x0F)); } bool DaliGatewayBridge::addToGroup(DaliTarget target, uint8_t group) const { return sendRaw(target, DALI_CMD_ADD_TO_GROUP(group & 0x0F)); } bool DaliGatewayBridge::removeFromGroup(DaliTarget target, uint8_t group) const { return sendRaw(target, DALI_CMD_REMOVE_FROM_GROUP(group & 0x0F)); } bool DaliGatewayBridge::removeFromScene(DaliTarget target, uint8_t scene) const { return sendRaw(target, DALI_CMD_REMOVE_SCENE(scene & 0x0F)); } bool DaliGatewayBridge::setSceneLevel(DaliTarget target, uint8_t scene, uint8_t level) const { return setDtr(level) && setDtrAsScene(target, scene); } // ---- Commissioning ---- bool DaliGatewayBridge::initialise(DaliTarget target) const { return sendRaw(target, DALI_CMD_SPECIAL_INITIALIZE); } bool DaliGatewayBridge::randomise() const { return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_RANDOMIZE); } bool DaliGatewayBridge::searchAddrH(uint8_t high) const { return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), DALI_CMD_SPECIAL_SEARCHADDRH) && dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), high); } bool DaliGatewayBridge::searchAddrM(uint8_t middle) const { return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), DALI_CMD_SPECIAL_SEARCHADDRM) && dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), middle); } bool DaliGatewayBridge::searchAddrL(uint8_t low) const { return dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), DALI_CMD_SPECIAL_SEARCHADDRL) && dali_.sendRaw(gateway_id_, makeBroadcastCmdAddr(), low); } bool DaliGatewayBridge::compare() const { return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_COMPARE); } bool DaliGatewayBridge::withdraw() const { return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_WITHDRAW); } bool DaliGatewayBridge::terminate() const { return sendRaw({DaliTargetKind::kBroadcast, 0}, DALI_CMD_SPECIAL_TERMINATE); } bool DaliGatewayBridge::programShort(DaliTarget target, uint8_t short_address) const { const uint8_t raw = (short_address << 1) | 0x01; return dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target), DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS) && dali_.sendRaw(gateway_id_, encodeDaliRawAddr(target), raw); } bool DaliGatewayBridge::verifyShort(DaliTarget target) const { return sendRaw(target, DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS); } std::optional DaliGatewayBridge::queryShort(DaliTarget target) const { return dali_.queryRaw(gateway_id_, encodeDaliRawAddr(target), DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS); } bool DaliGatewayBridge::allocateAllAddr(int start_address) const { return dali_.allocateAllAddr(gateway_id_, start_address); } void DaliGatewayBridge::stopAllocAddr() const { dali_.stopAllocAddr(gateway_id_); } bool DaliGatewayBridge::resetAndAllocAddr(int start_address, bool remove_addr_first, bool close_light) const { return dali_.resetAndAllocAddr(gateway_id_, start_address, remove_addr_first, close_light); } bool DaliGatewayBridge::resetBus() const { return dali_.resetBus(gateway_id_); } } // namespace knx_dali_gw } // namespace gateway