]> git.basschouten.com Git - openhab-addons.git/commitdiff
[dali] Implement DT8 (single-channel RGB & color temperature) device type (#12955)
authorSebastian P <5564491+s3lph@users.noreply.github.com>
Fri, 1 Jul 2022 06:11:00 +0000 (08:11 +0200)
committerGitHub <noreply@github.com>
Fri, 1 Jul 2022 06:11:00 +0000 (08:11 +0200)
* DALI: Implement DT8 (single-channel RGB & color temperature) device type, fix device and group addressing
* dali: Store BridgeHandler in a local variable instead of retrieving it over and over again.
* dali: Follow logging guidelines.

Signed-off-by: Sebastian Philipp <github-ebqurd@s3lph.me>
bundles/org.openhab.binding.dali/README.md
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/DaliBindingConstants.java
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/DaliHandlerFactory.java
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/handler/DaliDeviceHandler.java
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/handler/DaliDt8DeviceHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/handler/DaliserverBridgeHandler.java
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/protocol/DaliAddress.java
bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/protocol/DaliStandardCommand.java
bundles/org.openhab.binding.dali/src/main/resources/OH-INF/i18n/dali.properties
bundles/org.openhab.binding.dali/src/main/resources/OH-INF/thing/thing-types.xml

index 2c89809c0f2f4d63a5aaa7e6143bd703999a200b..ebbd159ece186889b1dd750d89ba87294da15fbe 100644 (file)
@@ -13,6 +13,8 @@ Currently, these things are supported:
  - device (single device/ballast on the DALI bus)
  - group (group of DALI devices)
  - rgb (virtual device consisting of three directly addressed devices that represent r/g/b (LED) color channels)
+ - device-dt8 (single device/ballast supporting DT8 (single-channel RGB & color temperature control))
+ - group-dt8 (group of DALI devices supporting DT8)
  
 This binding was tested on a DALI 1 bus with daliserver 0.2.
 
@@ -37,9 +39,10 @@ Automatic device discovery is not yet implemented.
 
 ### group
 
-| Parameter   | Parameter ID | Required/Optional |  description                           |
-|-------------|--------------|-------------------|----------------------------------------|
-| Group  ID   | targetId     | Required          | Address of group in the DALI bus       |
+| Parameter      | Parameter ID       | Required/Optional | description                                                                                  |
+|----------------|--------------------|-------------------|----------------------------------------------------------------------------------------------|
+| Group  ID      | targetId           | Required          | Address of group in the DALI bus                                                             |
+| Read Device ID | readDeviceTargetId | Optional          | If reading values from this group fails, you can choose to read from a single device instead |
 
 ### rgb
 
@@ -49,6 +52,19 @@ Automatic device discovery is not yet implemented.
 | G Device ID | targetIdG    | Required          | Address of device in the DALI bus      |
 | B Device ID | targetIdB    | Required          | Address of device in the DALI bus      |
 
+### device-dt8
+
+| Parameter   | Parameter ID | Required/Optional |  description                           |
+|-------------|--------------|-------------------|----------------------------------------|
+| Device ID   | targetId     | Required          | Address of device in the DALI bus      |
+
+### group-dt8
+
+| Parameter      | Parameter ID       | Required/Optional | description                                                                                  |
+|----------------|--------------------|-------------------|----------------------------------------------------------------------------------------------|
+| Group  ID      | targetId           | Required          | Address of group in the DALI bus                                                             |
+| Read Device ID | readDeviceTargetId | Optional          | If reading values from this group fails, you can choose to read from a single device instead |
+
 ## Full Example
 
 .things file
@@ -57,7 +73,7 @@ Automatic device discovery is not yet implemented.
 Bridge dali:daliserver:237dbae7 "Daliserver" [ host="localhost", port=55825] {
     Thing rgb 87bf0403-a45d-4037-b874-28f4ece30004 "RGB Lights" [ targetIdR=0, targetIdG=1, targetIdB=2 ]
     Thing device 995e16ca-07c4-4111-9cda-504cb5120f82 "Warm White" [ targetId=3 ]
-    Thing group 31da8dac-8e09-455a-bc7a-6ed70f740001 "Living Room Lights" [ targetId=0 ]
+    Thing group 31da8dac-8e09-455a-bc7a-6ed70f740001 "Living Room Lights" [ targetId=0, readDeviceTargetId=3 ]
 }
 ```
 
index 2b3a35c1600556f7df024de5f2e952e16d09ee79..de0cce92e5c5194f791adaa4370874d44d54ed54 100644 (file)
@@ -35,15 +35,18 @@ public class DaliBindingConstants {
     public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
     public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");
     public static final ThingTypeUID THING_TYPE_RGB = new ThingTypeUID(BINDING_ID, "rgb");
-
-    public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES_UIDS = new HashSet<>(
-            Arrays.asList(THING_TYPE_DEVICE, THING_TYPE_GROUP, THING_TYPE_RGB));
+    public static final ThingTypeUID THING_TYPE_DEVICE_DT8 = new ThingTypeUID(BINDING_ID, "device-dt8");
+    public static final ThingTypeUID THING_TYPE_GROUP_DT8 = new ThingTypeUID(BINDING_ID, "group-dt8");
+    public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES_UIDS = new HashSet<>(Arrays
+            .asList(THING_TYPE_DEVICE, THING_TYPE_GROUP, THING_TYPE_RGB, THING_TYPE_DEVICE_DT8, THING_TYPE_GROUP_DT8));
 
     public static final String CHANNEL_DIM_AT_FADE_RATE = "dimAtFadeRate";
     public static final String CHANNEL_DIM_IMMEDIATELY = "dimImmediately";
     public static final String CHANNEL_COLOR = "color";
+    public static final String CHANNEL_COLOR_TEMPERATURE = "color-temperature-abs";
 
     public static final String TARGET_ID = "targetId";
+    public static final String READ_DEVICE_TARGET_ID = "readDeviceTargetId";
     public static final String TARGET_ID_R = "targetIdR";
     public static final String TARGET_ID_G = "targetIdG";
     public static final String TARGET_ID_B = "targetIdB";
index 9f3b358a4fbf6e6f68fc209a095b17e8db10f8f0..e75b04fd8c012a9069b94ee24500295e5ab9cb4c 100644 (file)
@@ -21,6 +21,7 @@ import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.dali.internal.handler.DaliDeviceHandler;
+import org.openhab.binding.dali.internal.handler.DaliDt8DeviceHandler;
 import org.openhab.binding.dali.internal.handler.DaliRgbHandler;
 import org.openhab.binding.dali.internal.handler.DaliserverBridgeHandler;
 import org.openhab.core.thing.Bridge;
@@ -64,6 +65,9 @@ public class DaliHandlerFactory extends BaseThingHandlerFactory {
         if (THING_TYPE_RGB.equals(thingTypeUID)) {
             return new DaliRgbHandler(thing);
         }
+        if (THING_TYPE_DEVICE_DT8.equals(thingTypeUID) || THING_TYPE_GROUP_DT8.equals(thingTypeUID)) {
+            return new DaliDt8DeviceHandler(thing);
+        }
 
         return null;
     }
index d531df1fc7b70d1e2ccaae977c6a268f13da9039..c371fd10844cf71f8cbd1a2e8c1515ae331e11d9 100644 (file)
@@ -22,6 +22,7 @@ import org.openhab.binding.dali.internal.protocol.DaliAddress;
 import org.openhab.binding.dali.internal.protocol.DaliDAPCCommand;
 import org.openhab.binding.dali.internal.protocol.DaliResponse;
 import org.openhab.binding.dali.internal.protocol.DaliStandardCommand;
+import org.openhab.core.config.core.Configuration;
 import org.openhab.core.library.types.IncreaseDecreaseType;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.PercentType;
@@ -45,7 +46,8 @@ import org.slf4j.LoggerFactory;
 @NonNullByDefault
 public class DaliDeviceHandler extends BaseThingHandler {
     private final Logger logger = LoggerFactory.getLogger(DaliDeviceHandler.class);
-    private @Nullable Integer targetId;
+    protected @Nullable Integer targetId;
+    protected @Nullable Integer readDeviceTargetId;
 
     public DaliDeviceHandler(Thing thing) {
         super(thing);
@@ -61,7 +63,16 @@ public class DaliDeviceHandler extends BaseThingHandler {
             updateStatus(ThingStatus.ONLINE);
         }
 
-        targetId = ((BigDecimal) this.thing.getConfiguration().get(TARGET_ID)).intValueExact();
+        final Configuration conf = this.thing.getConfiguration();
+        targetId = ((BigDecimal) conf.get(TARGET_ID)).intValueExact();
+        // Reading from group addresses does not work generally, so if a fallback device id is
+        // defined, use that instead when reading the current state
+        if (conf.get(READ_DEVICE_TARGET_ID) != null) {
+            readDeviceTargetId = ((BigDecimal) this.thing.getConfiguration().get(READ_DEVICE_TARGET_ID))
+                    .intValueExact();
+        } else {
+            readDeviceTargetId = null;
+        }
     }
 
     @Override
@@ -70,9 +81,11 @@ public class DaliDeviceHandler extends BaseThingHandler {
             if (CHANNEL_DIM_AT_FADE_RATE.equals(channelUID.getId())
                     || CHANNEL_DIM_IMMEDIATELY.equals(channelUID.getId())) {
                 DaliAddress address;
-                if (THING_TYPE_DEVICE.equals(this.thing.getThingTypeUID())) {
+                if (THING_TYPE_DEVICE.equals(this.thing.getThingTypeUID())
+                        || THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
                     address = DaliAddress.createShortAddress(targetId);
-                } else if (THING_TYPE_GROUP.equals(this.thing.getThingTypeUID())) {
+                } else if (THING_TYPE_GROUP.equals(this.thing.getThingTypeUID())
+                        || THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
                     address = DaliAddress.createGroupAddress(targetId);
                 } else {
                     throw new DaliException("unknown device type");
@@ -110,8 +123,12 @@ public class DaliDeviceHandler extends BaseThingHandler {
                 }
 
                 if (queryDeviceState) {
+                    DaliAddress readAddress = address;
+                    if (readDeviceTargetId != null) {
+                        readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
+                    }
                     getBridgeHandler()
-                            .sendCommandWithResponse(DaliStandardCommand.createQueryActualLevelCommand(address),
+                            .sendCommandWithResponse(DaliStandardCommand.createQueryActualLevelCommand(readAddress),
                                     DaliResponse.NumericMask.class)
                             .thenAccept(response -> {
                                 if (response != null && !response.mask) {
diff --git a/bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/handler/DaliDt8DeviceHandler.java b/bundles/org.openhab.binding.dali/src/main/java/org/openhab/binding/dali/internal/handler/DaliDt8DeviceHandler.java
new file mode 100644 (file)
index 0000000..d911161
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dali.internal.handler;
+
+import static org.openhab.binding.dali.internal.DaliBindingConstants.CHANNEL_COLOR;
+import static org.openhab.binding.dali.internal.DaliBindingConstants.CHANNEL_COLOR_TEMPERATURE;
+import static org.openhab.binding.dali.internal.DaliBindingConstants.THING_TYPE_DEVICE_DT8;
+import static org.openhab.binding.dali.internal.DaliBindingConstants.THING_TYPE_GROUP_DT8;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.dali.internal.protocol.DaliAddress;
+import org.openhab.binding.dali.internal.protocol.DaliResponse;
+import org.openhab.binding.dali.internal.protocol.DaliResponse.NumericMask;
+import org.openhab.binding.dali.internal.protocol.DaliStandardCommand;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link DaliDeviceHandler} handles commands for things of type Device and Group.
+ *
+ * @author Robert Schmid - Initial contribution
+ */
+@NonNullByDefault
+public class DaliDt8DeviceHandler extends DaliDeviceHandler {
+    private final Logger logger = LoggerFactory.getLogger(DaliDt8DeviceHandler.class);
+
+    public DaliDt8DeviceHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        try {
+            final DaliserverBridgeHandler daliHandler = getBridgeHandler();
+            if (CHANNEL_COLOR_TEMPERATURE.equals(channelUID.getId())) {
+                DaliAddress address;
+                if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
+                    address = DaliAddress.createShortAddress(targetId);
+                } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
+                    address = DaliAddress.createGroupAddress(targetId);
+                } else {
+                    throw new DaliException("unknown device type");
+                }
+                if (command instanceof DecimalType) {
+                    // Color temperature in DALI is represented in mirek ("reciprocal megakelvin")
+                    // It is one million times the reciprocal of the color temperature (in Kelvin)
+                    final int mirek = (int) (1E6f
+                            / (Math.min(Math.max(((DecimalType) command).intValue(), 1000), 20000)));
+                    final byte mirekLsb = (byte) (mirek & 0xff);
+                    final byte mirekMsb = (byte) ((mirek >> 8) & 0xff);
+                    // Write mirek value to the DTR0+DTR1 registers
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(mirekLsb));
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(mirekMsb));
+                    // Indicate that the follwing command is a DT8 (WW/CW and single-channel RGB) command
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                    // Set the color temperature to the value in DTR0+DTR1
+                    daliHandler.sendCommand(DaliStandardCommand.createSetColorTemperatureCommand(address));
+                    // Finish the command sequence
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                    daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
+
+                }
+
+                DaliAddress readAddress = address;
+                if (readDeviceTargetId != null) {
+                    readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
+                }
+                // Write argument 0x02 (query color temperature) to DTR0 and set DT8
+                daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(2));
+                daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                // Mirek MSB is returned as result
+                CompletableFuture<@Nullable NumericMask> responseMsb = daliHandler.sendCommandWithResponse(
+                        DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
+                daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                // Mirek LSB is written to DTR0
+                CompletableFuture<@Nullable NumericMask> responseLsb = daliHandler.sendCommandWithResponse(
+                        DaliStandardCommand.createQueryContentDTR0Command(readAddress), DaliResponse.NumericMask.class);
+
+                CompletableFuture.allOf(responseMsb, responseLsb).thenAccept(x -> {
+                    @Nullable
+                    NumericMask msb = responseMsb.join(), lsb = responseLsb.join();
+                    if (msb != null && !msb.mask && lsb != null && !lsb.mask) {
+                        final int msbValue = msb.value != null ? msb.value : 0;
+                        final int lsbValue = lsb.value != null ? lsb.value : 0;
+                        final int mirek = ((msbValue & 0xff) << 8) | (lsbValue & 0xff);
+                        final int kelvin = (int) (1E6f / mirek);
+                        updateState(channelUID, new DecimalType(kelvin));
+                    }
+                }).exceptionally(e -> {
+                    logger.warn("Error querying device status: {}", e.getMessage());
+                    return null;
+                });
+
+            } else if (CHANNEL_COLOR.equals(channelUID.getId())) {
+                DaliAddress address;
+                if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
+                    address = DaliAddress.createShortAddress(targetId);
+                } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
+                    address = DaliAddress.createGroupAddress(targetId);
+                } else {
+                    throw new DaliException("unknown device type");
+                }
+                if (command instanceof HSBType) {
+                    PercentType[] rgb = ((HSBType) command).toRGB();
+                    final int r = (int) (254 * (rgb[0].floatValue() / 100));
+                    final int g = (int) (254 * (rgb[1].floatValue() / 100));
+                    final int b = (int) (254 * (rgb[2].floatValue() / 100));
+                    logger.trace("RGB: {} {} {}", r, g, b);
+                    // Write RGB values to the DTR0+DTR1+DTR2 registers
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(r));
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(g));
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDTR2Command(b));
+                    // Indicate that the following command is a DT8 (WW/CW and single-channel RGB) command
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                    // Set the color to the values in DTR0+DTR1+DTR2
+                    daliHandler.sendCommand(DaliStandardCommand.createSetRgbDimlevelCommand(address));
+                    // Finish the command sequence
+                    daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                    daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
+                }
+
+                DaliAddress readAddress = address;
+                if (readDeviceTargetId != null) {
+                    readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
+                }
+                // Write argument 0xE9 (query red dimlevel) to DTR0 and set DT8
+                daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xe9));
+                daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                // Red component is returned as result
+                CompletableFuture<@Nullable NumericMask> responseRed = daliHandler.sendCommandWithResponse(
+                        DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
+                // Write argument 0xEA (query green dimlevel) to DTR0 and set DT8
+                daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xea));
+                daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                // Green component is returned as result
+                CompletableFuture<@Nullable NumericMask> responseGreen = daliHandler.sendCommandWithResponse(
+                        DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
+                // Write argument 0xEB (query blue dimlevel) to DTR0 and set DT8
+                daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xeb));
+                daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
+                // Blue component is returned as result
+                CompletableFuture<@Nullable NumericMask> responseBlue = daliHandler.sendCommandWithResponse(
+                        DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
+
+                CompletableFuture.allOf(responseRed, responseGreen, responseBlue).thenAccept(x -> {
+                    @Nullable
+                    NumericMask r = responseRed.join(), g = responseGreen.join(), b = responseBlue.join();
+                    if (r != null && !r.mask && g != null && !g.mask && b != null && !b.mask) {
+                        final int rValue = r.value != null ? r.value : 0;
+                        final int gValue = g.value != null ? g.value : 0;
+                        final int bValue = b.value != null ? b.value : 0;
+                        updateState(channelUID, HSBType.fromRGB(rValue, gValue, bValue));
+                    }
+                }).exceptionally(e -> {
+                    logger.warn("Error querying device status: {}", e.getMessage());
+                    return null;
+                });
+
+            } else {
+                super.handleCommand(channelUID, command);
+            }
+        } catch (DaliException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+        }
+    }
+}
index 7fdf593f986a98948e123e290d30cf59f0b5cfd0..8818ea76378102d8eb47ba769a26b6f51bb43b6b 100644 (file)
@@ -142,6 +142,7 @@ public class DaliserverBridgeHandler extends BaseBridgeHandler {
                 } catch (Exception e) {
                     logger.warn("Unexpected exception while sending command to daliserver: {} Message: {}", frame,
                             e.getMessage());
+                    logger.trace("Stacktrace", e);
                     future.completeExceptionally(e);
                 }
             });
@@ -161,7 +162,7 @@ public class DaliserverBridgeHandler extends BaseBridgeHandler {
             }
             byte status = response[1], rval = response[2];
             if (status == 0) {
-                result.parse(null);
+                // No return value to process.
             } else if (status == 1) {
                 result.parse(new DaliBackwardFrame(rval));
             } else if (status == 255) {
index 18ae68b05a5601fda1b5407be302c28dba46270d..c3494917b5bd74f0c0d633073c358cee2cd84e59 100644 (file)
@@ -36,10 +36,27 @@ public abstract class DaliAddress {
             protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
                 if (frame.length() == 16) {
                     frame.data &= ~(1 << 15); // unset bit 15
-                    frame.data |= ((address & 0b11111) << 9);
+                    frame.data |= ((address & 0b111111) << 9);
                 } else if (frame.length() == 24) {
                     frame.data &= ~(1 << 23); // unset bit 23
-                    frame.data |= ((address & 0b11111) << 17);
+                    frame.data |= ((address & 0b111111) << 17);
+                } else {
+                    throw new DaliException("Unsupported frame size");
+                }
+                return frame;
+            }
+        };
+    }
+
+    static DaliAddress createRawAddress(int address) {
+        return new DaliAddress() {
+            @Override
+            protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
+                // Keep all bits of the raw address
+                if (frame.length() == 16) {
+                    frame.data |= ((address & 0xff) << 8);
+                } else if (frame.length() == 24) {
+                    frame.data |= ((address & 0xff) << 16);
                 } else {
                     throw new DaliException("Unsupported frame size");
                 }
@@ -91,9 +108,9 @@ public abstract class DaliAddress {
                     if (address > 15) {
                         throw new DaliException("Groups 16..31 are not supported in 16-bit forward frames");
                     }
-                    frame.data |= ((0x4 << 3) & (address & 0b111)) << 9;
+                    frame.data |= (0x80 | ((address & 0b1111) << 1)) << 8;
                 } else if (frame.length() == 24) {
-                    frame.data |= ((0x2 << 4) & (address & 0b1111)) << 17;
+                    frame.data |= (0x80 | ((address & 0b11111) << 1)) << 16;
                 } else {
                     throw new DaliException("Unsupported frame size");
                 }
index c239b0911ee41d8a69c94738387423dd38c39454..aa7362543f377664300ea88e1c2f061c0d771bca 100644 (file)
@@ -81,7 +81,47 @@ public class DaliStandardCommand extends DaliGearCommandBase {
         return new DaliStandardCommand(target, 0x90, 0, false);
     }
 
+    public static DaliStandardCommand createQueryContentDTR0Command(DaliAddress target) throws DaliException {
+        return new DaliStandardCommand(target, 0x98, 0, false);
+    }
+
     public static DaliStandardCommand createQueryActualLevelCommand(DaliAddress target) throws DaliException {
         return new DaliStandardCommand(target, 0xa0, 0, false);
     }
+
+    public static DaliStandardCommand createActivateCommand(DaliAddress target) throws DaliException {
+        return new DaliStandardCommand(target, 0xe2, 0, false);
+    }
+
+    public static DaliStandardCommand createQueryColorValueCommand(DaliAddress target) throws DaliException {
+        return new DaliStandardCommand(target, 0xfa, 0, false);
+    }
+
+    // Global commands sent to special addresses
+
+    public static DaliStandardCommand createSetDTR0Command(int value) throws DaliException {
+        return new DaliStandardCommand(DaliAddress.createRawAddress(0xa3), value, 0, false);
+    }
+
+    public static DaliStandardCommand createSetDTR1Command(int value) throws DaliException {
+        return new DaliStandardCommand(DaliAddress.createRawAddress(0xc3), value, 0, false);
+    }
+
+    public static DaliStandardCommand createSetDTR2Command(int value) throws DaliException {
+        return new DaliStandardCommand(DaliAddress.createRawAddress(0xc5), value, 0, false);
+    }
+
+    public static DaliStandardCommand createSetDeviceTypeCommand(int value) throws DaliException {
+        return new DaliStandardCommand(DaliAddress.createRawAddress(0xc1), value, 0, false);
+    }
+
+    // DT8 (color temperature and single-channel RGB) commands
+
+    public static DaliStandardCommand createSetRgbDimlevelCommand(DaliAddress target) throws DaliException {
+        return new DaliStandardCommand(target, 0xeb, 0, false);
+    }
+
+    public static DaliStandardCommand createSetColorTemperatureCommand(DaliAddress target) throws DaliException {
+        return new DaliStandardCommand(target, 0xe7, 0, false);
+    }
 }
index 333b4dbd0bda4ac5d6c208b900e4c88580e48822..99ce34dbcd1f059cbf1a54990230ac9ad75ba2da 100644 (file)
@@ -13,6 +13,10 @@ thing-type.dali.group.label = DALI Group
 thing-type.dali.group.description = Controls a group of devices/ballasts
 thing-type.dali.rgb.label = DALI RGB Device
 thing-type.dali.rgb.description = Controls three DALI devices representing R,G,B lighting channels
+thing-type.dali.device-dt8.label = DALI DT8 Device
+thing-type.dali.device-dt8.description = Controls a single DT8 device/ballast
+thing-type.dali.group-dt8.label = DALI DT8 Group
+thing-type.dali.group-dt8.description = Controls a group of DT8 device/ballast
 
 # thing types config
 
@@ -24,6 +28,8 @@ thing-type.config.dali.device.targetId.label = Device ID
 thing-type.config.dali.device.targetId.description = Address of the device in the DALI bus
 thing-type.config.dali.group.targetId.label = Group ID
 thing-type.config.dali.group.targetId.description = Address of the group in the DALI bus
+thing-type.config.dali.group.readDeviceTargetId.label = Read Device ID
+thing-type.config.dali.group.readDeviceTargetId.description = If reading values from this group fails, you can choose to read from a single device instead.
 thing-type.config.dali.rgb.targetIdB.label = B Device ID
 thing-type.config.dali.rgb.targetIdB.description = Address of the device in the DALI bus
 thing-type.config.dali.rgb.targetIdG.label = G Device ID
index 14da66f0f001b98faeb5cf5467db99ae7dd81855..1c13dbdfc50ba9a8a84c7ec44c6b5c2ca6bc64f1 100644 (file)
                                <label>Group ID</label>
                                <description>Address of the group in the DALI bus</description>
                        </parameter>
+                       <parameter name="readDeviceTargetId" type="integer" required="false" min="0" max="63">
+                               <label>Read Device ID</label>
+                               <description>
+                                       If reading values from this group fails, you can choose to read from a single device instead.
+                               </description>
+                       </parameter>
                </config-description>
        </thing-type>
 
                </config-description>
        </thing-type>
 
+       <!-- Single DT8 Device Type -->
+       <thing-type id="device-dt8">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="daliserver"/>
+               </supported-bridge-type-refs>
+               <label>DALI DT8 Device</label>
+               <description>Controls a single DT8 device/ballast</description>
+               <channels>
+                       <channel id="dimAtFadeRate" typeId="system.brightness"/>
+                       <channel id="dimImmediately" typeId="system.brightness"/>
+                       <channel id="color" typeId="system.color"/>
+                       <channel id="color-temperature-abs" typeId="system.color-temperature-abs"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="targetId" type="integer" required="true" min="0" max="63">
+                               <label>Device ID</label>
+                               <description>Address of the device in the DALI bus</description>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <!-- Group DT8 Device Type -->
+       <thing-type id="group-dt8">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="daliserver"/>
+               </supported-bridge-type-refs>
+               <label>DALI DT8 Group</label>
+               <description>Controls a DT8 group of devices/ballasts</description>
+               <channels>
+                       <channel id="dimAtFadeRate" typeId="system.brightness"/>
+                       <channel id="dimImmediately" typeId="system.brightness"/>
+                       <channel id="color" typeId="system.color"/>
+                       <channel id="color-temperature-abs" typeId="system.color-temperature-abs"/>
+               </channels>
+               <config-description>
+                       <parameter name="targetId" type="integer" required="true" min="0" max="31">
+                               <label>Group ID</label>
+                               <description>Address of the group in the DALI bus</description>
+                       </parameter>
+                       <parameter name="readDeviceTargetId" type="integer" required="false" min="0" max="63">
+                               <label>Read Device ID</label>
+                               <description>
+                                       If reading values from this group fails, you can choose to read from a single device instead.
+                               </description>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
 </thing:thing-descriptions>