]> git.basschouten.com Git - openhab-addons.git/blob
00c2fe9d0212914e3543a04c8b4aeab9d167b446
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.dali.internal.handler;
14
15 import static org.openhab.binding.dali.internal.DaliBindingConstants.CHANNEL_COLOR;
16 import static org.openhab.binding.dali.internal.DaliBindingConstants.CHANNEL_COLOR_TEMPERATURE;
17 import static org.openhab.binding.dali.internal.DaliBindingConstants.THING_TYPE_DEVICE_DT8;
18 import static org.openhab.binding.dali.internal.DaliBindingConstants.THING_TYPE_GROUP_DT8;
19
20 import java.util.concurrent.CompletableFuture;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.dali.internal.protocol.DaliAddress;
25 import org.openhab.binding.dali.internal.protocol.DaliResponse;
26 import org.openhab.binding.dali.internal.protocol.DaliResponse.NumericMask;
27 import org.openhab.binding.dali.internal.protocol.DaliStandardCommand;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.HSBType;
30 import org.openhab.core.library.types.PercentType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.types.Command;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link DaliDeviceHandler} handles commands for things of type Device and Group.
43  *
44  * @author Robert Schmid - Initial contribution
45  */
46 @NonNullByDefault
47 public class DaliDt8DeviceHandler extends DaliDeviceHandler {
48     private final Logger logger = LoggerFactory.getLogger(DaliDt8DeviceHandler.class);
49
50     public DaliDt8DeviceHandler(Thing thing) {
51         super(thing);
52     }
53
54     @Override
55     public void handleCommand(ChannelUID channelUID, Command command) {
56         try {
57             final DaliserverBridgeHandler daliHandler = getBridgeHandler();
58             if (CHANNEL_COLOR_TEMPERATURE.equals(channelUID.getId())) {
59                 DaliAddress address;
60                 if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
61                     address = DaliAddress.createShortAddress(targetId);
62                 } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
63                     address = DaliAddress.createGroupAddress(targetId);
64                 } else {
65                     throw new DaliException("unknown device type");
66                 }
67                 int mirek;
68                 if (command instanceof DecimalType) {
69                     // Color temperature in DALI is represented in mirek ("reciprocal megakelvin")
70                     // It is one million times the reciprocal of the color temperature (in Kelvin)
71                     mirek = (int) (1E6f / (Math.min(Math.max(((DecimalType) command).intValue(), 1000), 20000)));
72                 } else if (command instanceof QuantityType) {
73                     // ensure it's in the correct units
74                     QuantityType<?> commandQuantity = ((QuantityType) command).toInvertibleUnit(Units.MIRED);
75                     if (commandQuantity == null) {
76                         logger.warn("Unable to convert command {} to mireks", command);
77                         return;
78                     }
79                     mirek = commandQuantity.intValue();
80                 } else {
81                     logger.warn("Unable to convert command {} to mireks", command);
82                     return;
83                 }
84
85                 final byte mirekLsb = (byte) (mirek & 0xff);
86                 final byte mirekMsb = (byte) ((mirek >> 8) & 0xff);
87                 // Write mirek value to the DTR0+DTR1 registers
88                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(mirekLsb));
89                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(mirekMsb));
90                 // Indicate that the follwing command is a DT8 (WW/CW and single-channel RGB) command
91                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
92                 // Set the color temperature to the value in DTR0+DTR1
93                 daliHandler.sendCommand(DaliStandardCommand.createSetColorTemperatureCommand(address));
94                 // Finish the command sequence
95                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
96                 daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
97
98                 DaliAddress readAddress = address;
99                 if (readDeviceTargetId != null) {
100                     readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
101                 }
102                 // Write argument 0xc2 (query temporary color temperature) to DTR0 and set DT8
103                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xc2));
104                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
105                 // Mirek MSB is returned as result
106                 CompletableFuture<@Nullable NumericMask> responseMsb = daliHandler.sendCommandWithResponse(
107                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
108                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
109                 // Mirek LSB is written to DTR0
110                 CompletableFuture<@Nullable NumericMask> responseLsb = daliHandler.sendCommandWithResponse(
111                         DaliStandardCommand.createQueryContentDTR0Command(readAddress), DaliResponse.NumericMask.class);
112
113                 CompletableFuture.allOf(responseMsb, responseLsb).thenAccept(x -> {
114                     @Nullable
115                     NumericMask msb = responseMsb.join(), lsb = responseLsb.join();
116                     if (msb != null && !msb.mask && lsb != null && !lsb.mask) {
117                         final int msbValue = msb.value != null ? msb.value : 0;
118                         final int lsbValue = lsb.value != null ? lsb.value : 0;
119                         final int mirekState = ((msbValue & 0xff) << 8) | (lsbValue & 0xff);
120                         final int kelvin = (int) (1E6f / mirekState);
121                         updateState(channelUID, new QuantityType(kelvin, Units.KELVIN));
122                     }
123                 }).exceptionally(e -> {
124                     logger.warn("Error querying device status: {}", e.getMessage());
125                     return null;
126                 });
127
128             } else if (CHANNEL_COLOR.equals(channelUID.getId())) {
129                 DaliAddress address;
130                 if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
131                     address = DaliAddress.createShortAddress(targetId);
132                 } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
133                     address = DaliAddress.createGroupAddress(targetId);
134                 } else {
135                     throw new DaliException("unknown device type");
136                 }
137                 if (command instanceof HSBType) {
138                     PercentType[] rgb = ((HSBType) command).toRGB();
139                     final int r = (int) (254 * (rgb[0].floatValue() / 100));
140                     final int g = (int) (254 * (rgb[1].floatValue() / 100));
141                     final int b = (int) (254 * (rgb[2].floatValue() / 100));
142                     logger.trace("RGB: {} {} {}", r, g, b);
143                     // Write RGB values to the DTR0+DTR1+DTR2 registers
144                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(r));
145                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(g));
146                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR2Command(b));
147                     // Indicate that the following command is a DT8 (WW/CW and single-channel RGB) command
148                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
149                     // Set the color to the values in DTR0+DTR1+DTR2
150                     daliHandler.sendCommand(DaliStandardCommand.createSetRgbDimlevelCommand(address));
151                     // Finish the command sequence
152                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
153                     daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
154                 }
155
156                 DaliAddress readAddress = address;
157                 if (readDeviceTargetId != null) {
158                     readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
159                 }
160                 // Write argument 0xE9 (query red dimlevel) to DTR0 and set DT8
161                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xe9));
162                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
163                 // Red component is returned as result
164                 CompletableFuture<@Nullable NumericMask> responseRed = daliHandler.sendCommandWithResponse(
165                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
166                 // Write argument 0xEA (query green dimlevel) to DTR0 and set DT8
167                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xea));
168                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
169                 // Green component is returned as result
170                 CompletableFuture<@Nullable NumericMask> responseGreen = daliHandler.sendCommandWithResponse(
171                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
172                 // Write argument 0xEB (query blue dimlevel) to DTR0 and set DT8
173                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xeb));
174                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
175                 // Blue component is returned as result
176                 CompletableFuture<@Nullable NumericMask> responseBlue = daliHandler.sendCommandWithResponse(
177                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
178
179                 CompletableFuture.allOf(responseRed, responseGreen, responseBlue).thenAccept(x -> {
180                     @Nullable
181                     NumericMask r = responseRed.join(), g = responseGreen.join(), b = responseBlue.join();
182                     if (r != null && !r.mask && g != null && !g.mask && b != null && !b.mask) {
183                         final int rValue = r.value != null ? r.value : 0;
184                         final int gValue = g.value != null ? g.value : 0;
185                         final int bValue = b.value != null ? b.value : 0;
186                         updateState(channelUID, HSBType.fromRGB(rValue, gValue, bValue));
187                     }
188                 }).exceptionally(e -> {
189                     logger.warn("Error querying device status: {}", e.getMessage());
190                     return null;
191                 });
192
193             } else {
194                 super.handleCommand(channelUID, command);
195             }
196         } catch (DaliException e) {
197             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
198         }
199     }
200 }