]> git.basschouten.com Git - openhab-addons.git/blob
a4c7868beadf3e87533a70bd46322819d5dc8866
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.hue.internal.handler;
14
15 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
16 import static org.openhab.core.thing.Thing.*;
17
18 import java.math.BigDecimal;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Set;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.hue.internal.FullLight;
28 import org.openhab.binding.hue.internal.State;
29 import org.openhab.binding.hue.internal.StateUpdate;
30 import org.openhab.binding.hue.internal.dto.Capabilities;
31 import org.openhab.binding.hue.internal.dto.ColorTemperature;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.HSBType;
34 import org.openhab.core.library.types.IncreaseDecreaseType;
35 import org.openhab.core.library.types.OnOffType;
36 import org.openhab.core.library.types.PercentType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.ThingStatusInfo;
44 import org.openhab.core.thing.ThingTypeUID;
45 import org.openhab.core.thing.binding.BaseThingHandler;
46 import org.openhab.core.thing.binding.ThingHandler;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.StateDescriptionFragment;
49 import org.openhab.core.types.StateDescriptionFragmentBuilder;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 /**
54  * {@link HueLightHandler} is the handler for a hue light. It uses the {@link HueClient} to execute the actual
55  * command.
56  *
57  * @author Dennis Nobel - Initial contribution
58  * @author Oliver Libutzki - Adjustments
59  * @author Kai Kreuzer - stabilized code
60  * @author Andre Fuechsel - implemented switch off when brightness == 0, changed to support generic thing types, changed
61  *         the initialization of properties
62  * @author Thomas Höfer - added thing properties
63  * @author Jochen Hiller - fixed status updates for reachable=true/false
64  * @author Markus Mazurczak - added code for command handling of OSRAM PAR16 50
65  *         bulbs
66  * @author Yordan Zhelev - added alert and effect functions
67  * @author Denis Dudnik - switched to internally integrated source of Jue library
68  * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
69  * @author Jochen Leopold - Added support for custom fade times
70  */
71 @NonNullByDefault
72 public class HueLightHandler extends BaseThingHandler implements HueLightActionsHandler, LightStatusListener {
73
74     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_COLOR_LIGHT,
75             THING_TYPE_COLOR_TEMPERATURE_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT,
76             THING_TYPE_ON_OFF_LIGHT, THING_TYPE_ON_OFF_PLUG, THING_TYPE_DIMMABLE_PLUG);
77
78     public static final String OSRAM_PAR16_50_TW_MODEL_ID = "PAR16_50_TW";
79
80     private final Logger logger = LoggerFactory.getLogger(HueLightHandler.class);
81
82     private final HueStateDescriptionProvider stateDescriptionProvider;
83
84     private @NonNullByDefault({}) String lightId;
85
86     private @Nullable FullLight lastFullLight;
87     private long endBypassTime = 0L;
88
89     private @Nullable Integer lastSentColorTemp;
90     private @Nullable Integer lastSentBrightness;
91
92     // Flag to indicate whether the bulb is of type Osram par16 50 TW or not
93     private boolean isOsramPar16 = false;
94
95     private boolean propertiesInitializedSuccessfully = false;
96     private boolean capabilitiesInitializedSuccessfully = false;
97     private ColorTemperature colorTemperatureCapabilties = new ColorTemperature();
98     private long defaultFadeTime = 400;
99
100     private @Nullable HueClient hueClient;
101
102     private @Nullable ScheduledFuture<?> scheduledFuture;
103
104     public HueLightHandler(Thing hueLight, HueStateDescriptionProvider stateDescriptionProvider) {
105         super(hueLight);
106         this.stateDescriptionProvider = stateDescriptionProvider;
107     }
108
109     @Override
110     public void initialize() {
111         logger.debug("Initializing hue light handler.");
112         Bridge bridge = getBridge();
113         initializeThing((bridge == null) ? null : bridge.getStatus());
114     }
115
116     @Override
117     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
118         logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
119         initializeThing(bridgeStatusInfo.getStatus());
120     }
121
122     private void initializeThing(@Nullable ThingStatus bridgeStatus) {
123         logger.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus);
124         final String configLightId = (String) getConfig().get(LIGHT_ID);
125         if (configLightId != null) {
126             BigDecimal time = (BigDecimal) getConfig().get(FADETIME);
127             if (time != null) {
128                 defaultFadeTime = time.longValueExact();
129             }
130
131             lightId = configLightId;
132             // note: this call implicitly registers our handler as a listener on the bridge
133             HueClient bridgeHandler = getHueClient();
134             if (bridgeHandler != null) {
135                 if (bridgeStatus == ThingStatus.ONLINE) {
136                     FullLight fullLight = bridgeHandler.getLightById(lightId);
137                     initializeProperties(fullLight);
138                     initializeCapabilities(fullLight);
139                     updateStatus(ThingStatus.ONLINE);
140                 } else {
141                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
142                 }
143             } else {
144                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
145             }
146         } else {
147             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
148                     "@text/offline.conf-error-no-light-id");
149         }
150     }
151
152     private synchronized void initializeProperties(@Nullable FullLight fullLight) {
153         if (!propertiesInitializedSuccessfully && fullLight != null) {
154             Map<String, String> properties = editProperties();
155             String softwareVersion = fullLight.getSoftwareVersion();
156             if (softwareVersion != null) {
157                 properties.put(PROPERTY_FIRMWARE_VERSION, softwareVersion);
158             }
159             String modelId = fullLight.getNormalizedModelID();
160             if (modelId != null) {
161                 properties.put(PROPERTY_MODEL_ID, modelId);
162             }
163             properties.put(PROPERTY_VENDOR, fullLight.getManufacturerName());
164             properties.put(PRODUCT_NAME, fullLight.getProductName());
165             String uniqueID = fullLight.getUniqueID();
166             if (uniqueID != null) {
167                 properties.put(UNIQUE_ID, uniqueID);
168             }
169             updateProperties(properties);
170             isOsramPar16 = OSRAM_PAR16_50_TW_MODEL_ID.equals(modelId);
171             propertiesInitializedSuccessfully = true;
172         }
173     }
174
175     private void initializeCapabilities(@Nullable FullLight fullLight) {
176         if (!capabilitiesInitializedSuccessfully && fullLight != null) {
177             Capabilities capabilities = fullLight.capabilities;
178             if (capabilities != null) {
179                 ColorTemperature ct = capabilities.control.ct;
180                 if (ct != null) {
181                     colorTemperatureCapabilties = ct;
182
183                     // minimum and maximum are inverted due to mired/Kelvin conversion!
184                     StateDescriptionFragment stateDescriptionFragment = StateDescriptionFragmentBuilder.create()
185                             .withMinimum(new BigDecimal(LightStateConverter.miredToKelvin(ct.max))) //
186                             .withMaximum(new BigDecimal(LightStateConverter.miredToKelvin(ct.min))) //
187                             .withStep(new BigDecimal(100)) //
188                             .withPattern("%.0f K") //
189                             .build();
190                     stateDescriptionProvider.setStateDescriptionFragment(
191                             new ChannelUID(thing.getUID(), CHANNEL_COLORTEMPERATURE_ABS), stateDescriptionFragment);
192                 }
193             }
194             capabilitiesInitializedSuccessfully = true;
195         }
196     }
197
198     @Override
199     public void dispose() {
200         logger.debug("Hue light handler disposes. Unregistering listener.");
201         cancelScheduledFuture();
202         if (lightId != null) {
203             HueClient bridgeHandler = getHueClient();
204             if (bridgeHandler != null) {
205                 bridgeHandler.unregisterLightStatusListener(this);
206                 hueClient = null;
207             }
208             lightId = null;
209         }
210     }
211
212     @Override
213     public void handleCommand(ChannelUID channelUID, Command command) {
214         handleCommand(channelUID.getId(), command, defaultFadeTime);
215     }
216
217     @Override
218     public void handleCommand(String channel, Command command, long fadeTime) {
219         HueClient bridgeHandler = getHueClient();
220         if (bridgeHandler == null) {
221             logger.warn("hue bridge handler not found. Cannot handle command without bridge.");
222             return;
223         }
224
225         final FullLight light = lastFullLight == null ? bridgeHandler.getLightById(lightId) : lastFullLight;
226         if (light == null) {
227             logger.debug("hue light not known on bridge. Cannot handle command.");
228             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
229                     "@text/offline.conf-error-wrong-light-id");
230             return;
231         }
232
233         Integer lastColorTemp;
234         StateUpdate newState = null;
235         switch (channel) {
236             case CHANNEL_COLORTEMPERATURE:
237                 if (command instanceof PercentType) {
238                     newState = LightStateConverter.toColorTemperatureLightStateFromPercentType((PercentType) command,
239                             colorTemperatureCapabilties);
240                     newState.setTransitionTime(fadeTime);
241                 } else if (command instanceof OnOffType) {
242                     newState = LightStateConverter.toOnOffLightState((OnOffType) command);
243                     if (isOsramPar16) {
244                         newState = addOsramSpecificCommands(newState, (OnOffType) command);
245                     }
246                 } else if (command instanceof IncreaseDecreaseType) {
247                     newState = convertColorTempChangeToStateUpdate((IncreaseDecreaseType) command, light);
248                     if (newState != null) {
249                         newState.setTransitionTime(fadeTime);
250                     }
251                 }
252                 break;
253             case CHANNEL_COLORTEMPERATURE_ABS:
254                 if (command instanceof DecimalType) {
255                     newState = LightStateConverter.toColorTemperatureLightState((DecimalType) command,
256                             colorTemperatureCapabilties);
257                     newState.setTransitionTime(fadeTime);
258                 }
259                 break;
260             case CHANNEL_BRIGHTNESS:
261                 if (command instanceof PercentType) {
262                     newState = LightStateConverter.toBrightnessLightState((PercentType) command);
263                     newState.setTransitionTime(fadeTime);
264                 } else if (command instanceof OnOffType) {
265                     newState = LightStateConverter.toOnOffLightState((OnOffType) command);
266                     if (isOsramPar16) {
267                         newState = addOsramSpecificCommands(newState, (OnOffType) command);
268                     }
269                 } else if (command instanceof IncreaseDecreaseType) {
270                     newState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light);
271                     if (newState != null) {
272                         newState.setTransitionTime(fadeTime);
273                     }
274                 }
275                 lastColorTemp = lastSentColorTemp;
276                 if (newState != null && lastColorTemp != null) {
277                     // make sure that the light also has the latest color temp
278                     // this might not have been yet set in the light, if it was off
279                     newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
280                     newState.setTransitionTime(fadeTime);
281                 }
282                 break;
283             case CHANNEL_SWITCH:
284                 if (command instanceof OnOffType) {
285                     newState = LightStateConverter.toOnOffLightState((OnOffType) command);
286                     if (isOsramPar16) {
287                         newState = addOsramSpecificCommands(newState, (OnOffType) command);
288                     }
289                 }
290                 lastColorTemp = lastSentColorTemp;
291                 if (newState != null && lastColorTemp != null) {
292                     // make sure that the light also has the latest color temp
293                     // this might not have been yet set in the light, if it was off
294                     newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
295                     newState.setTransitionTime(fadeTime);
296                 }
297                 break;
298             case CHANNEL_COLOR:
299                 if (command instanceof HSBType) {
300                     HSBType hsbCommand = (HSBType) command;
301                     if (hsbCommand.getBrightness().intValue() == 0) {
302                         newState = LightStateConverter.toOnOffLightState(OnOffType.OFF);
303                     } else {
304                         newState = LightStateConverter.toColorLightState(hsbCommand, light.getState());
305                         newState.setTransitionTime(fadeTime);
306                     }
307                 } else if (command instanceof PercentType) {
308                     newState = LightStateConverter.toBrightnessLightState((PercentType) command);
309                     newState.setTransitionTime(fadeTime);
310                 } else if (command instanceof OnOffType) {
311                     newState = LightStateConverter.toOnOffLightState((OnOffType) command);
312                 } else if (command instanceof IncreaseDecreaseType) {
313                     newState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light);
314                     if (newState != null) {
315                         newState.setTransitionTime(fadeTime);
316                     }
317                 }
318                 break;
319             case CHANNEL_ALERT:
320                 if (command instanceof StringType) {
321                     newState = LightStateConverter.toAlertState((StringType) command);
322                     if (newState == null) {
323                         // Unsupported StringType is passed. Log a warning
324                         // message and return.
325                         logger.warn("Unsupported String command: {}. Supported commands are: {}, {}, {} ", command,
326                                 LightStateConverter.ALERT_MODE_NONE, LightStateConverter.ALERT_MODE_SELECT,
327                                 LightStateConverter.ALERT_MODE_LONG_SELECT);
328                         return;
329                     } else {
330                         scheduleAlertStateRestore(command);
331                     }
332                 }
333                 break;
334             case CHANNEL_EFFECT:
335                 if (command instanceof OnOffType) {
336                     newState = LightStateConverter.toOnOffEffectState((OnOffType) command);
337                 }
338                 break;
339         }
340         if (newState != null) {
341             // Cache values which we have sent
342             Integer tmpBrightness = newState.getBrightness();
343             if (tmpBrightness != null) {
344                 lastSentBrightness = tmpBrightness;
345             }
346             Integer tmpColorTemp = newState.getColorTemperature();
347             if (tmpColorTemp != null) {
348                 lastSentColorTemp = tmpColorTemp;
349             }
350             bridgeHandler.updateLightState(this, light, newState, fadeTime);
351         } else {
352             logger.warn("Command sent to an unknown channel id: {}:{}", getThing().getUID(), channel);
353         }
354     }
355
356     /*
357      * Applies additional {@link StateUpdate} commands as a workaround for Osram
358      * Lightify PAR16 TW firmware bug. Also see
359      * http://www.everyhue.com/vanilla/discussion/1756/solved-lightify-turning-off
360      */
361     private StateUpdate addOsramSpecificCommands(StateUpdate lightState, OnOffType actionType) {
362         if (actionType.equals(OnOffType.ON)) {
363             lightState.setBrightness(254);
364         } else {
365             lightState.setTransitionTime(0);
366         }
367         return lightState;
368     }
369
370     private @Nullable StateUpdate convertColorTempChangeToStateUpdate(IncreaseDecreaseType command, FullLight light) {
371         StateUpdate stateUpdate = null;
372         Integer currentColorTemp = getCurrentColorTemp(light.getState());
373         if (currentColorTemp != null) {
374             int newColorTemp = LightStateConverter.toAdjustedColorTemp(command, currentColorTemp,
375                     colorTemperatureCapabilties);
376             stateUpdate = new StateUpdate().setColorTemperature(newColorTemp, colorTemperatureCapabilties);
377         }
378         return stateUpdate;
379     }
380
381     private @Nullable Integer getCurrentColorTemp(@Nullable State lightState) {
382         Integer colorTemp = lastSentColorTemp;
383         if (colorTemp == null && lightState != null) {
384             return lightState.getColorTemperature();
385         }
386         return colorTemp;
387     }
388
389     private @Nullable StateUpdate convertBrightnessChangeToStateUpdate(IncreaseDecreaseType command, FullLight light) {
390         Integer currentBrightness = getCurrentBrightness(light.getState());
391         if (currentBrightness == null) {
392             return null;
393         }
394         int newBrightness = LightStateConverter.toAdjustedBrightness(command, currentBrightness);
395         return createBrightnessStateUpdate(currentBrightness, newBrightness);
396     }
397
398     private @Nullable Integer getCurrentBrightness(@Nullable State lightState) {
399         if (lastSentBrightness == null && lightState != null) {
400             return lightState.isOn() ? lightState.getBrightness() : 0;
401         }
402         return lastSentBrightness;
403     }
404
405     private StateUpdate createBrightnessStateUpdate(int currentBrightness, int newBrightness) {
406         StateUpdate lightUpdate = new StateUpdate();
407         if (newBrightness == 0) {
408             lightUpdate.turnOff();
409         } else {
410             lightUpdate.setBrightness(newBrightness);
411             if (currentBrightness == 0) {
412                 lightUpdate.turnOn();
413             }
414         }
415         return lightUpdate;
416     }
417
418     protected synchronized @Nullable HueClient getHueClient() {
419         if (hueClient == null) {
420             Bridge bridge = getBridge();
421             if (bridge == null) {
422                 return null;
423             }
424             ThingHandler handler = bridge.getHandler();
425             if (handler instanceof HueClient) {
426                 HueClient bridgeHandler = (HueClient) handler;
427                 hueClient = bridgeHandler;
428                 bridgeHandler.registerLightStatusListener(this);
429             } else {
430                 return null;
431             }
432         }
433         return hueClient;
434     }
435
436     @Override
437     public void setPollBypass(long bypassTime) {
438         endBypassTime = System.currentTimeMillis() + bypassTime;
439     }
440
441     @Override
442     public void unsetPollBypass() {
443         endBypassTime = 0L;
444     }
445
446     @Override
447     public boolean onLightStateChanged(FullLight fullLight) {
448         logger.trace("onLightStateChanged() was called");
449
450         if (System.currentTimeMillis() <= endBypassTime) {
451             logger.debug("Bypass light update after command ({}).", lightId);
452             return false;
453         }
454
455         State state = fullLight.getState();
456
457         final FullLight lastState = lastFullLight;
458         if (lastState == null || !Objects.equals(lastState.getState(), state)) {
459             lastFullLight = fullLight;
460         } else {
461             return true;
462         }
463
464         logger.trace("New state for light {}", lightId);
465
466         initializeProperties(fullLight);
467
468         lastSentColorTemp = null;
469         lastSentBrightness = null;
470
471         // update status (ONLINE, OFFLINE)
472         if (state.isReachable()) {
473             updateStatus(ThingStatus.ONLINE);
474         } else {
475             // we assume OFFLINE without any error (NONE), as this is an
476             // expected state (when bulb powered off)
477             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.light-not-reachable");
478         }
479
480         logger.debug("onLightStateChanged Light {}: on {} bri {} hue {} sat {} temp {} mode {} XY {}",
481                 fullLight.getName(), state.isOn(), state.getBrightness(), state.getHue(), state.getSaturation(),
482                 state.getColorTemperature(), state.getColorMode(), state.getXY());
483
484         HSBType hsbType = LightStateConverter.toHSBType(state);
485         if (!state.isOn()) {
486             hsbType = new HSBType(hsbType.getHue(), hsbType.getSaturation(), PercentType.ZERO);
487         }
488         updateState(CHANNEL_COLOR, hsbType);
489
490         PercentType brightnessPercentType = state.isOn() ? LightStateConverter.toBrightnessPercentType(state)
491                 : PercentType.ZERO;
492         updateState(CHANNEL_BRIGHTNESS, brightnessPercentType);
493
494         updateState(CHANNEL_SWITCH, OnOffType.from(state.isOn()));
495
496         updateState(CHANNEL_COLORTEMPERATURE,
497                 LightStateConverter.toColorTemperaturePercentType(state, colorTemperatureCapabilties));
498         updateState(CHANNEL_COLORTEMPERATURE_ABS, LightStateConverter.toColorTemperature(state));
499
500         StringType stringType = LightStateConverter.toAlertStringType(state);
501         if (!"NULL".equals(stringType.toString())) {
502             updateState(CHANNEL_ALERT, stringType);
503             scheduleAlertStateRestore(stringType);
504         }
505
506         return true;
507     }
508
509     @Override
510     public void channelLinked(ChannelUID channelUID) {
511         HueClient handler = getHueClient();
512         if (handler != null) {
513             FullLight light = handler.getLightById(lightId);
514             if (light != null) {
515                 onLightStateChanged(light);
516             }
517         }
518     }
519
520     @Override
521     public void onLightRemoved() {
522         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.light-removed");
523     }
524
525     @Override
526     public void onLightGone() {
527         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "@text/offline.light-not-reachable");
528     }
529
530     @Override
531     public void onLightAdded(FullLight light) {
532         onLightStateChanged(light);
533     }
534
535     /**
536      * Schedules restoration of the alert item state to {@link LightStateConverter#ALERT_MODE_NONE} after a given time.
537      * <br>
538      * Based on the initial command:
539      * <ul>
540      * <li>For {@link LightStateConverter#ALERT_MODE_SELECT} restoration will be triggered after <strong>2
541      * seconds</strong>.
542      * <li>For {@link LightStateConverter#ALERT_MODE_LONG_SELECT} restoration will be triggered after <strong>15
543      * seconds</strong>.
544      * </ul>
545      * This method also cancels any previously scheduled restoration.
546      *
547      * @param command The {@link Command} sent to the item
548      */
549     private void scheduleAlertStateRestore(Command command) {
550         cancelScheduledFuture();
551         int delay = getAlertDuration(command);
552
553         if (delay > 0) {
554             scheduledFuture = scheduler.schedule(() -> {
555                 updateState(CHANNEL_ALERT, new StringType(LightStateConverter.ALERT_MODE_NONE));
556             }, delay, TimeUnit.MILLISECONDS);
557         }
558     }
559
560     /**
561      * This method will cancel previously scheduled alert item state
562      * restoration.
563      */
564     private void cancelScheduledFuture() {
565         ScheduledFuture<?> scheduledJob = scheduledFuture;
566         if (scheduledJob != null) {
567             scheduledJob.cancel(true);
568             scheduledFuture = null;
569         }
570     }
571
572     /**
573      * This method returns the time in <strong>milliseconds</strong> after
574      * which, the state of the alert item has to be restored to {@link LightStateConverter#ALERT_MODE_NONE}.
575      *
576      * @param command The initial command sent to the alert item.
577      * @return Based on the initial command will return:
578      *         <ul>
579      *         <li><strong>2000</strong> for {@link LightStateConverter#ALERT_MODE_SELECT}.
580      *         <li><strong>15000</strong> for {@link LightStateConverter#ALERT_MODE_LONG_SELECT}.
581      *         <li><strong>-1</strong> for any command different from the previous two.
582      *         </ul>
583      */
584     private int getAlertDuration(Command command) {
585         int delay;
586         switch (command.toString()) {
587             case LightStateConverter.ALERT_MODE_LONG_SELECT:
588                 delay = 15000;
589                 break;
590             case LightStateConverter.ALERT_MODE_SELECT:
591                 delay = 2000;
592                 break;
593             default:
594                 delay = -1;
595                 break;
596         }
597
598         return delay;
599     }
600
601     @Override
602     public String getLightId() {
603         return lightId;
604     }
605 }