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.setLightTurn(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.setLightTurn(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 if (profile.settings.lights != null) {
351 // Channel control/timer
352 ShellySettingsRgbwLight ls = profile.settings.lights.get(lightId);
353 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(ls.autoOn));
354 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(ls.autoOff));
355 updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer));
356 updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
359 if (getBool(light.overpower)) {
360 postEvent(ALARM_TYPE_OVERPOWER, false);
363 if (profile.inColor) {
364 logger.trace("{}: update color settings", thingName);
365 col.setRGBW(getInteger(light.red), getInteger(light.green), getInteger(light.blue),
366 getInteger(light.white));
367 col.setGain(getInteger(light.gain));
368 col.setEffect(getInteger(light.effect));
370 String colorGroup = CHANNEL_GROUP_COLOR_CONTROL;
371 logger.trace("{}: Update channels for group {}: RGBW={}/{}/{}, in %:{}%/{}%/{}%, white={}%, gain={}%",
372 thingName, colorGroup, col.red, col.green, col.blue, col.percentRed, col.percentGreen,
373 col.percentBlue, col.percentWhite, col.percentGain);
374 updated |= updateChannel(colorGroup, CHANNEL_COLOR_RED, col.percentRed);
375 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GREEN, col.percentGreen);
376 updated |= updateChannel(colorGroup, CHANNEL_COLOR_BLUE, col.percentBlue);
377 updated |= updateChannel(colorGroup, CHANNEL_COLOR_WHITE, col.percentWhite);
378 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GAIN, col.percentGain);
379 updated |= updateChannel(colorGroup, CHANNEL_COLOR_EFFECT, getDecimal(col.effect));
380 setFullColor(colorGroup, col);
382 logger.trace("{}: update {}.color picker", thingName, colorGroup);
383 updated |= updateChannel(colorGroup, CHANNEL_COLOR_PICKER, col.toHSB());
386 if (!profile.inColor || profile.isBulb) {
387 String whiteGroup = buildWhiteGroupName(profile, channelId);
388 col.setBrightness(getInteger(light.brightness));
389 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Switch", col.power);
390 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Value",
391 toQuantityType(col.power == OnOffType.ON ? col.percentBrightness.doubleValue() : 0, DIGITS_NONE,
394 if ((profile.isBulb || profile.isDuo) && (light.temp != null)) {
395 col.setTemp(getInteger(light.temp));
396 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_TEMP, col.percentTemp);
397 logger.trace("{}: update {}.color picker", thingName, whiteGroup);
398 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_PICKER, col.toHSB());
402 // continue with next light
408 private void createLightChannels(ShellyStatusLightChannel status, int idx) {
409 if (!areChannelsCreated()) {
410 updateChannelDefinitions(ShellyChannelDefinitions.createLightChannels(getThing(), profile, status, idx));
414 private Integer setColor(Integer lightId, String colorName, Command command, Integer minValue, Integer maxValue)
415 throws ShellyApiException, IllegalArgumentException {
417 logger.debug("{}: Set {} to {} ({})", thingName, colorName, command, command.getClass());
418 if (command instanceof PercentType) {
419 PercentType percent = (PercentType) command;
420 double v = (double) maxValue * percent.doubleValue() / 100.0;
422 logger.debug("{}: Value for {} is in percent: {}%={}", thingName, colorName, percent, value);
423 } else if (command instanceof DecimalType) {
424 value = ((DecimalType) command).intValue();
425 logger.debug("Value for {} is a number: {}", colorName, value);
426 } else if (command instanceof OnOffType) {
427 value = ((OnOffType) command).equals(OnOffType.ON) ? SHELLY_MAX_COLOR : SHELLY_MIN_COLOR;
428 logger.debug("{}: Value for {} of type OnOff was converted to {}", thingName, colorName, value);
430 throw new IllegalArgumentException(
431 "Invalid value type for " + colorName + ": " + value + " / type " + value.getClass());
433 validateRange(colorName, value, minValue, maxValue);
434 return value.intValue();
437 private Integer setColor(Integer lightId, String colorName, Command command, Integer maxValue)
438 throws ShellyApiException, IllegalArgumentException {
439 return setColor(lightId, colorName, command, 0, maxValue);
442 private void setFullColor(String colorGroup, ShellyColorUtils col) {
443 if ((col.red == SHELLY_MAX_COLOR) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
444 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_YELLOW));
445 } else if ((col.red == SHELLY_MAX_COLOR) && (col.green == 0) && (col.blue == 0)) {
446 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_RED));
447 } else if ((col.red == 0) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
448 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_GREEN));
449 } else if ((col.red == 0) && (col.green == 0) && (col.blue == SHELLY_MAX_COLOR)) {
450 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_BLUE));
451 } else if ((col.red == 0) && (col.green == 0) && (col.blue == 0) && (col.white == SHELLY_MAX_COLOR)) {
452 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_WHITE));
456 private void sendColors(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils oldCol,
457 ShellyColorUtils newCol, boolean autoOn) throws ShellyApiException {
458 // boolean updated = false;
459 Integer channelId = lightId + 1;
460 Map<String, String> parms = new TreeMap<>();
463 "{}: New color settings for channel {}: RGB {}/{}/{}, white={}, gain={}, brightness={}, color-temp={}",
464 thingName, channelId, newCol.red, newCol.green, newCol.blue, newCol.white, newCol.gain,
465 newCol.brightness, newCol.temp);
466 if (autoOn && (newCol.brightness >= 0)) {
467 parms.put(SHELLY_LIGHT_TURN, profile.inColor || newCol.brightness > 0 ? SHELLY_API_ON : SHELLY_API_OFF);
469 if (profile.inColor) {
470 if (oldCol.red != newCol.red || oldCol.green != newCol.green || oldCol.blue != newCol.blue
471 || oldCol.white != newCol.white) {
472 logger.debug("{}: Setting RGBW to {}/{}/{}/{}", thingName, newCol.red, newCol.green, newCol.blue,
474 parms.put(SHELLY_COLOR_RED, String.valueOf(newCol.red));
475 parms.put(SHELLY_COLOR_GREEN, String.valueOf(newCol.green));
476 parms.put(SHELLY_COLOR_BLUE, String.valueOf(newCol.blue));
477 parms.put(SHELLY_COLOR_WHITE, String.valueOf(newCol.white));
480 if ((!profile.inColor) && (oldCol.temp != newCol.temp)) {
481 logger.debug("{}: Setting color temp to {}", thingName, newCol.temp);
482 parms.put(SHELLY_COLOR_TEMP, String.valueOf(newCol.temp));
484 if (oldCol.gain != newCol.gain) {
485 logger.debug("{}: Setting gain to {}", thingName, newCol.gain);
486 parms.put(SHELLY_COLOR_GAIN, String.valueOf(newCol.gain));
488 if ((newCol.brightness >= 0) && (!profile.inColor || profile.isBulb)
489 && (oldCol.brightness != newCol.brightness)) {
490 logger.debug("{}: Setting brightness to {}", thingName, newCol.brightness);
491 parms.put(SHELLY_COLOR_BRIGHTNESS, String.valueOf(newCol.brightness));
493 if (!oldCol.effect.equals(newCol.effect)) {
494 logger.debug("{}: Setting effect to {}", thingName, newCol.effect);
495 parms.put(SHELLY_COLOR_EFFECT, newCol.effect.toString());
497 if (!parms.isEmpty()) {
498 logger.debug("{}: Send light settings: {}", thingName, parms);
499 api.setLightParms(lightId, parms);
500 updateCurrentColors(lightId, newCol);
504 private void updateCurrentColors(int lightId, ShellyColorUtils col) {
505 channelColors.replace(lightId, col);
506 logger.debug("{}: Colors updated for lightId {}: RGBW={}/{}/{}/{}, Sat/Gain={}, Bright={}, Temp={} ", thingName,
507 lightId, col.red, col.green, col.blue, col.white, col.gain, col.brightness, col.temp);
510 private int getColorFromHSB(PercentType colorPercent) {
511 return getColorFromHSB(colorPercent, SATURATION_FACTOR);
514 private int getColorFromHSB(PercentType colorPercent, double factor) {
515 double value = Math.round(colorPercent.doubleValue() * factor);
516 logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, (int) value, factor);