2 * Copyright (c) 2010-2022 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.shelly.internal.handler;
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16 import static org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.*;
17 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
20 import java.util.TreeMap;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.openhab.binding.shelly.internal.api.ShellyApiException;
25 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRgbwLight;
26 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
27 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyShortLightStatus;
28 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLight;
29 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLightChannel;
30 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
31 import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
32 import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
33 import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
34 import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.HSBType;
37 import org.openhab.core.library.types.IncreaseDecreaseType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.PercentType;
40 import org.openhab.core.library.types.StringType;
41 import org.openhab.core.library.unit.Units;
42 import org.openhab.core.thing.ChannelUID;
43 import org.openhab.core.thing.Thing;
44 import org.openhab.core.types.Command;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The {@link ShellyLightHandler} handles light (Bulb, Duo and RGBW2) specific commands and status. All other commands
50 * will be routet of the ShellyBaseHandler.
52 * @author Markus Michels - Initial contribution
55 public class ShellyLightHandler extends ShellyBaseHandler {
56 private final Logger logger = LoggerFactory.getLogger(ShellyLightHandler.class);
57 private final Map<Integer, ShellyColorUtils> channelColors;
62 * @param thing The thing passed by the HandlerFactory
63 * @param bindingConfig configuration of the binding
64 * @param coapServer coap server instance
65 * @param localIP local IP of the openHAB host
66 * @param httpPort port of the openHAB HTTP API
68 public ShellyLightHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
69 final ShellyBindingConfiguration bindingConfig, final ShellyCoapServer coapServer, final String localIP,
70 int httpPort, final HttpClient httpClient) {
71 super(thing, translationProvider, bindingConfig, coapServer, localIP, httpPort, httpClient);
72 channelColors = new TreeMap<>();
76 public void initialize() {
77 logger.debug("Thing is using {}", this.getClass());
82 public boolean handleDeviceCommand(ChannelUID channelUID, Command command) throws IllegalArgumentException {
83 String groupName = getString(channelUID.getGroupId());
84 if (groupName.isEmpty()) {
85 throw new IllegalArgumentException("Empty groupName");
88 int lightId = getLightIdFromGroup(groupName);
89 logger.trace("{}: Execute command {} on channel {}, lightId={}", thingName, command, channelUID.getAsString(),
93 ShellyColorUtils oldCol = getCurrentColors(lightId);
94 oldCol.mode = profile.mode;
95 ShellyColorUtils col = new ShellyColorUtils(oldCol);
97 boolean update = true;
98 switch (channelUID.getIdWithoutGroup()) {
99 default: // non-bulb commands will be handled by the generic handler
102 case CHANNEL_LIGHT_POWER:
103 logger.debug("{}: Switch light {}", thingName, command);
104 api.setLightParm(lightId, SHELLY_LIGHT_TURN,
105 command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
106 col.power = (OnOffType) command;
107 requestUpdates(1, false);
110 case CHANNEL_LIGHT_COLOR_MODE:
111 logger.debug("{}: Select color mode {}", thingName, command);
112 col.setMode((OnOffType) command == OnOffType.ON ? SHELLY_MODE_COLOR : SHELLY_MODE_WHITE);
114 case CHANNEL_COLOR_PICKER:
115 logger.debug("{}: Update colors from color picker", thingName);
116 update = handleColorPicker(profile, lightId, col, command);
118 case CHANNEL_COLOR_FULL:
119 logger.debug("{}: Set colors to {}", thingName, command);
120 handleFullColor(col, command);
122 case CHANNEL_COLOR_RED:
123 col.setRed(setColor(lightId, SHELLY_COLOR_RED, command, SHELLY_MAX_COLOR));
125 case CHANNEL_COLOR_GREEN:
126 col.setGreen(setColor(lightId, SHELLY_COLOR_GREEN, command, SHELLY_MAX_COLOR));
128 case CHANNEL_COLOR_BLUE:
129 col.setBlue(setColor(lightId, SHELLY_COLOR_BLUE, command, SHELLY_MAX_COLOR));
131 case CHANNEL_COLOR_WHITE:
132 col.setWhite(setColor(lightId, SHELLY_COLOR_WHITE, command, SHELLY_MAX_COLOR));
134 case CHANNEL_COLOR_GAIN:
135 col.setGain(setColor(lightId, SHELLY_COLOR_GAIN, command, SHELLY_MIN_GAIN, SHELLY_MAX_GAIN));
137 case CHANNEL_BRIGHTNESS: // only in white mode
138 if (profile.inColor && !profile.isBulb) {
139 logger.debug("{}: Not in white mode, brightness not available", thingName);
144 if (command instanceof OnOffType) { // Switch
145 logger.debug("{}: Switch light {}", thingName, command);
146 ShellyShortLightStatus light = api.setRelayTurn(lightId,
147 command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
148 col.power = getOnOff(light.ison);
149 col.setBrightness(light.brightness);
150 updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Switch", col.power);
151 updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value", toQuantityType(
152 (double) (col.power == OnOffType.ON ? col.brightness : 0), DIGITS_NONE, Units.PERCENT));
157 if (command instanceof PercentType) {
158 Float percent = ((PercentType) command).floatValue();
159 value = percent.intValue(); // 0..100% = 0..100
160 logger.debug("{}: Set brightness to {}%/{}", thingName, percent, value);
161 } else if (command instanceof DecimalType) {
162 value = ((DecimalType) command).intValue();
163 logger.debug("{}: Set brightness to {} (Integer)", thingName, value);
166 logger.debug("{}: Brightness=0 -> switch light OFF", thingName);
167 api.setRelayTurn(lightId, SHELLY_API_OFF);
170 if (command instanceof IncreaseDecreaseType) {
171 ShellyShortLightStatus light = api.getLightStatus(lightId);
172 if (((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
173 value = Math.min(light.brightness + DIM_STEPSIZE, 100);
175 value = Math.max(light.brightness - DIM_STEPSIZE, 0);
177 logger.trace("{}: Change brightness from {} to {}", thingName, light.brightness, value);
180 validateRange("brightness", value, 0, 100);
181 logger.debug("{}: Changing brightness from {} to {}", thingName, oldCol.brightness, value);
182 col.setBrightness(value);
184 updateChannel(CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_LIGHT_POWER,
185 value > 0 ? OnOffType.ON : OnOffType.OFF);
188 case CHANNEL_COLOR_TEMP:
190 if (command instanceof PercentType) {
191 logger.debug("{}: Set color temp to {}%", thingName, ((PercentType) command).floatValue());
192 Float percent = ((PercentType) command).floatValue() / 100;
193 temp = new DecimalType(col.minTemp + ((col.maxTemp - col.minTemp)) * percent).intValue();
194 logger.debug("{}: Converted color-temp {}% to {}K (from Percent to Integer)", thingName,
196 } else if (command instanceof DecimalType) {
197 temp = ((DecimalType) command).intValue();
198 logger.debug("{}: Set color temp to {}K (Integer)", thingName, temp);
200 validateRange(CHANNEL_COLOR_TEMP, temp, col.minTemp, col.maxTemp);
205 case CHANNEL_COLOR_EFFECT:
206 Integer effect = ((DecimalType) command).intValue();
207 logger.debug("{}: Set color effect to {}", thingName, effect);
208 validateRange("effect", effect, SHELLY_MIN_EFFECT, SHELLY_MAX_EFFECT);
209 col.setEffect(effect.intValue());
213 // check for switching color mode
214 if (profile.isBulb && !col.mode.isEmpty() && !col.mode.equals(oldCol.mode)) {
215 logger.debug("{}: Color mode changed from {} to {}, set new mode", thingName, oldCol.mode,
217 api.setLightMode(col.mode);
220 // send changed colors to the device
221 sendColors(profile, lightId, oldCol, col, config.brightnessAutoOn);
224 } catch (ShellyApiException e) {
225 logger.debug("{}: Unable to handle command: {}", thingName, e.toString());
227 } catch (IllegalArgumentException e) {
228 logger.debug("{}: Unable to handle command", thingName, e);
233 private boolean handleColorPicker(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils col,
234 Command command) throws ShellyApiException {
235 boolean updated = false;
236 if (command instanceof HSBType) {
237 HSBType hsb = (HSBType) command;
239 logger.debug("HSB-Info={}, Hue={}, getRGB={}, toRGB={}/{}/{}", hsb, hsb.getHue(),
240 String.format("0x%08X", hsb.getRGB()), hsb.toRGB()[0], hsb.toRGB()[1], hsb.toRGB()[2]);
241 if (hsb.toString().contains("360,")) {
242 logger.trace("{}: need to fix the Hue value (360->0)", thingName);
243 HSBType fixHue = new HSBType(new DecimalType(0), hsb.getSaturation(), hsb.getBrightness());
247 col.setRed(getColorFromHSB(hsb.getRed()));
248 col.setBlue(getColorFromHSB(hsb.getBlue()));
249 col.setGreen(getColorFromHSB(hsb.getGreen()));
250 col.setBrightness(getColorFromHSB(hsb.getBrightness(), BRIGHTNESS_FACTOR));
251 // white, gain and temp are not part of the HSB color scheme
253 } else if (command instanceof PercentType) {
254 if (!profile.inColor || profile.isBulb) {
255 col.brightness = SHELLY_MAX_BRIGHTNESS * ((PercentType) command).intValue();
258 } else if (command instanceof OnOffType) {
259 logger.debug("{}: Switch light {}", thingName, command);
260 api.setLightParm(lightId, SHELLY_LIGHT_TURN,
261 (OnOffType) command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
262 col.power = (OnOffType) command;
263 } else if (command instanceof IncreaseDecreaseType) {
264 if (!profile.inColor || profile.isBulb) {
265 logger.debug("{}: {} brightness by {}", thingName, command, SHELLY_DIM_STEPSIZE);
266 PercentType percent = (PercentType) super.getChannelValue(CHANNEL_GROUP_COLOR_CONTROL,
268 int currentBrightness = percent.intValue() * SHELLY_MAX_BRIGHTNESS;
269 int newBrightness = currentBrightness;
270 if (command == IncreaseDecreaseType.DECREASE) {
271 newBrightness = Math.max(currentBrightness - SHELLY_DIM_STEPSIZE, 0);
273 newBrightness = Math.min(currentBrightness + SHELLY_DIM_STEPSIZE, SHELLY_MAX_BRIGHTNESS);
275 col.brightness = newBrightness;
276 updated = currentBrightness != newBrightness;
282 private boolean handleFullColor(ShellyColorUtils col, Command command) throws IllegalArgumentException {
283 String color = command.toString().toLowerCase();
284 if (color.contains(",")) {
286 } else if (color.equals(SHELLY_COLOR_RED)) {
287 col.setRGBW(SHELLY_MAX_COLOR, 0, 0, 0);
288 } else if (color.equals(SHELLY_COLOR_GREEN)) {
289 col.setRGBW(0, SHELLY_MAX_COLOR, 0, 0);
290 } else if (color.equals(SHELLY_COLOR_BLUE)) {
291 col.setRGBW(0, 0, SHELLY_MAX_COLOR, 0);
292 } else if (color.equals(SHELLY_COLOR_YELLOW)) {
293 col.setRGBW(SHELLY_MAX_COLOR, SHELLY_MAX_COLOR, 0, 0);
294 } else if (color.equals(SHELLY_COLOR_WHITE)) {
295 col.setRGBW(0, 0, 0, SHELLY_MAX_COLOR);
296 col.setMode(SHELLY_MODE_WHITE);
298 throw new IllegalArgumentException("Invalid full color selection: " + color);
300 col.setMode(color.equals(SHELLY_MODE_WHITE) ? SHELLY_MODE_WHITE : SHELLY_MODE_COLOR);
304 private ShellyColorUtils getCurrentColors(int lightId) {
305 ShellyColorUtils col = channelColors.get(lightId);
307 col = new ShellyColorUtils(); // create a new entry
308 col.setMinMaxTemp(profile.minTemp, profile.maxTemp);
309 channelColors.put(lightId, col);
310 logger.trace("{}: Colors entry created for lightId {}", thingName, lightId);
313 "{}: Colors loaded for lightId {}: power={}, RGBW={}/{}/{}/{}, gain={}, brightness={}, color temp={} (min={}, max={}",
314 thingName, lightId, col.power, col.red, col.green, col.blue, col.white, col.gain, col.brightness,
315 col.temp, col.minTemp, col.maxTemp);
321 public boolean updateDeviceStatus(ShellySettingsStatus genericStatus) throws ShellyApiException {
322 if (!profile.isInitialized()) {
323 logger.debug("{}: Device not yet initialized!", thingName);
326 if (!profile.isLight) {
327 logger.debug("{}: ERROR: Device is not a light. but class ShellyHandlerLight is called!", thingName);
330 ShellyStatusLight status = api.getLightStatus();
331 logger.trace("{}: Updating light status in {} mode, {} channel(s)", thingName, profile.mode,
332 status.lights.size());
334 // In white mode we have multiple channels
336 boolean updated = false;
337 for (ShellyStatusLightChannel light : status.lights) {
338 Integer channelId = lightId + 1;
339 String controlGroup = buildControlGroupName(profile, channelId);
340 createLightChannels(light, lightId);
341 // The bulb has a combined channel set for color or white mode
342 // The RGBW2 uses 2 different thing types: color=1 channel, white=4 channel
343 if (profile.isBulb) {
344 updateChannel(CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_LIGHT_COLOR_MODE, getOnOff(profile.inColor));
347 ShellyColorUtils col = getCurrentColors(lightId);
348 col.power = getOnOff(light.ison);
350 // Channel control/timer
351 ShellySettingsRgbwLight ls = profile.settings.lights.get(lightId);
352 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(ls.autoOn));
353 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(ls.autoOff));
354 updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
355 updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer));
357 if (getBool(light.overpower)) {
358 postEvent(ALARM_TYPE_OVERPOWER, false);
361 if (profile.inColor) {
362 logger.trace("{}: update color settings", thingName);
363 col.setRGBW(getInteger(light.red), getInteger(light.green), getInteger(light.blue),
364 getInteger(light.white));
365 col.setGain(getInteger(light.gain));
366 col.setEffect(getInteger(light.effect));
368 String colorGroup = CHANNEL_GROUP_COLOR_CONTROL;
369 logger.trace("{}: Update channels for group {}: RGBW={}/{}/{}, in %:{}%/{}%/{}%, white={}%, gain={}%",
370 thingName, colorGroup, col.red, col.green, col.blue, col.percentRed, col.percentGreen,
371 col.percentBlue, col.percentWhite, col.percentGain);
372 updated |= updateChannel(colorGroup, CHANNEL_COLOR_RED, col.percentRed);
373 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GREEN, col.percentGreen);
374 updated |= updateChannel(colorGroup, CHANNEL_COLOR_BLUE, col.percentBlue);
375 updated |= updateChannel(colorGroup, CHANNEL_COLOR_WHITE, col.percentWhite);
376 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GAIN, col.percentGain);
377 updated |= updateChannel(colorGroup, CHANNEL_COLOR_EFFECT, getDecimal(col.effect));
378 setFullColor(colorGroup, col);
380 logger.trace("{}: update {}.color picker", thingName, colorGroup);
381 updated |= updateChannel(colorGroup, CHANNEL_COLOR_PICKER, col.toHSB());
384 if (!profile.inColor || profile.isBulb) {
385 String whiteGroup = buildWhiteGroupName(profile, channelId);
386 col.setBrightness(getInteger(light.brightness));
387 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Switch", col.power);
388 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Value",
389 toQuantityType(col.power == OnOffType.ON ? col.percentBrightness.doubleValue() : 0, DIGITS_NONE,
392 if ((profile.isBulb || profile.isDuo) && (light.temp != null)) {
393 col.setTemp(getInteger(light.temp));
394 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_TEMP, col.percentTemp);
395 logger.trace("{}: update {}.color picker", thingName, whiteGroup);
396 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_PICKER, col.toHSB());
400 // continue with next light
406 private void createLightChannels(ShellyStatusLightChannel status, int idx) {
407 if (!areChannelsCreated()) {
408 updateChannelDefinitions(ShellyChannelDefinitions.createLightChannels(getThing(), profile, status, idx));
412 private Integer setColor(Integer lightId, String colorName, Command command, Integer minValue, Integer maxValue)
413 throws ShellyApiException, IllegalArgumentException {
415 logger.debug("{}: Set {} to {} ({})", thingName, colorName, command, command.getClass());
416 if (command instanceof PercentType) {
417 PercentType percent = (PercentType) command;
418 double v = (double) maxValue * percent.doubleValue() / 100.0;
420 logger.debug("{}: Value for {} is in percent: {}%={}", thingName, colorName, percent, value);
421 } else if (command instanceof DecimalType) {
422 value = ((DecimalType) command).intValue();
423 logger.debug("Value for {} is a number: {}", colorName, value);
424 } else if (command instanceof OnOffType) {
425 value = ((OnOffType) command).equals(OnOffType.ON) ? SHELLY_MAX_COLOR : SHELLY_MIN_COLOR;
426 logger.debug("{}: Value for {} of type OnOff was converted to {}", thingName, colorName, value);
428 throw new IllegalArgumentException(
429 "Invalid value type for " + colorName + ": " + value + " / type " + value.getClass());
431 validateRange(colorName, value, minValue, maxValue);
432 return value.intValue();
435 private Integer setColor(Integer lightId, String colorName, Command command, Integer maxValue)
436 throws ShellyApiException, IllegalArgumentException {
437 return setColor(lightId, colorName, command, 0, maxValue);
440 private void setFullColor(String colorGroup, ShellyColorUtils col) {
441 if ((col.red == SHELLY_MAX_COLOR) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
442 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_YELLOW));
443 } else if ((col.red == SHELLY_MAX_COLOR) && (col.green == 0) && (col.blue == 0)) {
444 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_RED));
445 } else if ((col.red == 0) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
446 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_GREEN));
447 } else if ((col.red == 0) && (col.green == 0) && (col.blue == SHELLY_MAX_COLOR)) {
448 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_BLUE));
449 } else if ((col.red == 0) && (col.green == 0) && (col.blue == 0) && (col.white == SHELLY_MAX_COLOR)) {
450 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_WHITE));
454 private void sendColors(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils oldCol,
455 ShellyColorUtils newCol, boolean autoOn) throws ShellyApiException {
456 // boolean updated = false;
457 Integer channelId = lightId + 1;
458 Map<String, String> parms = new TreeMap<>();
461 "{}: New color settings for channel {}: RGB {}/{}/{}, white={}, gain={}, brightness={}, color-temp={}",
462 thingName, channelId, newCol.red, newCol.green, newCol.blue, newCol.white, newCol.gain,
463 newCol.brightness, newCol.temp);
464 if (autoOn && (newCol.brightness >= 0)) {
465 parms.put(SHELLY_LIGHT_TURN, profile.inColor || newCol.brightness > 0 ? SHELLY_API_ON : SHELLY_API_OFF);
467 if (profile.inColor) {
468 if (oldCol.red != newCol.red || oldCol.green != newCol.green || oldCol.blue != newCol.blue
469 || oldCol.white != newCol.white) {
470 logger.debug("{}: Setting RGBW to {}/{}/{}/{}", thingName, newCol.red, newCol.green, newCol.blue,
472 parms.put(SHELLY_COLOR_RED, String.valueOf(newCol.red));
473 parms.put(SHELLY_COLOR_GREEN, String.valueOf(newCol.green));
474 parms.put(SHELLY_COLOR_BLUE, String.valueOf(newCol.blue));
475 parms.put(SHELLY_COLOR_WHITE, String.valueOf(newCol.white));
478 if ((!profile.inColor) && (oldCol.temp != newCol.temp)) {
479 logger.debug("{}: Setting color temp to {}", thingName, newCol.temp);
480 parms.put(SHELLY_COLOR_TEMP, String.valueOf(newCol.temp));
482 if (oldCol.gain != newCol.gain) {
483 logger.debug("{}: Setting gain to {}", thingName, newCol.gain);
484 parms.put(SHELLY_COLOR_GAIN, String.valueOf(newCol.gain));
486 if ((newCol.brightness >= 0) && (!profile.inColor || profile.isBulb)
487 && (oldCol.brightness != newCol.brightness)) {
488 logger.debug("{}: Setting brightness to {}", thingName, newCol.brightness);
489 parms.put(SHELLY_COLOR_BRIGHTNESS, String.valueOf(newCol.brightness));
491 if (!oldCol.effect.equals(newCol.effect)) {
492 logger.debug("{}: Setting effect to {}", thingName, newCol.effect);
493 parms.put(SHELLY_COLOR_EFFECT, newCol.effect.toString());
495 if (!parms.isEmpty()) {
496 logger.debug("{}: Send light settings: {}", thingName, parms);
497 api.setLightParms(lightId, parms);
498 updateCurrentColors(lightId, newCol);
502 private void updateCurrentColors(int lightId, ShellyColorUtils col) {
503 channelColors.replace(lightId, col);
504 logger.debug("{}: Colors updated for lightId {}: RGBW={}/{}/{}/{}, Sat/Gain={}, Bright={}, Temp={} ", thingName,
505 lightId, col.red, col.green, col.blue, col.white, col.gain, col.brightness, col.temp);
508 private int getColorFromHSB(PercentType colorPercent) {
509 return getColorFromHSB(colorPercent, SATURATION_FACTOR);
512 private int getColorFromHSB(PercentType colorPercent, double factor) {
513 double value = Math.round(colorPercent.doubleValue() * factor);
514 logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, (int) value, factor);