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.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 ShellyThingTable thingTable,
70 final Shelly1CoapServer coapServer, final HttpClient httpClient) {
71 super(thing, translationProvider, bindingConfig, thingTable, coapServer, 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);