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.api1.Shelly1ApiJsonDTO.*;
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.ShellyDeviceProfile;
26 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight;
27 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
28 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
29 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusLight;
30 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusLightChannel;
31 import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
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 Shelly1CoapServer 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,
354 toQuantityType(getDouble(ls.autoOn), Units.SECOND));
355 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF,
356 toQuantityType(getDouble(ls.autoOff), Units.SECOND));
357 updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
358 updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer));
359 updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
362 if (getBool(light.overpower)) {
363 postEvent(ALARM_TYPE_OVERPOWER, false);
366 if (profile.inColor) {
367 logger.trace("{}: update color settings", thingName);
368 col.setRGBW(getInteger(light.red), getInteger(light.green), getInteger(light.blue),
369 getInteger(light.white));
370 col.setGain(getInteger(light.gain));
371 col.setEffect(getInteger(light.effect));
373 String colorGroup = CHANNEL_GROUP_COLOR_CONTROL;
374 logger.trace("{}: Update channels for group {}: RGBW={}/{}/{}, in %:{}%/{}%/{}%, white={}%, gain={}%",
375 thingName, colorGroup, col.red, col.green, col.blue, col.percentRed, col.percentGreen,
376 col.percentBlue, col.percentWhite, col.percentGain);
377 updated |= updateChannel(colorGroup, CHANNEL_COLOR_RED, col.percentRed);
378 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GREEN, col.percentGreen);
379 updated |= updateChannel(colorGroup, CHANNEL_COLOR_BLUE, col.percentBlue);
380 updated |= updateChannel(colorGroup, CHANNEL_COLOR_WHITE, col.percentWhite);
381 updated |= updateChannel(colorGroup, CHANNEL_COLOR_GAIN, col.percentGain);
382 updated |= updateChannel(colorGroup, CHANNEL_COLOR_EFFECT, getDecimal(col.effect));
383 setFullColor(colorGroup, col);
385 logger.trace("{}: update {}.color picker", thingName, colorGroup);
386 updated |= updateChannel(colorGroup, CHANNEL_COLOR_PICKER, col.toHSB());
389 if (!profile.inColor || profile.isBulb) {
390 String whiteGroup = buildWhiteGroupName(profile, channelId);
391 col.setBrightness(getInteger(light.brightness));
392 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Switch", col.power);
393 updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Value",
394 toQuantityType(col.power == OnOffType.ON ? col.percentBrightness.doubleValue() : 0, DIGITS_NONE,
397 if ((profile.isBulb || profile.isDuo) && (light.temp != null)) {
398 col.setTemp(getInteger(light.temp));
399 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_TEMP, col.percentTemp);
400 logger.trace("{}: update {}.color picker", thingName, whiteGroup);
401 updated |= updateChannel(whiteGroup, CHANNEL_COLOR_PICKER, col.toHSB());
405 // continue with next light
411 private void createLightChannels(ShellyStatusLightChannel status, int idx) {
412 if (!areChannelsCreated()) {
413 updateChannelDefinitions(ShellyChannelDefinitions.createLightChannels(getThing(), profile, status, idx));
417 private Integer setColor(Integer lightId, String colorName, Command command, Integer minValue, Integer maxValue)
418 throws ShellyApiException, IllegalArgumentException {
420 logger.debug("{}: Set {} to {} ({})", thingName, colorName, command, command.getClass());
421 if (command instanceof PercentType) {
422 PercentType percent = (PercentType) command;
423 double v = (double) maxValue * percent.doubleValue() / 100.0;
425 logger.debug("{}: Value for {} is in percent: {}%={}", thingName, colorName, percent, value);
426 } else if (command instanceof DecimalType) {
427 value = ((DecimalType) command).intValue();
428 logger.debug("Value for {} is a number: {}", colorName, value);
429 } else if (command instanceof OnOffType) {
430 value = ((OnOffType) command).equals(OnOffType.ON) ? SHELLY_MAX_COLOR : SHELLY_MIN_COLOR;
431 logger.debug("{}: Value for {} of type OnOff was converted to {}", thingName, colorName, value);
433 throw new IllegalArgumentException(
434 "Invalid value type for " + colorName + ": " + value + " / type " + value.getClass());
436 validateRange(colorName, value, minValue, maxValue);
437 return value.intValue();
440 private Integer setColor(Integer lightId, String colorName, Command command, Integer maxValue)
441 throws ShellyApiException, IllegalArgumentException {
442 return setColor(lightId, colorName, command, 0, maxValue);
445 private void setFullColor(String colorGroup, ShellyColorUtils col) {
446 if ((col.red == SHELLY_MAX_COLOR) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
447 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_YELLOW));
448 } else if ((col.red == SHELLY_MAX_COLOR) && (col.green == 0) && (col.blue == 0)) {
449 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_RED));
450 } else if ((col.red == 0) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
451 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_GREEN));
452 } else if ((col.red == 0) && (col.green == 0) && (col.blue == SHELLY_MAX_COLOR)) {
453 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_BLUE));
454 } else if ((col.red == 0) && (col.green == 0) && (col.blue == 0) && (col.white == SHELLY_MAX_COLOR)) {
455 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_WHITE));
459 private void sendColors(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils oldCol,
460 ShellyColorUtils newCol, boolean autoOn) throws ShellyApiException {
461 // boolean updated = false;
462 Integer channelId = lightId + 1;
463 Map<String, String> parms = new TreeMap<>();
466 "{}: New color settings for channel {}: RGB {}/{}/{}, white={}, gain={}, brightness={}, color-temp={}",
467 thingName, channelId, newCol.red, newCol.green, newCol.blue, newCol.white, newCol.gain,
468 newCol.brightness, newCol.temp);
469 if (autoOn && (newCol.brightness >= 0)) {
470 parms.put(SHELLY_LIGHT_TURN, profile.inColor || newCol.brightness > 0 ? SHELLY_API_ON : SHELLY_API_OFF);
472 if (profile.inColor) {
473 if (oldCol.red != newCol.red || oldCol.green != newCol.green || oldCol.blue != newCol.blue
474 || oldCol.white != newCol.white) {
475 logger.debug("{}: Setting RGBW to {}/{}/{}/{}", thingName, newCol.red, newCol.green, newCol.blue,
477 parms.put(SHELLY_COLOR_RED, String.valueOf(newCol.red));
478 parms.put(SHELLY_COLOR_GREEN, String.valueOf(newCol.green));
479 parms.put(SHELLY_COLOR_BLUE, String.valueOf(newCol.blue));
480 parms.put(SHELLY_COLOR_WHITE, String.valueOf(newCol.white));
483 if ((!profile.inColor) && (oldCol.temp != newCol.temp)) {
484 logger.debug("{}: Setting color temp to {}", thingName, newCol.temp);
485 parms.put(SHELLY_COLOR_TEMP, String.valueOf(newCol.temp));
487 if (oldCol.gain != newCol.gain) {
488 logger.debug("{}: Setting gain to {}", thingName, newCol.gain);
489 parms.put(SHELLY_COLOR_GAIN, String.valueOf(newCol.gain));
491 if ((newCol.brightness >= 0) && (!profile.inColor || profile.isBulb)
492 && (oldCol.brightness != newCol.brightness)) {
493 logger.debug("{}: Setting brightness to {}", thingName, newCol.brightness);
494 parms.put(SHELLY_COLOR_BRIGHTNESS, String.valueOf(newCol.brightness));
496 if (!oldCol.effect.equals(newCol.effect)) {
497 logger.debug("{}: Setting effect to {}", thingName, newCol.effect);
498 parms.put(SHELLY_COLOR_EFFECT, newCol.effect.toString());
500 if (!parms.isEmpty()) {
501 logger.debug("{}: Send light settings: {}", thingName, parms);
502 api.setLightParms(lightId, parms);
503 updateCurrentColors(lightId, newCol);
507 private void updateCurrentColors(int lightId, ShellyColorUtils col) {
508 channelColors.replace(lightId, col);
509 logger.debug("{}: Colors updated for lightId {}: RGBW={}/{}/{}/{}, Sat/Gain={}, Bright={}, Temp={} ", thingName,
510 lightId, col.red, col.green, col.blue, col.white, col.gain, col.brightness, col.temp);
513 private int getColorFromHSB(PercentType colorPercent) {
514 return getColorFromHSB(colorPercent, SATURATION_FACTOR);
517 private int getColorFromHSB(PercentType colorPercent, double factor) {
518 double value = Math.round(colorPercent.doubleValue() * factor);
519 logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, (int) value, factor);