]> git.basschouten.com Git - openhab-addons.git/blob
91457b76ed529e345172da0ab5c431d7ba9a7471
[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.dmx.internal.handler;
14
15 import static org.openhab.binding.dmx.internal.DmxBindingConstants.*;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.stream.IntStream;
22
23 import org.openhab.binding.dmx.internal.DmxBindingConstants.ListenerType;
24 import org.openhab.binding.dmx.internal.DmxBridgeHandler;
25 import org.openhab.binding.dmx.internal.DmxThingHandler;
26 import org.openhab.binding.dmx.internal.Util;
27 import org.openhab.binding.dmx.internal.ValueSet;
28 import org.openhab.binding.dmx.internal.action.FadeAction;
29 import org.openhab.binding.dmx.internal.config.ColorThingHandlerConfiguration;
30 import org.openhab.binding.dmx.internal.multiverse.BaseDmxChannel;
31 import org.openhab.binding.dmx.internal.multiverse.DmxChannel;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.HSBType;
34 import org.openhab.core.library.types.IncreaseDecreaseType;
35 import org.openhab.core.library.types.OnOffType;
36 import org.openhab.core.library.types.PercentType;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.ThingTypeUID;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * The {@link ColorThingHandler} is responsible for handling commands, which are
50  * sent to the dimmer.
51  *
52  * @author Jan N. Klug - Initial contribution
53  */
54
55 public class ColorThingHandler extends DmxThingHandler {
56     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_COLOR);
57
58     private final Logger logger = LoggerFactory.getLogger(ColorThingHandler.class);
59
60     private final List<DmxChannel> channels = new ArrayList<>();
61
62     private final List<Integer> currentValues = new ArrayList<>();
63     private HSBType currentColor = new HSBType();
64
65     private ValueSet turnOnValue = new ValueSet(0, -1, DmxChannel.MAX_VALUE);
66     private ValueSet turnOffValue = new ValueSet(0, -1, DmxChannel.MIN_VALUE);
67
68     private int fadeTime = 0;
69     private int dimTime = 0;
70
71     private boolean dynamicTurnOnValue = false;
72     private boolean isDimming = false;
73
74     public ColorThingHandler(Thing dimmerThing) {
75         super(dimmerThing);
76     }
77
78     @Override
79     public void handleCommand(ChannelUID channelUID, Command command) {
80         logger.trace("received command {} in channel {}", command, channelUID);
81         ValueSet targetValueSet = new ValueSet(fadeTime, -1);
82         switch (channelUID.getId()) {
83             case CHANNEL_BRIGHTNESS_R:
84                 if (command instanceof RefreshType) {
85                     logger.trace("sending update on refresh to channel {}:brightness_r", this.thing.getUID());
86                     currentValues.set(0, channels.get(0).getValue());
87                     updateCurrentColor();
88                     updateState(channelUID, Util.toPercentValue(currentValues.get(0)));
89                     return;
90                 } else {
91                     logger.debug("command {} not supported in channel {}:brightness_r", command.getClass(),
92                             this.thing.getUID());
93                     return;
94                 }
95             case CHANNEL_BRIGHTNESS_G:
96                 if (command instanceof RefreshType) {
97                     logger.trace("sending update on refresh to channel {}:brightness_g", this.thing.getUID());
98                     currentValues.set(1, channels.get(1).getValue());
99                     updateCurrentColor();
100                     updateState(channelUID, Util.toPercentValue(currentValues.get(1)));
101                     return;
102                 } else {
103                     logger.debug("command {} not supported in channel {}:brightness_g", command.getClass(),
104                             this.thing.getUID());
105                     return;
106                 }
107             case CHANNEL_BRIGHTNESS_B:
108                 if (command instanceof RefreshType) {
109                     logger.trace("sending update on refresh to channel {}:brightness_b", this.thing.getUID());
110                     currentValues.set(2, channels.get(2).getValue());
111                     updateCurrentColor();
112                     updateState(channelUID, Util.toPercentValue(currentValues.get(2)));
113                     return;
114                 } else {
115                     logger.debug("command {} not supported in channel {}:brightness_b", command.getClass(),
116                             this.thing.getUID());
117                     return;
118                 }
119             case CHANNEL_COLOR: {
120                 if (command instanceof OnOffType) {
121                     logger.trace("adding {} fade to channels in thing {}", command, this.thing.getUID());
122                     if (((OnOffType) command) == OnOffType.ON) {
123                         targetValueSet = turnOnValue;
124                     } else {
125                         if (dynamicTurnOnValue) {
126                             turnOnValue.clear();
127                             for (DmxChannel channel : channels) {
128                                 turnOnValue.addValue(channel.getValue());
129                             }
130                             logger.trace("stored channel values fort next turn-on");
131                         }
132                         targetValueSet = turnOffValue;
133                     }
134                 } else if (command instanceof HSBType) {
135                     logger.trace("adding color fade to channels in thing {}", this.thing.getUID());
136                     targetValueSet.addValue(((HSBType) command).getRed());
137                     targetValueSet.addValue(((HSBType) command).getGreen());
138                     targetValueSet.addValue(((HSBType) command).getBlue());
139                 } else if ((command instanceof PercentType) || (command instanceof DecimalType)) {
140                     logger.trace("adding brightness fade to channels in thing {}", this.thing.getUID());
141                     PercentType brightness = (command instanceof PercentType) ? (PercentType) command
142                             : Util.toPercentValue(((DecimalType) command).intValue());
143                     HSBType targetColor = new HSBType(currentColor.getHue(), currentColor.getSaturation(), brightness);
144                     targetValueSet.addValue(targetColor.getRed());
145                     targetValueSet.addValue(targetColor.getGreen());
146                     targetValueSet.addValue(targetColor.getBlue());
147                 } else if (command instanceof IncreaseDecreaseType) {
148                     if (isDimming && ((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
149                         logger.trace("stopping fade in thing {}", this.thing.getUID());
150                         channels.forEach(DmxChannel::clearAction);
151                         isDimming = false;
152                         return;
153                     } else {
154                         logger.trace("starting {} fade in thing {}", command, this.thing.getUID());
155                         HSBType targetColor;
156                         if (((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
157                             targetColor = new HSBType(currentColor.getHue(), currentColor.getSaturation(),
158                                     PercentType.HUNDRED);
159                         } else {
160                             targetColor = new HSBType(currentColor.getHue(), currentColor.getSaturation(),
161                                     PercentType.ZERO);
162                         }
163                         targetValueSet.addValue(targetColor.getRed());
164                         targetValueSet.addValue(targetColor.getGreen());
165                         targetValueSet.addValue(targetColor.getBlue());
166                         targetValueSet.setFadeTime(dimTime);
167                         isDimming = true;
168                     }
169                 } else if (command instanceof RefreshType) {
170                     logger.trace("sending update on refresh to channel {}:color", this.thing.getUID());
171                     currentValues.set(0, channels.get(0).getValue());
172                     currentValues.set(1, channels.get(1).getValue());
173                     currentValues.set(2, channels.get(2).getValue());
174                     updateCurrentColor();
175                     updateState(channelUID, currentColor);
176                     return;
177                 } else {
178                     logger.debug("command {} not supported in channel {}:color", command.getClass(),
179                             this.thing.getUID());
180                     return;
181                 }
182                 break;
183             }
184             default:
185                 logger.debug("channel {} not supported in thing {}", channelUID.getId(), this.thing.getUID());
186                 return;
187         }
188         final ValueSet valueSet = targetValueSet;
189         IntStream.range(0, channels.size()).forEach(i -> {
190             channels.get(i).setChannelAction(new FadeAction(valueSet.getFadeTime(), channels.get(i).getValue(),
191                     valueSet.getValue(i), valueSet.getHoldTime()));
192         });
193     }
194
195     @Override
196     public void initialize() {
197         Bridge bridge = getBridge();
198         DmxBridgeHandler bridgeHandler;
199         if (bridge == null) {
200             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "no bridge assigned");
201             dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
202             return;
203         } else {
204             bridgeHandler = (DmxBridgeHandler) bridge.getHandler();
205             if (bridgeHandler == null) {
206                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "no bridge handler available");
207                 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
208                 return;
209             }
210         }
211
212         ColorThingHandlerConfiguration configuration = getConfig().as(ColorThingHandlerConfiguration.class);
213         if (configuration.dmxid.isEmpty()) {
214             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
215                     "DMX channel configuration missing");
216             dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
217             return;
218         }
219         try {
220             List<BaseDmxChannel> configChannels = BaseDmxChannel.fromString(configuration.dmxid,
221                     bridgeHandler.getUniverseId());
222             logger.trace("found {} channels in {}", configChannels.size(), this.thing.getUID());
223             for (BaseDmxChannel channel : configChannels) {
224                 channels.add(bridgeHandler.getDmxChannel(channel, this.thing));
225             }
226         } catch (IllegalArgumentException e) {
227             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
228             dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
229             return;
230         }
231
232         currentValues.add(DmxChannel.MIN_VALUE);
233         currentValues.add(DmxChannel.MIN_VALUE);
234         currentValues.add(DmxChannel.MIN_VALUE);
235
236         fadeTime = configuration.fadetime;
237         logger.trace("setting fadeTime to {} ms in {}", fadeTime, this.thing.getUID());
238
239         dimTime = configuration.dimtime;
240         logger.trace("setting dimTime to {} ms in {}", fadeTime, this.thing.getUID());
241
242         String turnOnValueString = String.valueOf(fadeTime) + ":" + configuration.turnonvalue + ":-1";
243         ValueSet turnOnValue = ValueSet.fromString(turnOnValueString);
244         if (turnOnValue.size() % 3 == 0) {
245             this.turnOnValue = turnOnValue;
246             logger.trace("set turnonvalue to {} in {}", turnOnValue, this.thing.getUID());
247         } else {
248             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "turn-on value malformed");
249             dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
250             return;
251         }
252         this.turnOnValue.setFadeTime(fadeTime);
253
254         dynamicTurnOnValue = configuration.dynamicturnonvalue;
255
256         String turnOffValueString = String.valueOf(fadeTime) + ":" + configuration.turnoffvalue + ":-1";
257         ValueSet turnOffValue = ValueSet.fromString(turnOffValueString);
258         if (turnOffValue.size() % 3 == 0) {
259             this.turnOffValue = turnOffValue;
260             logger.trace("set turnoffvalue to {} in {}", turnOffValue, this.thing.getUID());
261         } else {
262             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "turn-off value malformed");
263             dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
264             return;
265         }
266
267         this.turnOffValue.setFadeTime(fadeTime);
268
269         // register feedback listeners
270         channels.get(0).addListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_R), this,
271                 ListenerType.VALUE);
272         channels.get(1).addListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_G), this,
273                 ListenerType.VALUE);
274         channels.get(2).addListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_B), this,
275                 ListenerType.VALUE);
276
277         if (bridge.getStatus().equals(ThingStatus.ONLINE)) {
278             updateStatus(ThingStatus.ONLINE);
279             dmxHandlerStatus = ThingStatusDetail.NONE;
280         } else {
281             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
282         }
283     }
284
285     @Override
286     public void dispose() {
287         if (!channels.isEmpty()) {
288             channels.get(0).removeListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_R));
289             channels.get(1).removeListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_G));
290             channels.get(2).removeListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS_B));
291         }
292         channels.clear();
293         currentValues.clear();
294         currentColor = new HSBType();
295     }
296
297     @Override
298     public void updateChannelValue(ChannelUID channelUID, int value) {
299         updateState(channelUID, Util.toPercentValue(value));
300         switch (channelUID.getId()) {
301             case CHANNEL_BRIGHTNESS_R:
302                 currentValues.set(0, value);
303                 break;
304             case CHANNEL_BRIGHTNESS_G:
305                 currentValues.set(1, value);
306                 break;
307             case CHANNEL_BRIGHTNESS_B:
308                 currentValues.set(2, value);
309                 break;
310             default:
311                 logger.debug("don't know how to handle {} in RGB type", channelUID.getId());
312                 return;
313         }
314         updateCurrentColor();
315         updateState(new ChannelUID(this.thing.getUID(), CHANNEL_COLOR), currentColor);
316         logger.trace("received update {} in channel {}, result is {}", value, channelUID, currentColor);
317     }
318
319     private void updateCurrentColor() {
320         currentColor = HSBType.fromRGB(currentValues.get(0), currentValues.get(1), currentValues.get(2));
321     }
322 }