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