2 * Copyright (c) 2010-2020 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.ShellySettingsStatus;
26 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyShortLightStatus;
27 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLight;
28 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLightChannel;
29 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
30 import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
31 import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
32 import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
33 import org.openhab.core.library.types.DecimalType;
34 import org.openhab.core.library.types.HSBType;
35 import org.openhab.core.library.types.IncreaseDecreaseType;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.core.library.types.PercentType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.library.unit.SmartHomeUnits;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.types.Command;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link ShellyLightHandler} handles light (Bulb, Duo and RGBW2) specific commands and status. All other commands
48 * will be routet of the ShellyBaseHandler.
50 * @author Markus Michels - Initial contribution
53 public class ShellyLightHandler extends ShellyBaseHandler {
54 private final Logger logger = LoggerFactory.getLogger(ShellyLightHandler.class);
55 private final Map<Integer, ShellyColorUtils> channelColors;
60 * @param thing The thing passed by the HandlerFactory
61 * @param bindingConfig configuration of the binding
62 * @param coapServer coap server instance
63 * @param localIP local IP of the openHAB host
64 * @param httpPort port of the openHAB HTTP API
66 public ShellyLightHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
67 final ShellyBindingConfiguration bindingConfig, final ShellyCoapServer coapServer, final String localIP,
68 int httpPort, final HttpClient httpClient) {
69 super(thing, translationProvider, bindingConfig, coapServer, localIP, httpPort, httpClient);
70 channelColors = new TreeMap<>();
74 public void initialize() {
75 logger.debug("Thing is using {}", this.getClass());
80 public boolean handleDeviceCommand(ChannelUID channelUID, Command command) throws IllegalArgumentException {
81 String groupName = getString(channelUID.getGroupId());
82 if (groupName.isEmpty()) {
83 throw new IllegalArgumentException("Empty groupName");
86 int lightId = getLightIdFromGroup(groupName);
87 logger.trace("{}: Execute command {} on channel {}, lightId={}", thingName, command, channelUID.getAsString(),
91 ShellyColorUtils oldCol = getCurrentColors(lightId);
92 oldCol.mode = profile.mode;
93 ShellyColorUtils col = new ShellyColorUtils(oldCol);
95 boolean update = true;
96 switch (channelUID.getIdWithoutGroup()) {
97 default: // non-bulb commands will be handled by the generic handler
100 case CHANNEL_LIGHT_POWER:
101 logger.debug("{}: Switch light {}", thingName, command);
102 api.setLightParm(lightId, SHELLY_LIGHT_TURN,
103 command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
104 col.power = (OnOffType) command;
105 requestUpdates(1, false);
108 case CHANNEL_LIGHT_COLOR_MODE:
109 logger.debug("{}: Select color mode {}", thingName, command);
110 col.setMode((OnOffType) command == OnOffType.ON ? SHELLY_MODE_COLOR : SHELLY_MODE_WHITE);
112 case CHANNEL_COLOR_PICKER:
113 logger.debug("{}: Update colors from color picker", thingName);
114 update = handleColorPicker(profile, lightId, col, command);
116 case CHANNEL_COLOR_FULL:
117 logger.debug("{}: Set colors to {}", thingName, command);
118 handleFullColor(col, command);
120 case CHANNEL_COLOR_RED:
121 col.setRed(setColor(lightId, SHELLY_COLOR_RED, command, SHELLY_MAX_COLOR));
123 case CHANNEL_COLOR_GREEN:
124 col.setGreen(setColor(lightId, SHELLY_COLOR_GREEN, command, SHELLY_MAX_COLOR));
126 case CHANNEL_COLOR_BLUE:
127 col.setBlue(setColor(lightId, SHELLY_COLOR_BLUE, command, SHELLY_MAX_COLOR));
129 case CHANNEL_COLOR_WHITE:
130 col.setWhite(setColor(lightId, SHELLY_COLOR_WHITE, command, SHELLY_MAX_COLOR));
132 case CHANNEL_COLOR_GAIN:
133 col.setGain(setColor(lightId, SHELLY_COLOR_GAIN, command, SHELLY_MIN_GAIN, SHELLY_MAX_GAIN));
135 case CHANNEL_BRIGHTNESS: // only in white mode
136 if (profile.inColor && !profile.isBulb) {
137 logger.debug("{}: Not in white mode, brightness not available", thingName);
142 if (command instanceof OnOffType) { // Switch
143 logger.debug("{}: Switch light {}", thingName, command);
144 ShellyShortLightStatus light = api.setRelayTurn(lightId,
145 command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
146 col.power = getOnOff(light.ison);
147 col.setBrightness(light.brightness);
148 updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Switch", col.power);
149 updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value",
150 toQuantityType(new Double(col.power == OnOffType.ON ? col.brightness : 0), DIGITS_NONE,
151 SmartHomeUnits.PERCENT));
156 if (command instanceof PercentType) {
157 Float percent = ((PercentType) command).floatValue();
158 value = percent.intValue(); // 0..100% = 0..100
159 logger.debug("{}: Set brightness to {}%/{}", thingName, percent, value);
160 } else if (command instanceof DecimalType) {
161 value = ((DecimalType) command).intValue();
162 logger.debug("{}: Set brightness to {} (Integer)", thingName, value);
165 logger.debug("{}: Brightness=0 -> switch light OFF", thingName);
166 api.setRelayTurn(lightId, SHELLY_API_OFF);
169 if (command instanceof IncreaseDecreaseType) {
170 ShellyShortLightStatus light = api.getLightStatus(lightId);
171 if (((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
172 value = Math.min(light.brightness + DIM_STEPSIZE, 100);
174 value = Math.max(light.brightness - DIM_STEPSIZE, 0);
176 logger.trace("{}: Change brightness from {} to {}", thingName, light.brightness, value);
179 validateRange("brightness", value, 0, 100);
180 logger.debug("{}: Changing brightness from {} to {}", thingName, oldCol.brightness, value);
181 col.setBrightness(value);
183 updateChannel(CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_LIGHT_POWER,
184 value > 0 ? OnOffType.ON : OnOffType.OFF);
187 case CHANNEL_COLOR_TEMP:
189 if (command instanceof PercentType) {
190 logger.debug("{}: Set color temp to {}%", thingName, ((PercentType) command).floatValue());
191 Float percent = ((PercentType) command).floatValue() / 100;
192 temp = new DecimalType(col.minTemp + ((col.maxTemp - col.minTemp)) * percent).intValue();
193 logger.debug("{}: Converted color-temp {}% to {}K (from Percent to Integer)", thingName,
195 } else if (command instanceof DecimalType) {
196 temp = ((DecimalType) command).intValue();
197 logger.debug("{}: Set color temp to {}K (Integer)", thingName, temp);
199 validateRange(CHANNEL_COLOR_TEMP, temp, col.minTemp, col.maxTemp);
204 case CHANNEL_COLOR_EFFECT:
205 Integer effect = ((DecimalType) command).intValue();
206 logger.debug("{}: Set color effect to {}", thingName, effect);
207 validateRange("effect", effect, SHELLY_MIN_EFFECT, SHELLY_MAX_EFFECT);
208 col.setEffect(effect.intValue());
212 // check for switching color mode
213 if (profile.isBulb && !col.mode.isEmpty() && !col.mode.equals(oldCol.mode)) {
214 logger.debug("{}: Color mode changed from {} to {}, set new mode", thingName, oldCol.mode,
216 api.setLightMode(col.mode);
219 // send changed colors to the device
220 sendColors(profile, lightId, oldCol, col, config.brightnessAutoOn);
223 } catch (ShellyApiException e) {
224 logger.debug("{}: Unable to handle command: {}", thingName, e.toString());
226 } catch (IllegalArgumentException e) {
227 logger.debug("{}: Unable to handle command", thingName, e);
232 private boolean handleColorPicker(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils col,
233 Command command) throws ShellyApiException {
234 boolean updated = false;
235 if (command instanceof HSBType) {
236 HSBType hsb = (HSBType) command;
238 logger.debug("HSB-Info={}, Hue={}, getRGB={}, toRGB={}/{}/{}", hsb, hsb.getHue(),
239 String.format("0x%08X", hsb.getRGB()), hsb.toRGB()[0], hsb.toRGB()[1], hsb.toRGB()[2]);
240 if (hsb.toString().contains("360,")) {
241 logger.trace("{}: need to fix the Hue value (360->0)", thingName);
242 HSBType fixHue = new HSBType(new DecimalType(0), hsb.getSaturation(), hsb.getBrightness());
246 col.setRed(getColorFromHSB(hsb.getRed()));
247 col.setBlue(getColorFromHSB(hsb.getBlue()));
248 col.setGreen(getColorFromHSB(hsb.getGreen()));
249 col.setBrightness(getColorFromHSB(hsb.getBrightness(), BRIGHTNESS_FACTOR));
250 // white, gain and temp are not part of the HSB color scheme
252 } else if (command instanceof PercentType) {
253 if (!profile.inColor || profile.isBulb) {
254 col.brightness = SHELLY_MAX_BRIGHTNESS * ((PercentType) command).intValue();
257 } else if (command instanceof OnOffType) {
258 logger.debug("{}: Switch light {}", thingName, command);
259 api.setLightParm(lightId, SHELLY_LIGHT_TURN,
260 (OnOffType) command == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
261 col.power = (OnOffType) command;
262 } else if (command instanceof IncreaseDecreaseType) {
263 if (!profile.inColor || profile.isBulb) {
264 logger.debug("{}: {} brightness by {}", thingName, command, SHELLY_DIM_STEPSIZE);
265 PercentType percent = (PercentType) super.getChannelValue(CHANNEL_GROUP_COLOR_CONTROL,
267 if (percent != null) {
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;
283 private boolean handleFullColor(ShellyColorUtils col, Command command) throws IllegalArgumentException {
284 String color = command.toString().toLowerCase();
285 if (color.contains(",")) {
287 } else if (color.equals(SHELLY_COLOR_RED)) {
288 col.setRGBW(SHELLY_MAX_COLOR, 0, 0, 0);
289 } else if (color.equals(SHELLY_COLOR_GREEN)) {
290 col.setRGBW(0, SHELLY_MAX_COLOR, 0, 0);
291 } else if (color.equals(SHELLY_COLOR_BLUE)) {
292 col.setRGBW(0, 0, SHELLY_MAX_COLOR, 0);
293 } else if (color.equals(SHELLY_COLOR_YELLOW)) {
294 col.setRGBW(SHELLY_MAX_COLOR, SHELLY_MAX_COLOR, 0, 0);
295 } else if (color.equals(SHELLY_COLOR_WHITE)) {
296 col.setRGBW(0, 0, 0, SHELLY_MAX_COLOR);
297 col.setMode(SHELLY_MODE_WHITE);
299 throw new IllegalArgumentException("Invalid full color selection: " + color);
301 col.setMode(color.equals(SHELLY_MODE_WHITE) ? SHELLY_MODE_WHITE : SHELLY_MODE_COLOR);
305 private ShellyColorUtils getCurrentColors(int lightId) {
306 ShellyColorUtils col;
307 if (!channelColors.containsKey(lightId)) {
308 col = new ShellyColorUtils(); // create a new entry
309 col.setMinMaxTemp(profile.minTemp, profile.maxTemp);
310 channelColors.put(lightId, col);
311 logger.trace("{}: Colors entry created for lightId {}", thingName, lightId);
313 col = channelColors.get(lightId);
315 "{}: Colors loaded for lightId {}: power={}, RGBW={}/{}/{}/{}, gain={}, brightness={}, color temp={} (min={}, max={}",
316 thingName, lightId, col.power, col.red, col.green, col.blue, col.white, col.gain, col.brightness,
317 col.temp, col.minTemp, col.maxTemp);
323 public boolean updateDeviceStatus(ShellySettingsStatus genericStatus) throws ShellyApiException {
324 if (!profile.isInitialized()) {
325 logger.debug("{}: Device not yet initialized!", thingName);
328 if (!profile.isLight) {
329 logger.debug("{}: ERROR: Device is not a light. but class ShellyHandlerLight is called!", thingName);
332 ShellyStatusLight status = api.getLightStatus();
333 logger.trace("{}: Updating light status in {} mode, {} channel(s)", thingName, profile.mode,
334 status.lights.size());
336 // In white mode we have multiple channels
338 boolean updated = false;
339 for (ShellyStatusLightChannel light : status.lights) {
340 Integer channelId = lightId + 1;
341 String controlGroup = buildControlGroupName(profile, channelId);
342 // The bulb has a combined channel set for color or white mode
343 // The RGBW2 uses 2 different thing types: color=1 channel, white=4 channel
344 if (profile.isBulb) {
345 updateChannel(CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_LIGHT_COLOR_MODE, getOnOff(profile.inColor));
348 ShellyColorUtils col = getCurrentColors(lightId);
349 col.power = getOnOff(light.ison);
351 // Channel control/timer
352 // ShellyStatusLightChannel light = status.lights.get(i);
353 updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
354 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(light.autoOn));
355 updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(light.autoOff));
356 updated |= updateInputs(controlGroup, genericStatus, lightId);
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, new DecimalType(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() : new Double(0),
390 DIGITS_NONE, SmartHomeUnits.PERCENT));
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 Integer setColor(Integer lightId, String colorName, Command command, Integer minValue, Integer maxValue)
407 throws ShellyApiException, IllegalArgumentException {
409 logger.debug("{}: Set {} to {} ({})", thingName, colorName, command, command.getClass());
410 if (command instanceof PercentType) {
411 PercentType percent = (PercentType) command;
412 Double v = new Double(maxValue) * percent.doubleValue() / 100.0;
413 value = v.intValue();
414 logger.debug("{}: Value for {} is in percent: {}%={}", thingName, colorName, percent, value);
415 } else if (command instanceof DecimalType) {
416 value = ((DecimalType) command).intValue();
417 logger.debug("Value for {} is a number: {}", colorName, value);
418 } else if (command instanceof OnOffType) {
419 value = ((OnOffType) command).equals(OnOffType.ON) ? SHELLY_MAX_COLOR : SHELLY_MIN_COLOR;
420 logger.debug("{}: Value for {} of type OnOff was converted to {}", thingName, colorName, value);
422 throw new IllegalArgumentException(
423 "Invalid value type for " + colorName + ": " + value + " / type " + value.getClass());
425 validateRange(colorName, value, minValue, maxValue);
426 return value.intValue();
429 private Integer setColor(Integer lightId, String colorName, Command command, Integer maxValue)
430 throws ShellyApiException, IllegalArgumentException {
431 return setColor(lightId, colorName, command, 0, maxValue);
434 private void setFullColor(String colorGroup, ShellyColorUtils col) {
435 if ((col.red == SHELLY_MAX_COLOR) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
436 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_YELLOW));
437 } else if ((col.red == SHELLY_MAX_COLOR) && (col.green == 0) && (col.blue == 0)) {
438 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_RED));
439 } else if ((col.red == 0) && (col.green == SHELLY_MAX_COLOR) && (col.blue == 0)) {
440 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_GREEN));
441 } else if ((col.red == 0) && (col.green == 0) && (col.blue == SHELLY_MAX_COLOR)) {
442 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_BLUE));
443 } else if ((col.red == 0) && (col.green == 0) && (col.blue == 0) && (col.white == SHELLY_MAX_COLOR)) {
444 updateChannel(colorGroup, CHANNEL_COLOR_FULL, new StringType(SHELLY_COLOR_WHITE));
448 private void sendColors(ShellyDeviceProfile profile, Integer lightId, ShellyColorUtils oldCol,
449 ShellyColorUtils newCol, boolean autoOn) throws ShellyApiException {
450 // boolean updated = false;
451 Integer channelId = lightId + 1;
452 Map<String, String> parms = new TreeMap<>();
455 "{}: New color settings for channel {}: RGB {}/{}/{}, white={}, gain={}, brightness={}, color-temp={}",
456 thingName, channelId, newCol.red, newCol.green, newCol.blue, newCol.white, newCol.gain,
457 newCol.brightness, newCol.temp);
458 if (autoOn && (newCol.brightness >= 0)) {
459 parms.put(SHELLY_LIGHT_TURN, profile.inColor || newCol.brightness > 0 ? SHELLY_API_ON : SHELLY_API_OFF);
461 if (profile.inColor) {
462 if (oldCol.red != newCol.red || oldCol.green != newCol.green || oldCol.blue != newCol.blue
463 || oldCol.white != newCol.white) {
464 logger.debug("{}: Setting RGBW to {}/{}/{}/{}", thingName, newCol.red, newCol.green, newCol.blue,
466 parms.put(SHELLY_COLOR_RED, String.valueOf(newCol.red));
467 parms.put(SHELLY_COLOR_GREEN, String.valueOf(newCol.green));
468 parms.put(SHELLY_COLOR_BLUE, String.valueOf(newCol.blue));
469 parms.put(SHELLY_COLOR_WHITE, String.valueOf(newCol.white));
472 if ((!profile.inColor) && (oldCol.temp != newCol.temp)) {
473 logger.debug("{}: Setting color temp to {}", thingName, newCol.temp);
474 parms.put(SHELLY_COLOR_TEMP, String.valueOf(newCol.temp));
476 if (oldCol.gain != newCol.gain) {
477 logger.debug("{}: Setting gain to {}", thingName, newCol.gain);
478 parms.put(SHELLY_COLOR_GAIN, String.valueOf(newCol.gain));
480 if ((newCol.brightness >= 0) && (!profile.inColor || profile.isBulb)
481 && (oldCol.brightness != newCol.brightness)) {
482 logger.debug("{}: Setting brightness to {}", thingName, newCol.brightness);
483 parms.put(SHELLY_COLOR_BRIGHTNESS, String.valueOf(newCol.brightness));
485 if (!oldCol.effect.equals(newCol.effect)) {
486 logger.debug("{}: Setting effect to {}", thingName, newCol.effect);
487 parms.put(SHELLY_COLOR_EFFECT, newCol.effect.toString());
489 if (parms.size() > 0) {
490 logger.debug("{}: Send light settings: {}", thingName, parms);
491 api.setLightParms(lightId, parms);
492 updateCurrentColors(lightId, newCol);
496 private void updateCurrentColors(int lightId, ShellyColorUtils col) {
497 channelColors.replace(lightId, col);
498 logger.debug("{}: Colors updated for lightId {}: RGBW={}/{}/{}/{}, Sat/Gain={}, Bright={}, Temp={} ", thingName,
499 lightId, col.red, col.green, col.blue, col.white, col.gain, col.brightness, col.temp);
502 private Integer getColorFromHSB(PercentType colorPercent) {
503 return getColorFromHSB(colorPercent, new Double(SATURATION_FACTOR));
506 private Integer getColorFromHSB(PercentType colorPercent, Double factor) {
507 Double value = new Double(Math.round(colorPercent.doubleValue() * factor));
508 logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, value.intValue(),
510 return value.intValue();