2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.dmx.internal.handler;
15 import static org.openhab.binding.dmx.internal.DmxBindingConstants.*;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
21 import java.util.stream.IntStream;
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.DimmerThingHandlerConfiguration;
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.IncreaseDecreaseType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.PercentType;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingTypeUID;
42 import org.openhab.core.types.Command;
43 import org.openhab.core.types.RefreshType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link DimmerThingHandler} is responsible for handling commands, which are
51 * @author Jan N. Klug - Initial contribution
54 public class DimmerThingHandler extends DmxThingHandler {
55 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DIMMER);
57 private final Logger logger = LoggerFactory.getLogger(DimmerThingHandler.class);
59 private final List<DmxChannel> channels = new ArrayList<>();
61 private PercentType currentBrightness = PercentType.ZERO;
63 private ValueSet turnOnValue = new ValueSet(0, -1, DmxChannel.MAX_VALUE);
64 private ValueSet turnOffValue = new ValueSet(0, -1, DmxChannel.MIN_VALUE);
66 private int fadeTime = 0, dimTime = 0;
68 private boolean dynamicTurnOnValue = false;
69 private boolean isDimming = false;
71 public DimmerThingHandler(Thing dimmerThing) {
76 public void handleCommand(ChannelUID channelUID, Command command) {
77 logger.trace("received command {} in channel {}", command, channelUID);
78 ValueSet targetValueSet = new ValueSet(fadeTime, -1);
79 switch (channelUID.getId()) {
80 case CHANNEL_BRIGHTNESS: {
81 if (command instanceof PercentType || command instanceof DecimalType) {
82 PercentType brightness = (command instanceof PercentType) ? (PercentType) command
83 : Util.toPercentValue(((DecimalType) command).intValue());
84 logger.trace("adding fade to channels in thing {}", this.thing.getUID());
85 targetValueSet.addValue(brightness);
86 } else if (command instanceof OnOffType) {
87 logger.trace("adding {} fade to channels in thing {}", command, this.thing.getUID());
88 if (((OnOffType) command) == OnOffType.ON) {
89 targetValueSet = turnOnValue;
91 if (dynamicTurnOnValue) {
93 for (DmxChannel channel : channels) {
94 turnOnValue.addValue(channel.getValue());
96 logger.trace("stored channel values fort next turn-on");
98 targetValueSet = turnOffValue;
100 } else if (command instanceof IncreaseDecreaseType) {
101 if (isDimming && ((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
102 logger.trace("stopping fade in thing {}", this.thing.getUID());
103 channels.forEach(DmxChannel::clearAction);
107 logger.trace("starting {} fade in thing {}", command, this.thing.getUID());
108 targetValueSet = ((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)
111 targetValueSet.setFadeTime(dimTime);
114 } else if (command instanceof RefreshType) {
115 logger.trace("sending update on refresh to channel {}:brightness", this.thing.getUID());
116 currentBrightness = Util.toPercentValue(channels.get(0).getValue());
117 updateState(channelUID, currentBrightness);
120 logger.debug("command {} not supported in channel {}:brightness", command.getClass(),
121 this.thing.getUID());
127 logger.debug("channel {} not supported in thing {}", channelUID.getId(), this.thing.getUID());
130 final ValueSet valueSet = targetValueSet;
131 IntStream.range(0, channels.size()).forEach(i -> {
132 channels.get(i).setChannelAction(new FadeAction(valueSet.getFadeTime(), channels.get(i).getValue(),
133 valueSet.getValue(i), valueSet.getHoldTime()));
138 public void initialize() {
139 Bridge bridge = getBridge();
140 DmxBridgeHandler bridgeHandler;
141 if (bridge == null) {
142 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "no bridge assigned");
143 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
146 bridgeHandler = (DmxBridgeHandler) bridge.getHandler();
147 if (bridgeHandler == null) {
148 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "no bridge handler available");
149 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
154 DimmerThingHandlerConfiguration configuration = getConfig().as(DimmerThingHandlerConfiguration.class);
156 if (configuration.dmxid.isEmpty()) {
157 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
158 "DMX channel configuration missing");
159 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
163 List<BaseDmxChannel> configChannels = BaseDmxChannel.fromString(configuration.dmxid,
164 bridgeHandler.getUniverseId());
165 logger.trace("found {} channels in {}", configChannels.size(), this.thing.getUID());
166 for (BaseDmxChannel channel : configChannels) {
167 channels.add(bridgeHandler.getDmxChannel(channel, this.thing));
169 } catch (IllegalArgumentException e) {
170 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
171 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
175 fadeTime = configuration.fadetime;
176 logger.trace("setting fadeTime to {} ms in {}", fadeTime, this.thing.getUID());
178 dimTime = configuration.dimtime;
179 logger.trace("setting dimTime to {} ms in {}", fadeTime, this.thing.getUID());
181 String turnOnValueString = String.valueOf(fadeTime) + ":" + configuration.turnonvalue + ":-1";
183 ValueSet turnOnValue = ValueSet.fromString(turnOnValueString);
184 if (!turnOnValue.isEmpty()) {
185 this.turnOnValue = turnOnValue;
186 logger.trace("set turnonvalue to {} in {}", turnOnValue, this.thing.getUID());
188 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "turn-on value malformed");
189 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
193 this.turnOnValue.setFadeTime(fadeTime);
195 dynamicTurnOnValue = configuration.dynamicturnonvalue;
197 String turnOffValueString = String.valueOf(fadeTime) + ":" + configuration.turnoffvalue + ":-1";
198 ValueSet turnOffValue = ValueSet.fromString(turnOffValueString);
199 if (!turnOffValue.isEmpty()) {
200 this.turnOffValue = turnOffValue;
201 logger.trace("set turnoffvalue to {} in {}", turnOffValue, this.thing.getUID());
203 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "turn-off value malformed");
204 dmxHandlerStatus = ThingStatusDetail.CONFIGURATION_ERROR;
208 this.turnOffValue.setFadeTime(fadeTime);
210 // register feedback listener
211 channels.get(0).addListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS), this, ListenerType.VALUE);
213 if (bridge.getStatus().equals(ThingStatus.ONLINE)) {
214 updateStatus(ThingStatus.ONLINE);
215 dmxHandlerStatus = ThingStatusDetail.NONE;
217 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
222 public void dispose() {
223 if (!channels.isEmpty()) {
224 channels.get(0).removeListener(new ChannelUID(this.thing.getUID(), CHANNEL_BRIGHTNESS));
226 Bridge bridge = getBridge();
227 if (bridge != null) {
228 DmxBridgeHandler bridgeHandler = (DmxBridgeHandler) bridge.getHandler();
229 if (bridgeHandler != null) {
230 bridgeHandler.unregisterDmxChannels(this.thing);
231 logger.debug("removing {} channels from {}", channels.size(), this.thing.getUID());
239 public void updateChannelValue(ChannelUID channelUID, int value) {
240 updateState(channelUID, Util.toPercentValue(value));
241 if (channelUID.getId().equals(CHANNEL_BRIGHTNESS)) {
242 currentBrightness = Util.toPercentValue(value);
244 logger.debug("don't know how to handle {}", channelUID.getId());
247 logger.trace("received update {} in channel {}, resulting in brightness={}", value, channelUID,