feat: Enhance DALI Gateway with RGBW and RGBCW support

- Added support for RGBW and RGBCW color modes in the DALI Gateway.
- Updated JSON color mode parsing to handle new color types.
- Extended the StoreDt8SceneSnapshot function to include white, amber, and free color parameters.
- Introduced new methods in DaliGatewayBridge for setting RGBW, RGBCW, and RGBWAF colors.
- Modified KnxDaliChannel to send RGBW and RGBCW colors based on the color type.
- Updated parameter types and definitions in the KNX product XML files to accommodate new color modes.
- Enhanced README with migration details and validation instructions.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-29 11:32:10 +08:00
parent f39ae6f0c6
commit c60ef2ccde
19 changed files with 554 additions and 18 deletions
+1 -1
View File
@@ -1991,7 +1991,7 @@ config GATEWAY_KNX_OEM_APPLICATION_VERSION
hex "KNX OEM application version"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0x00 0xff
default 0x08
default 0x09
help
Application version advertised by the ETS-programmable KNX-DALI gateway
application. Keep this in sync with MAIN_ApplicationVersion from the
@@ -82,6 +82,8 @@ enum class DaliDt8SceneColorMode {
kDisabled,
kColorTemperature,
kRgb,
kRgbw,
kRgbcw,
};
struct DaliDomainSnapshot {
@@ -158,7 +160,8 @@ class DaliDomainService {
bool storeDt8SceneSnapshot(uint8_t gateway_id, int short_address, int scene, int brightness,
DaliDt8SceneColorMode color_mode = DaliDt8SceneColorMode::kDisabled,
int color_temperature = 0, int red = 0, int green = 0,
int blue = 0) const;
int blue = 0, int white = 0, int amber = 0,
int freecolour = 255, int rgbwaf_control = -1) const;
bool storeDt8PowerOnLevelSnapshot(uint8_t gateway_id, int short_address, int level) const;
bool storeDt8SystemFailureLevelSnapshot(uint8_t gateway_id, int short_address, int level) const;
bool setBright(uint8_t gateway_id, int short_address, int brightness) const;
@@ -166,6 +169,11 @@ class DaliDomainService {
bool setColTemp(uint8_t gateway_id, int short_address, int kelvin) const;
bool setColourRaw(uint8_t gateway_id, int raw_addr, int x, int y) const;
bool setColourRGB(uint8_t gateway_id, int short_address, int r, int g, int b) const;
bool setColourRGBW(uint8_t gateway_id, int short_address, int r, int g, int b, int w) const;
bool setColourRGBCW(uint8_t gateway_id, int short_address, int r, int g, int b,
int cool_white, int warm_white) const;
bool setColourRGBWAF(uint8_t gateway_id, int short_address, int r, int g, int b, int w,
int amber, int freecolour, int control = -1) const;
bool on(uint8_t gateway_id, int short_address) const;
bool off(uint8_t gateway_id, int short_address) const;
bool off(int short_address) const;
+57 -2
View File
@@ -107,12 +107,30 @@ void PutOptionalNumber(DaliDomainSnapshot& snapshot, const char* name,
}
}
template <typename Report>
void PutRgbwafReport(DaliDomainSnapshot& snapshot, const Report& report) {
if (!report.hasRgbwaf()) {
return;
}
snapshot.int_arrays["rgbwaf"] = report.rgbwaf;
snapshot.int_arrays["rgbw"] = {report.rgbwaf[0], report.rgbwaf[1], report.rgbwaf[2], report.rgbwaf[3]};
snapshot.int_arrays["rgbcw"] = {report.rgbwaf[0], report.rgbwaf[1], report.rgbwaf[2],
report.rgbwaf[3], report.rgbwaf[4]};
if (report.rgbwafControl.has_value()) {
snapshot.ints["rgbwafControl"] = report.rgbwafControl.value();
}
}
Dt8SceneStoreColorMode ToDaliCppColorMode(DaliDt8SceneColorMode color_mode) {
switch (color_mode) {
case DaliDt8SceneColorMode::kColorTemperature:
return Dt8SceneStoreColorMode::colorTemperature;
case DaliDt8SceneColorMode::kRgb:
return Dt8SceneStoreColorMode::rgb;
case DaliDt8SceneColorMode::kRgbw:
return Dt8SceneStoreColorMode::rgbw;
case DaliDt8SceneColorMode::kRgbcw:
return Dt8SceneStoreColorMode::rgbcw;
case DaliDt8SceneColorMode::kDisabled:
default:
return Dt8SceneStoreColorMode::disabled;
@@ -1099,6 +1117,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SceneColorReport(
if (report->hasXy()) {
snapshot.number_arrays["xy"] = report->xy;
}
PutRgbwafReport(snapshot, report.value());
return snapshot;
}
@@ -1122,6 +1141,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8PowerOnLevelColorReport(
if (report->hasXy()) {
snapshot.number_arrays["xy"] = report->xy;
}
PutRgbwafReport(snapshot, report.value());
return snapshot;
}
@@ -1145,6 +1165,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SystemFailureLevelColorR
if (report->hasXy()) {
snapshot.number_arrays["xy"] = report->xy;
}
PutRgbwafReport(snapshot, report.value());
return snapshot;
}
@@ -1152,7 +1173,8 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
int brightness,
DaliDt8SceneColorMode color_mode,
int color_temperature, int red, int green,
int blue) const {
int blue, int white, int amber, int freecolour,
int rgbwaf_control) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
return false;
@@ -1160,7 +1182,8 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
markBusActivity(gateway_id);
return channel->dali->dt8.storeSceneSnapshot(short_address, scene, brightness,
ToDaliCppColorMode(color_mode), color_temperature,
red, green, blue);
red, green, blue, white, amber, freecolour,
rgbwaf_control);
}
bool DaliDomainService::storeDt8PowerOnLevelSnapshot(uint8_t gateway_id, int short_address,
@@ -1229,6 +1252,38 @@ bool DaliDomainService::setColourRGB(uint8_t gateway_id, int short_address, int
return channel->dali->dt8.setColourRGB(short_address, r, g, b);
}
bool DaliDomainService::setColourRGBW(uint8_t gateway_id, int short_address, int r, int g,
int b, int w) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColourRGBW(short_address, r, g, b, w);
}
bool DaliDomainService::setColourRGBCW(uint8_t gateway_id, int short_address, int r, int g,
int b, int cool_white, int warm_white) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColourRGBCW(short_address, r, g, b, cool_white, warm_white);
}
bool DaliDomainService::setColourRGBWAF(uint8_t gateway_id, int short_address, int r, int g,
int b, int w, int amber, int freecolour,
int control) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColourRGBWAF(short_address, r, g, b, w, amber, freecolour,
control);
}
bool DaliDomainService::on(uint8_t gateway_id, int short_address) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
@@ -1020,6 +1020,12 @@ DaliDt8SceneColorMode JsonColorMode(const cJSON* root) {
if (item->valueint == 2) {
return DaliDt8SceneColorMode::kRgb;
}
if (item->valueint == 3) {
return DaliDt8SceneColorMode::kRgbw;
}
if (item->valueint == 4) {
return DaliDt8SceneColorMode::kRgbcw;
}
return DaliDt8SceneColorMode::kDisabled;
}
if (!cJSON_IsString(item) || item->valuestring == nullptr) {
@@ -1032,6 +1038,12 @@ DaliDt8SceneColorMode JsonColorMode(const cJSON* root) {
if (mode == "rgb") {
return DaliDt8SceneColorMode::kRgb;
}
if (mode == "rgbw") {
return DaliDt8SceneColorMode::kRgbw;
}
if (mode == "rgbcw" || mode == "rgbWwCw" || mode == "rgb_cw") {
return DaliDt8SceneColorMode::kRgbcw;
}
return DaliDt8SceneColorMode::kDisabled;
}
@@ -1049,6 +1061,15 @@ GatewayBridgeHttpResponse StoreDt8SceneSnapshot(DaliDomainService& domain, uint8
const int red = JsonIntAny(root, "red", "r").value_or(0);
const int green = JsonIntAny(root, "green", "g").value_or(0);
const int blue = JsonIntAny(root, "blue", "b").value_or(0);
const int white = JsonIntAny(root, "white", "w")
.value_or(JsonIntAny(root, "coolWhite", "cool_white")
.value_or(JsonInt(root, "cw").value_or(0)));
const int amber = JsonIntAny(root, "amber", "a")
.value_or(JsonIntAny(root, "warmWhite", "warm_white")
.value_or(JsonInt(root, "ww").value_or(0)));
const int freecolour = JsonIntAny(root, "freecolour", "freeColor").value_or(255);
const int rgbwaf_control = JsonIntAny(root, "rgbwafControl", "rgbwaf_control")
.value_or(JsonInt(root, "control").value_or(-1));
cJSON_Delete(root);
if (!address.has_value() || !scene.has_value() || !brightness.has_value() ||
@@ -1057,7 +1078,7 @@ GatewayBridgeHttpResponse StoreDt8SceneSnapshot(DaliDomainService& domain, uint8
}
if (!domain.storeDt8SceneSnapshot(gateway_id, address.value(), scene.value(),
brightness.value(), color_mode, color_temperature, red,
green, blue)) {
green, blue, white, amber, freecolour, rgbwaf_control)) {
return ErrorResponse(ESP_FAIL, "failed to store DT8 scene snapshot");
}
return StoredSnapshotResponse(domain.dt8SceneColorReport(gateway_id, address.value(),
@@ -76,6 +76,11 @@ class DaliGatewayBridge {
bool setColourTemperature(int short_address, int kelvin) const;
bool setColourRGB(int short_address, uint8_t r, uint8_t g, uint8_t b) const;
bool setColourRGBW(int short_address, uint8_t r, uint8_t g, uint8_t b, uint8_t w) const;
bool setColourRGBCW(int short_address, uint8_t r, uint8_t g, uint8_t b,
uint8_t cool_white, uint8_t warm_white) const;
bool setColourRGBWAF(int short_address, uint8_t r, uint8_t g, uint8_t b, uint8_t w,
uint8_t amber, uint8_t freecolour, uint8_t control) const;
std::optional<DaliDomainSnapshot> dt8StatusSnapshot(int short_address) const;
std::optional<DaliDomainSnapshot> dt8SceneColorReport(int short_address, int scene) const;
+8 -2
View File
@@ -1,6 +1,10 @@
#pragma once
#include <stdint.h>
#if __has_include("sdkconfig.h")
#include "sdkconfig.h"
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID
#define CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID 0x00A4
@@ -11,12 +15,12 @@
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x08
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x09
#endif
// Minimal stub for knxprod.h — generated KNX product definitions.
// The full file (1796 bytes of parameters, 1439 group objects) will be
// adapted in Phase 3 to use the gateway/knx API directly.
// replaced by the OpenKNXproducer-generated header.
// Product identity
#define MAIN_OpenKnxId (CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID & 0xff)
@@ -45,6 +49,8 @@ enum PT_ColorType : uint8_t {
PT_colorType_RGB = 1,
PT_colorType_TW = 2,
PT_colorType_XYY = 3,
PT_colorType_RGBW = 4,
PT_colorType_RGBCW = 5,
};
enum PT_ColorSpace : uint8_t {
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<Parameters>
<Parameter Id="%AID%_P-%TT%%CC%001" Name="colorType%C%" ParameterType="%AID%_PT-ColorType" Text="Address %C% colour type" Value="2">
<Property ObjectType="160" PropertyId="201" Offset="%C-1%" BitOffset="0" />
</Parameter>
</Parameters>
<ParameterRefs>
<ParameterRef Id="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_P-%TT%%CC%001" />
</ParameterRefs>
<ComObjectTable>
<ComObject Id="%AID%_O-%TT%%CC%001" Number="%K0%" Name="Address %C% switch" Text="Switch" FunctionText="Switch" DatapointType="DPST-1-1" ObjectSize="1 Bit" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%002" Number="%K1%" Name="Address %C% brightness" Text="Brightness" FunctionText="Brightness" DatapointType="DPST-5-1" ObjectSize="1 Byte" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%003" Number="%K2%" Name="Address %C% RGB colour" Text="RGB colour" FunctionText="RGB colour" DatapointType="DPST-232-600" ObjectSize="3 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%004" Number="%K3%" Name="Address %C% RGBW colour" Text="RGBW colour" FunctionText="RGBW colour" DatapointType="DPST-251-600" ObjectSize="6 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%005" Number="%K4%" Name="Address %C% RGBCW colour" Text="RGBCW colour" FunctionText="RGBCW colour" DatapointType="DPST-251-600" ObjectSize="6 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%006" Number="%K5%" Name="Address %C% colour temperature" Text="Colour temperature" FunctionText="Colour temperature" DatapointType="DPST-7-600" ObjectSize="2 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
</ComObjectTable>
<ComObjectRefs>
<ComObjectRef Id="%AID%_O-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_O-%TT%%CC%001" />
<ComObjectRef Id="%AID%_O-%TT%%CC%002_R-%TT%%CC%00201" RefId="%AID%_O-%TT%%CC%002" />
<ComObjectRef Id="%AID%_O-%TT%%CC%003_R-%TT%%CC%00301" RefId="%AID%_O-%TT%%CC%003" />
<ComObjectRef Id="%AID%_O-%TT%%CC%004_R-%TT%%CC%00401" RefId="%AID%_O-%TT%%CC%004" />
<ComObjectRef Id="%AID%_O-%TT%%CC%005_R-%TT%%CC%00501" RefId="%AID%_O-%TT%%CC%005" />
<ComObjectRef Id="%AID%_O-%TT%%CC%006_R-%TT%%CC%00601" RefId="%AID%_O-%TT%%CC%006" />
</ComObjectRefs>
</Static>
<Dynamic>
<ChannelIndependentBlock>
<ParameterBlock Id="%AID%_PB-ADR%CC%" Text="DALI address %C%" Inline="true" Layout="Grid">
<Rows>
<Row Id="%AID%_PB-ADR%CC%_R-1" />
</Rows>
<Columns>
<Column Id="%AID%_PB-ADR%CC%_C-1" Width="30%" />
<Column Id="%AID%_PB-ADR%CC%_C-2" Width="70%" />
</Columns>
<ParameterSeparator Id="%AID%_PS-ADR%CC%" Text="DALI address %C%" Cell="1,1" />
<ParameterRefRef RefId="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" Cell="1,2" />
</ParameterBlock>
</ChannelIndependentBlock>
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<ParameterTypes>
<ParameterType Id="%AID%_PT-ColorType" Name="ColorType">
<TypeRestriction Base="Value" SizeInBit="%DALI_ColorTypeBits%">
<Enumeration Id="%AID%_PT-ColorType_EN-0" Text="HSV" Value="0" />
<Enumeration Id="%AID%_PT-ColorType_EN-1" Text="RGB" Value="1" />
<Enumeration Id="%AID%_PT-ColorType_EN-2" Text="Tunable white" Value="2" />
<Enumeration Id="%AID%_PT-ColorType_EN-3" Text="xyY" Value="3" />
<Enumeration Id="%AID%_PT-ColorType_EN-4" Text="RGBW" Value="4" />
<Enumeration Id="%AID%_PT-ColorType_EN-5" Text="RGBCW" Value="5" />
</TypeRestriction>
</ParameterType>
</ParameterTypes>
</Static>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<Parameters>
<Parameter Id="%AID%_P-%TT%%CC%001" Name="colorType%C%" ParameterType="%AID%_PT-ColorType" Text="Group %C% colour type" Value="2">
<Property ObjectType="161" PropertyId="201" Offset="%C-1%" BitOffset="0" />
</Parameter>
</Parameters>
<ParameterRefs>
<ParameterRef Id="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_P-%TT%%CC%001" />
</ParameterRefs>
<ComObjectTable>
<ComObject Id="%AID%_O-%TT%%CC%001" Number="%K0%" Name="Group %C% switch" Text="Switch" FunctionText="Switch" DatapointType="DPST-1-1" ObjectSize="1 Bit" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%002" Number="%K1%" Name="Group %C% brightness" Text="Brightness" FunctionText="Brightness" DatapointType="DPST-5-1" ObjectSize="1 Byte" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%003" Number="%K2%" Name="Group %C% RGB colour" Text="RGB colour" FunctionText="RGB colour" DatapointType="DPST-232-600" ObjectSize="3 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%004" Number="%K3%" Name="Group %C% RGBW colour" Text="RGBW colour" FunctionText="RGBW colour" DatapointType="DPST-251-600" ObjectSize="6 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%005" Number="%K4%" Name="Group %C% RGBCW colour" Text="RGBCW colour" FunctionText="RGBCW colour" DatapointType="DPST-251-600" ObjectSize="6 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
<ComObject Id="%AID%_O-%TT%%CC%006" Number="%K5%" Name="Group %C% colour temperature" Text="Colour temperature" FunctionText="Colour temperature" DatapointType="DPST-7-600" ObjectSize="2 Bytes" Priority="Low" ReadFlag="false" WriteFlag="true" CommunicationFlag="true" TransmitFlag="false" UpdateFlag="false" />
</ComObjectTable>
<ComObjectRefs>
<ComObjectRef Id="%AID%_O-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_O-%TT%%CC%001" />
<ComObjectRef Id="%AID%_O-%TT%%CC%002_R-%TT%%CC%00201" RefId="%AID%_O-%TT%%CC%002" />
<ComObjectRef Id="%AID%_O-%TT%%CC%003_R-%TT%%CC%00301" RefId="%AID%_O-%TT%%CC%003" />
<ComObjectRef Id="%AID%_O-%TT%%CC%004_R-%TT%%CC%00401" RefId="%AID%_O-%TT%%CC%004" />
<ComObjectRef Id="%AID%_O-%TT%%CC%005_R-%TT%%CC%00501" RefId="%AID%_O-%TT%%CC%005" />
<ComObjectRef Id="%AID%_O-%TT%%CC%006_R-%TT%%CC%00601" RefId="%AID%_O-%TT%%CC%006" />
</ComObjectRefs>
</Static>
<Dynamic>
<ChannelIndependentBlock>
<ParameterBlock Id="%AID%_PB-GRP%CC%" Text="DALI group %C%" Inline="true" Layout="Grid">
<Rows>
<Row Id="%AID%_PB-GRP%CC%_R-1" />
</Rows>
<Columns>
<Column Id="%AID%_PB-GRP%CC%_C-1" Width="30%" />
<Column Id="%AID%_PB-GRP%CC%_C-2" Width="70%" />
</Columns>
<ParameterSeparator Id="%AID%_PS-GRP%CC%" Text="DALI group %C%" Cell="1,1" />
<ParameterRefRef RefId="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" Cell="1,2" />
</ParameterBlock>
</ChannelIndependentBlock>
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<ParameterTypes>
<ParameterType Id="%AID%_PT-Bool" Name="Bool">
<TypeRestriction Base="Value" SizeInBit="1">
<Enumeration Id="%AID%_PT-Bool_EN-0" Text="Disabled" Value="0" />
<Enumeration Id="%AID%_PT-Bool_EN-1" Text="Enabled" Value="1" />
</TypeRestriction>
</ParameterType>
</ParameterTypes>
</Static>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<Parameters>
<Parameter Id="%AID%_P-%TT%%CC%001" Name="curveEnabled%C%" ParameterType="%AID%_PT-Bool" Text="HCL curve %C% enabled" Value="0">
<Property ObjectType="162" PropertyId="201" Offset="%C-1%" BitOffset="0" />
</Parameter>
</Parameters>
<ParameterRefs>
<ParameterRef Id="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_P-%TT%%CC%001" />
</ParameterRefs>
</Static>
<Dynamic>
<ChannelIndependentBlock>
<ParameterBlock Id="%AID%_PB-HCL%CC%" Text="HCL curve %C%" Inline="true" Layout="Grid">
<Rows>
<Row Id="%AID%_PB-HCL%CC%_R-1" />
</Rows>
<Columns>
<Column Id="%AID%_PB-HCL%CC%_C-1" Width="30%" />
<Column Id="%AID%_PB-HCL%CC%_C-2" Width="70%" />
</Columns>
<ParameterSeparator Id="%AID%_PS-HCL%CC%" Text="HCL curve %C%" Cell="1,1" />
<ParameterRefRef RefId="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" Cell="1,2" />
</ParameterBlock>
</ChannelIndependentBlock>
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<Catalog>
<CatalogSection Id="M-00FA_CS-DALI" Name="DALI" Number="DALI" DefaultLanguage="en-US">
<CatalogItem Id="%CatalogItemId%" Name="OpenKNX: %ProductName%%BuildSuffixText%" Number="1" ProductRefId="%ProductId%" Hardware2ProgramRefId="%Hardware2ProgramId%" DefaultLanguage="en-US" />
</CatalogSection>
</Catalog>
<ApplicationPrograms>
<op:version OpenKnxId="%OpenKnxId%" ApplicationNumber="%ApplicationNumber%" ApplicationVersion="%ApplicationVersion%" ReplacesVersions="%ReplacesVersions%" ApplicationRevision="%ApplicationRevision%" FirmwareRevision="%FirmwareRevision%" />
<ApplicationProgram Id="%AID%" ProgramType="ApplicationProgram" MaskVersion="%MaskVersion%" Name="%ApplicationName%%BuildSuffix%" LoadProcedureStyle="MergedProcedure" PeiType="0" DefaultLanguage="en-US" IsSecureEnabled="false" DynamicTableManagement="false" Linkable="false" MinEtsVersion="6.0" ApplicationNumber="0" ApplicationVersion="0" ReplacesVersions="0">
<Static>
<Code>
<AbsoluteSegment Id="%AID%_AS-0100" Address="256" Size="1024" />
</Code>
<ParameterTypes>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterTypes/ParameterType" prefix="%prefix%" />
</ParameterTypes>
<Parameters>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/Parameters/Parameter|//ApplicationProgram/Static/Parameters/Union" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/Parameters/Parameter|//ApplicationProgram/Static/Parameters/Union" type="template" prefix="%prefix%" />
</Parameters>
<ParameterRefs>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterRefs/ParameterRef" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ParameterRefs/ParameterRef" type="template" prefix="%prefix%" />
</ParameterRefs>
<AddressTable MaxEntries="2047" />
<AssociationTable MaxEntries="2047" />
<ComObjectTable>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ComObjectTable/ComObject" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ComObjectTable/ComObject" type="template" prefix="%prefix%" />
</ComObjectTable>
<ComObjectRefs>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ComObjectRefs/ComObjectRef" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ComObjectRefs/ComObjectRef" type="template" prefix="%prefix%" />
</ComObjectRefs>
<LoadProcedures>
<LoadProcedure MergeId="4">
<LdCtrlWriteMem Address="256" Size="1024" Verify="true" />
</LoadProcedure>
</LoadProcedures>
<Messages>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/Messages/*" prefix="%prefix%" />
</Messages>
<Options Comparable="true" Reconstructable="true" DownloadInvisibleParameters="None" />
</Static>
<ModuleDefs>
<op:includetemplate href="%share%" xpath="//ApplicationProgram/ModuleDefs/*" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/ModuleDefs/*" type="template" prefix="%prefix%" />
</ModuleDefs>
<Dynamic>
<op:includetemplate href="%share%" xpath="//Dynamic/*" prefix="%prefix%" />
<op:includetemplate href="%templ%" xpath="//Dynamic/*" type="template" prefix="%prefix%" />
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
<Hardware>
<Hardware Id="%HardwareId%" Name="OpenKNX-%HardwareName%%BuildSuffix%" SerialNumber="%SerialNumber%" VersionNumber="1" BusCurrent="%BusCurrent%" HasIndividualAddress="true" HasApplicationProgram="true">
<Products>
<Product Id="%ProductId%" Text="OpenKNX: %ProductName%%BuildSuffixText%" OrderNumber="%OrderNumber%" IsRailMounted="%IsRailMounted%" WidthInMillimeter="72" DefaultLanguage="en-US">
<RegistrationInfo RegistrationStatus="Registered" />
</Product>
</Products>
<Hardware2Programs>
<Hardware2Program Id="%Hardware2ProgramId%" MediumTypes="%MediumTypes%">
<ApplicationProgramRef RefId="%AID%" />
<RegistrationInfo RegistrationStatus="Registered" RegistrationNumber="0001/%HardwareVersionEncoded%1" />
</Hardware2Program>
</Hardware2Programs>
</Hardware>
</Hardware>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<ApplicationPrograms>
<ApplicationProgram>
<Static>
<ParameterTypes>
<ParameterType Id="%AID%_PT-Bool" Name="Bool">
<TypeRestriction Base="Value" SizeInBit="1">
<Enumeration Id="%AID%_PT-Bool_EN-0" Text="Disabled" Value="0" />
<Enumeration Id="%AID%_PT-Bool_EN-1" Text="Enabled" Value="1" />
</TypeRestriction>
</ParameterType>
<ParameterType Id="%AID%_PT-ColorType" Name="ColorType">
<TypeRestriction Base="Value" SizeInBit="%DALI_ColorTypeBits%">
<Enumeration Id="%AID%_PT-ColorType_EN-0" Text="HSV" Value="0" />
<Enumeration Id="%AID%_PT-ColorType_EN-1" Text="RGB" Value="1" />
<Enumeration Id="%AID%_PT-ColorType_EN-2" Text="Tunable white" Value="2" />
<Enumeration Id="%AID%_PT-ColorType_EN-3" Text="xyY" Value="3" />
<Enumeration Id="%AID%_PT-ColorType_EN-4" Text="RGBW" Value="4" />
<Enumeration Id="%AID%_PT-ColorType_EN-5" Text="RGBCW" Value="5" />
</TypeRestriction>
</ParameterType>
<ParameterType Id="%AID%_PT-Level" Name="Level">
<TypeRestriction Base="Value" SizeInBit="8" />
</ParameterType>
<ParameterType Id="%AID%_PT-Kelvin" Name="Kelvin">
<TypeNumber SizeInBit="16" Type="unsignedInt" minInclusive="1000" maxInclusive="20000" />
</ParameterType>
</ParameterTypes>
<ComObjectTable />
<ComObjectRefs />
<ParameterRefs />
</Static>
<Dynamic>
<ChannelIndependentBlock>
<ParameterBlock Id="%AID%_PB-DALI-Main" Text="DALI Gateway" Inline="true" Layout="Grid">
<Rows>
<Row Id="%AID%_PB-DALI-Main_R-1" />
</Rows>
<Columns>
<Column Id="%AID%_PB-DALI-Main_C-1" Width="100%" />
</Columns>
<ParameterSeparator Id="%AID%_PS-DALI-Main" Text="DALI Gateway" Cell="1,1" />
</ParameterBlock>
</ChannelIndependentBlock>
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
</Manufacturer>
</ManufacturerData>
</KNX>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
<op:config name="%DALI_AddressChannels%" value="64" />
<op:config name="%DALI_GroupChannels%" value="16" />
<op:config name="%DALI_HclCurves%" value="3" />
<op:config name="%DALI_ColorTypeBits%" value="3" />
<op:ETS OpenKnxId="0xA4"
ApplicationNumber="1"
ApplicationVersion="0.9"
ReplacesVersions="0.8 0.5"
ApplicationRevision="0"
ProductName="DALI Gateway"
SerialNumber="0xA401"
OrderNumber="REG1-Dali"
ApplicationName="DALI Gateway"
IsRailMounted="true"
BusCurrent="10"
MaskVersion="MV-07B0" />
<op:define prefix="DALI" NumChannels="%DALI_AddressChannels%" ModuleType="60" KoOffset="1" KoBlockSize="16" share="DALI-GW.share.xml" template="DALI-GW-address.templ.xml" />
<op:define prefix="DGRP" NumChannels="%DALI_GroupChannels%" ModuleType="61" KoOffset="1025" KoBlockSize="16" share="DALI-GW-group.share.xml" template="DALI-GW-group.templ.xml" />
<op:define prefix="HCL" NumChannels="%DALI_HclCurves%" ModuleType="62" share="DALI-GW-hcl.share.xml" template="DALI-GW-hcl.templ.xml" />
<generate base="DALI-GW.base.xml" />
</KNX>
+48
View File
@@ -0,0 +1,48 @@
# DALI-GW KNX Product XML
This folder contains the gateway-owned OpenKNXproducer source for the REG1-Dali KNX-DALI gateway persona.
The source migrates the legacy Kaenx Creator database from `temp/openknx/GW-REG1-Dali/DALI-GW.ae-manu` into the gateway component so the generated product database, generated `knxprod.h`, and ESP-IDF runtime can evolve together.
Current migration targets:
- Manufacturer/OpenKNX id: `0xA4`
- Application number: `0x01`
- Application version: `0x09`
- Mask: `MV-07B0`
- Order number: `REG1-Dali`
- Address channels: `64`
- Group channels: `16`
- HCL curves: `3`
The existing color type values stay stable for compatibility:
- `HSV = 0`
- `RGB = 1`
- `TW = 2`
- `XYY = 3`
- `RGBW = 4`
- `RGBCW = 5`
RGBW is exposed with standard DPT `251.600` (`DPT_Colour_RGBW`). RGBCW uses the same RGBW payload shape and maps the white and amber DT8 RGBWAF channels as cool-white and warm-white levels at runtime because the local KNX master database does not define a standard RGBCW datapoint subtype.
## Validation
Run a well-formed XML check from the repository root:
```sh
xmllint --noout gateway/components/knx_dali_gw/knxprod/*.xml
```
Generate the producer header and product database artifacts with:
```sh
OpenKNXproducer create --NoXsd \
-h /tmp/DALI-GW-knxprod.h \
-o /tmp/DALI-GW.knxprod \
gateway/components/knx_dali_gw/knxprod/DALI-GW.xml
```
In this workspace the producer validates the XML and writes `/tmp/DALI-GW-knxprod.h`. If the local producer installation does not have an ETS converter available it prints `No ETS found, skipped knxprod creation!` and omits the `.knxprod` archive while still exiting successfully.
The generated header must stay synchronized with the runtime identity in `gateway/apps/gateway/main/Kconfig.projbuild` and `gateway/components/knx_dali_gw/src/knx_dali_gw.cpp`.
@@ -207,6 +207,25 @@ bool DaliGatewayBridge::setColourRGB(int short_address, uint8_t r, uint8_t g,
return dali_.setColourRGB(gateway_id_, short_address, r, g, b);
}
bool DaliGatewayBridge::setColourRGBW(int short_address, uint8_t r, uint8_t g,
uint8_t b, uint8_t w) const {
return dali_.setColourRGBW(gateway_id_, short_address, r, g, b, w);
}
bool DaliGatewayBridge::setColourRGBCW(int short_address, uint8_t r, uint8_t g,
uint8_t b, uint8_t cool_white,
uint8_t warm_white) const {
return dali_.setColourRGBCW(gateway_id_, short_address, r, g, b, cool_white,
warm_white);
}
bool DaliGatewayBridge::setColourRGBWAF(int short_address, uint8_t r, uint8_t g,
uint8_t b, uint8_t w, uint8_t amber,
uint8_t freecolour, uint8_t control) const {
return dali_.setColourRGBWAF(gateway_id_, short_address, r, g, b, w, amber,
freecolour, control);
}
std::optional<DaliDomainSnapshot> DaliGatewayBridge::dt8StatusSnapshot(
int short_address) const {
return dali_.dt8StatusSnapshot(gateway_id_, short_address);
@@ -132,8 +132,25 @@ void KnxDaliChannel::setDimmState(uint8_t value, bool, bool) {
void KnxDaliChannel::sendColor() {
if (dali_ == nullptr) return;
dali_->setColourRGB(static_cast<int>(index_), current_color_[0],
current_color_[1], current_color_[2]);
const auto color_type = is_group_ ? ParamGRP_colorType(index_) : ParamADR_colorType(index_);
switch (color_type) {
case PT_ColorType::PT_colorType_RGBW:
dali_->setColourRGBW(static_cast<int>(index_), current_color_[0], current_color_[1],
current_color_[2], current_color_[3]);
break;
case PT_ColorType::PT_colorType_RGBCW:
dali_->setColourRGBCW(static_cast<int>(index_), current_color_[0], current_color_[1],
current_color_[2], current_color_[3], current_color_[4]);
break;
case PT_ColorType::PT_colorType_RGB:
case PT_ColorType::PT_colorType_HSV:
case PT_ColorType::PT_colorType_XYY:
case PT_ColorType::PT_colorType_TW:
default:
dali_->setColourRGB(static_cast<int>(index_), current_color_[0], current_color_[1],
current_color_[2]);
break;
}
}
// ---- KO Handlers ----
@@ -172,12 +189,21 @@ void KnxDaliChannel::koHandleLock(GroupObject& ko) {
}
void KnxDaliChannel::koHandleColor(GroupObject& ko) {
KNXValue val = ko.value();
if (true) {
// RGB packed in float or raw bytes
// Simplified: store and send
sendColor();
const auto color_type = is_group_ ? ParamGRP_colorType(index_) : ParamADR_colorType(index_);
uint8_t* data = ko.valueRef();
if (data != nullptr) {
current_color_[0] = data[0];
current_color_[1] = data[1];
current_color_[2] = data[2];
if (color_type == PT_ColorType::PT_colorType_RGBW ||
color_type == PT_ColorType::PT_colorType_RGBCW) {
current_color_[3] = data[3];
}
if (color_type == PT_ColorType::PT_colorType_RGBCW) {
current_color_[4] = data[4];
}
}
sendColor();
}
} // namespace knx_dali_gw
@@ -77,7 +77,7 @@ class KnxDaliChannel {
bool current_state_{false};
uint8_t current_step_{0};
bool current_is_locked_{false};
uint8_t current_color_[4]{};
uint8_t current_color_[6]{};
// HCL
uint8_t hcl_curve_{255};
+9 -3
View File
@@ -21,21 +21,27 @@ constexpr const char* kTag = "knx_dali_gw";
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER 0x0001
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID
#define CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID 0xA401
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x08
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x09
#endif
constexpr uint16_t kKnxOemManufacturerId =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID);
constexpr uint16_t kKnxOemApplicationNumber =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER);
constexpr uint16_t kKnxOemHardwareId =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID);
constexpr uint8_t kKnxOemApplicationVersion =
static_cast<uint8_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION);
constexpr uint8_t kKnxOemHardwareType[6] = {
0x00,
0x00,
static_cast<uint8_t>(kKnxOemManufacturerId & 0xff),
static_cast<uint8_t>(kKnxOemApplicationNumber & 0xff),
static_cast<uint8_t>((kKnxOemHardwareId >> 8) & 0xff),
static_cast<uint8_t>(kKnxOemHardwareId & 0xff),
kKnxOemApplicationVersion,
0x00};
constexpr uint8_t kKnxOemProgramVersion[5] = {