]> git.basschouten.com Git - openhab-addons.git/blob
36e82641a011fbe2913279ecf5d2903fb620fd35
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.types.Command;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link DaliDeviceHandler} handles commands for things of type Device and Group.
41  *
42  * @author Robert Schmid - Initial contribution
43  */
44 @NonNullByDefault
45 public class DaliDt8DeviceHandler extends DaliDeviceHandler {
46     private final Logger logger = LoggerFactory.getLogger(DaliDt8DeviceHandler.class);
47
48     public DaliDt8DeviceHandler(Thing thing) {
49         super(thing);
50     }
51
52     @Override
53     public void handleCommand(ChannelUID channelUID, Command command) {
54         try {
55             final DaliserverBridgeHandler daliHandler = getBridgeHandler();
56             if (CHANNEL_COLOR_TEMPERATURE.equals(channelUID.getId())) {
57                 DaliAddress address;
58                 if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
59                     address = DaliAddress.createShortAddress(targetId);
60                 } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
61                     address = DaliAddress.createGroupAddress(targetId);
62                 } else {
63                     throw new DaliException("unknown device type");
64                 }
65                 if (command instanceof DecimalType) {
66                     // Color temperature in DALI is represented in mirek ("reciprocal megakelvin")
67                     // It is one million times the reciprocal of the color temperature (in Kelvin)
68                     final int mirek = (int) (1E6f
69                             / (Math.min(Math.max(((DecimalType) command).intValue(), 1000), 20000)));
70                     final byte mirekLsb = (byte) (mirek & 0xff);
71                     final byte mirekMsb = (byte) ((mirek >> 8) & 0xff);
72                     // Write mirek value to the DTR0+DTR1 registers
73                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(mirekLsb));
74                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(mirekMsb));
75                     // Indicate that the follwing command is a DT8 (WW/CW and single-channel RGB) command
76                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
77                     // Set the color temperature to the value in DTR0+DTR1
78                     daliHandler.sendCommand(DaliStandardCommand.createSetColorTemperatureCommand(address));
79                     // Finish the command sequence
80                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
81                     daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
82
83                 }
84
85                 DaliAddress readAddress = address;
86                 if (readDeviceTargetId != null) {
87                     readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
88                 }
89                 // Write argument 0xc2 (query temporary color temperature) to DTR0 and set DT8
90                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xc2));
91                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
92                 // Mirek MSB is returned as result
93                 CompletableFuture<@Nullable NumericMask> responseMsb = daliHandler.sendCommandWithResponse(
94                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
95                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
96                 // Mirek LSB is written to DTR0
97                 CompletableFuture<@Nullable NumericMask> responseLsb = daliHandler.sendCommandWithResponse(
98                         DaliStandardCommand.createQueryContentDTR0Command(readAddress), DaliResponse.NumericMask.class);
99
100                 CompletableFuture.allOf(responseMsb, responseLsb).thenAccept(x -> {
101                     @Nullable
102                     NumericMask msb = responseMsb.join(), lsb = responseLsb.join();
103                     if (msb != null && !msb.mask && lsb != null && !lsb.mask) {
104                         final int msbValue = msb.value != null ? msb.value : 0;
105                         final int lsbValue = lsb.value != null ? lsb.value : 0;
106                         final int mirek = ((msbValue & 0xff) << 8) | (lsbValue & 0xff);
107                         final int kelvin = (int) (1E6f / mirek);
108                         updateState(channelUID, new DecimalType(kelvin));
109                     }
110                 }).exceptionally(e -> {
111                     logger.warn("Error querying device status: {}", e.getMessage());
112                     return null;
113                 });
114
115             } else if (CHANNEL_COLOR.equals(channelUID.getId())) {
116                 DaliAddress address;
117                 if (THING_TYPE_DEVICE_DT8.equals(this.thing.getThingTypeUID())) {
118                     address = DaliAddress.createShortAddress(targetId);
119                 } else if (THING_TYPE_GROUP_DT8.equals(this.thing.getThingTypeUID())) {
120                     address = DaliAddress.createGroupAddress(targetId);
121                 } else {
122                     throw new DaliException("unknown device type");
123                 }
124                 if (command instanceof HSBType) {
125                     PercentType[] rgb = ((HSBType) command).toRGB();
126                     final int r = (int) (254 * (rgb[0].floatValue() / 100));
127                     final int g = (int) (254 * (rgb[1].floatValue() / 100));
128                     final int b = (int) (254 * (rgb[2].floatValue() / 100));
129                     logger.trace("RGB: {} {} {}", r, g, b);
130                     // Write RGB values to the DTR0+DTR1+DTR2 registers
131                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(r));
132                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR1Command(g));
133                     daliHandler.sendCommand(DaliStandardCommand.createSetDTR2Command(b));
134                     // Indicate that the following command is a DT8 (WW/CW and single-channel RGB) command
135                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
136                     // Set the color to the values in DTR0+DTR1+DTR2
137                     daliHandler.sendCommand(DaliStandardCommand.createSetRgbDimlevelCommand(address));
138                     // Finish the command sequence
139                     daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
140                     daliHandler.sendCommand(DaliStandardCommand.createActivateCommand(address));
141                 }
142
143                 DaliAddress readAddress = address;
144                 if (readDeviceTargetId != null) {
145                     readAddress = DaliAddress.createShortAddress(readDeviceTargetId);
146                 }
147                 // Write argument 0xE9 (query red dimlevel) to DTR0 and set DT8
148                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xe9));
149                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
150                 // Red component is returned as result
151                 CompletableFuture<@Nullable NumericMask> responseRed = daliHandler.sendCommandWithResponse(
152                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
153                 // Write argument 0xEA (query green dimlevel) to DTR0 and set DT8
154                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xea));
155                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
156                 // Green component is returned as result
157                 CompletableFuture<@Nullable NumericMask> responseGreen = daliHandler.sendCommandWithResponse(
158                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
159                 // Write argument 0xEB (query blue dimlevel) to DTR0 and set DT8
160                 daliHandler.sendCommand(DaliStandardCommand.createSetDTR0Command(0xeb));
161                 daliHandler.sendCommand(DaliStandardCommand.createSetDeviceTypeCommand(8));
162                 // Blue component is returned as result
163                 CompletableFuture<@Nullable NumericMask> responseBlue = daliHandler.sendCommandWithResponse(
164                         DaliStandardCommand.createQueryColorValueCommand(readAddress), DaliResponse.NumericMask.class);
165
166                 CompletableFuture.allOf(responseRed, responseGreen, responseBlue).thenAccept(x -> {
167                     @Nullable
168                     NumericMask r = responseRed.join(), g = responseGreen.join(), b = responseBlue.join();
169                     if (r != null && !r.mask && g != null && !g.mask && b != null && !b.mask) {
170                         final int rValue = r.value != null ? r.value : 0;
171                         final int gValue = g.value != null ? g.value : 0;
172                         final int bValue = b.value != null ? b.value : 0;
173                         updateState(channelUID, HSBType.fromRGB(rValue, gValue, bValue));
174                     }
175                 }).exceptionally(e -> {
176                     logger.warn("Error querying device status: {}", e.getMessage());
177                     return null;
178                 });
179
180             } else {
181                 super.handleCommand(channelUID, command);
182             }
183         } catch (DaliException e) {
184             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
185         }
186     }
187 }