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.tradfri.internal.handler;
15 import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.tradfri.internal.TradfriCoapClient;
20 import org.openhab.binding.tradfri.internal.model.TradfriLightData;
21 import org.openhab.core.library.types.HSBType;
22 import org.openhab.core.library.types.IncreaseDecreaseType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.Thing;
27 import org.openhab.core.thing.ThingStatus;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.RefreshType;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.gson.JsonElement;
36 * The {@link TradfriLightHandler} is responsible for handling commands for individual lights.
38 * @author Kai Kreuzer - Initial contribution
39 * @author Holger Reichert - Support for color bulbs
40 * @author Christoph Weitkamp - Restructuring and refactoring of the binding
43 public class TradfriLightHandler extends TradfriThingHandler {
45 private final Logger logger = LoggerFactory.getLogger(TradfriLightHandler.class);
47 // step size for increase/decrease commands
48 private static final int STEP = 10;
50 // keeps track of the current state for handling of increase/decrease
51 private @Nullable TradfriLightData state;
53 public TradfriLightHandler(Thing thing) {
58 public void onUpdate(JsonElement data) {
59 if (active && !(data.isJsonNull())) {
60 TradfriLightData state = new TradfriLightData(data);
61 updateStatus(state.getReachabilityStatus() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
63 if (!state.getOnOffState()) {
64 logger.debug("Setting state to OFF");
65 updateState(CHANNEL_BRIGHTNESS, PercentType.ZERO);
66 if (lightHasColorSupport()) {
67 updateState(CHANNEL_COLOR, HSBType.BLACK);
69 // if we are turned off, we do not set any brightness value
73 PercentType dimmer = state.getBrightness();
74 if (dimmer != null && !lightHasColorSupport()) { // color lights do not have brightness channel
75 updateState(CHANNEL_BRIGHTNESS, dimmer);
78 PercentType colorTemp = state.getColorTemperature();
79 if (colorTemp != null) {
80 updateState(CHANNEL_COLOR_TEMPERATURE, colorTemp);
84 if (lightHasColorSupport()) {
85 color = state.getColor();
87 updateState(CHANNEL_COLOR, color);
91 updateDeviceProperties(state);
96 "Updating thing for lightId {} to state {dimmer: {}, colorTemp: {}, color: {}, firmwareVersion: {}, modelId: {}, vendor: {}}",
97 state.getDeviceId(), dimmer, colorTemp, color, state.getFirmwareVersion(), state.getModelId(),
102 private void setBrightness(PercentType percent) {
103 TradfriLightData data = new TradfriLightData();
104 data.setBrightness(percent).setTransitionTime(DEFAULT_DIMMER_TRANSITION_TIME);
105 set(data.getJsonString());
108 private void setState(OnOffType onOff) {
109 TradfriLightData data = new TradfriLightData();
110 data.setOnOffState(onOff == OnOffType.ON);
111 set(data.getJsonString());
114 private void setColorTemperature(PercentType percent) {
115 TradfriLightData data = new TradfriLightData();
116 data.setColorTemperature(percent).setTransitionTime(DEFAULT_DIMMER_TRANSITION_TIME);
117 set(data.getJsonString());
120 private void setColor(HSBType hsb) {
121 TradfriLightData data = new TradfriLightData();
122 data.setColor(hsb).setTransitionTime(DEFAULT_DIMMER_TRANSITION_TIME);
123 set(data.getJsonString());
127 public void handleCommand(ChannelUID channelUID, Command command) {
129 if (command instanceof RefreshType) {
130 TradfriCoapClient coapClient = this.coapClient;
131 if (coapClient != null) {
132 logger.debug("Refreshing channel {}", channelUID);
133 coapClient.asyncGet(this);
135 logger.debug("coapClient is null!");
140 switch (channelUID.getId()) {
141 case CHANNEL_BRIGHTNESS:
142 handleBrightnessCommand(command);
144 case CHANNEL_COLOR_TEMPERATURE:
145 handleColorTemperatureCommand(command);
148 handleColorCommand(command);
151 logger.error("Unknown channel UID {}", channelUID);
156 private void handleBrightnessCommand(Command command) {
157 if (command instanceof PercentType percentCommand) {
158 setBrightness(percentCommand);
159 } else if (command instanceof OnOffType onOffCommand) {
160 setState(onOffCommand);
161 } else if (command instanceof IncreaseDecreaseType) {
162 final TradfriLightData state = this.state;
163 if (state != null && state.getBrightness() != null) {
164 @SuppressWarnings("null")
165 int current = state.getBrightness().intValue();
166 if (IncreaseDecreaseType.INCREASE.equals(command)) {
167 setBrightness(new PercentType(Math.min(current + STEP, PercentType.HUNDRED.intValue())));
169 setBrightness(new PercentType(Math.max(current - STEP, PercentType.ZERO.intValue())));
172 logger.debug("Cannot handle inc/dec as current state is not known.");
175 logger.debug("Cannot handle command {} for channel {}", command, CHANNEL_BRIGHTNESS);
179 private void handleColorTemperatureCommand(Command command) {
180 if (command instanceof PercentType percentCommand) {
181 setColorTemperature(percentCommand);
182 } else if (command instanceof IncreaseDecreaseType) {
183 final TradfriLightData state = this.state;
184 if (state != null && state.getColorTemperature() != null) {
185 @SuppressWarnings("null")
186 int current = state.getColorTemperature().intValue();
187 if (IncreaseDecreaseType.INCREASE.equals(command)) {
188 setColorTemperature(new PercentType(Math.min(current + STEP, PercentType.HUNDRED.intValue())));
190 setColorTemperature(new PercentType(Math.max(current - STEP, PercentType.ZERO.intValue())));
193 logger.debug("Cannot handle inc/dec as current state is not known.");
196 logger.debug("Can't handle command {} on channel {}", command, CHANNEL_COLOR_TEMPERATURE);
200 private void handleColorCommand(Command command) {
201 if (command instanceof HSBType hsbCommand) {
202 setColor(hsbCommand);
203 setBrightness(hsbCommand.getBrightness());
204 } else if (command instanceof OnOffType onOffCommand) {
205 setState(onOffCommand);
206 } else if (command instanceof PercentType percentCommand) {
207 setBrightness(percentCommand);
208 } else if (command instanceof IncreaseDecreaseType) {
209 final TradfriLightData state = this.state;
210 // increase or decrease only the brightness, but keep color
211 if (state != null && state.getBrightness() != null) {
212 @SuppressWarnings("null")
213 int current = state.getBrightness().intValue();
214 if (IncreaseDecreaseType.INCREASE.equals(command)) {
215 setBrightness(new PercentType(Math.min(current + STEP, PercentType.HUNDRED.intValue())));
217 setBrightness(new PercentType(Math.max(current - STEP, PercentType.ZERO.intValue())));
220 logger.debug("Cannot handle inc/dec for color as current brightness is not known.");
223 logger.debug("Can't handle command {} on channel {}", command, CHANNEL_COLOR);
228 * Checks if this light supports full color.
230 * @return true if the light supports full color
232 private boolean lightHasColorSupport() {
233 return thing.getThingTypeUID().getId().equals(THING_TYPE_COLOR_LIGHT.getId());