]> git.basschouten.com Git - openhab-addons.git/blob
b30374ab12ca97a00aa61e6fc0ac93db1dfddd7e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.wled.internal.api;
14
15 import static org.openhab.binding.wled.internal.WLedBindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.util.ArrayList;
20 import java.util.Comparator;
21 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.TimeoutException;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.eclipse.jetty.client.api.ContentResponse;
30 import org.eclipse.jetty.client.api.Request;
31 import org.eclipse.jetty.client.util.StringContentProvider;
32 import org.eclipse.jetty.http.HttpHeader;
33 import org.eclipse.jetty.http.HttpMethod;
34 import org.openhab.binding.wled.internal.WLedHelper;
35 import org.openhab.binding.wled.internal.WledState;
36 import org.openhab.binding.wled.internal.WledState.InfoResponse;
37 import org.openhab.binding.wled.internal.WledState.JsonResponse;
38 import org.openhab.binding.wled.internal.WledState.LedInfo;
39 import org.openhab.binding.wled.internal.WledState.StateResponse;
40 import org.openhab.binding.wled.internal.handlers.WLedBridgeHandler;
41 import org.openhab.core.library.types.DecimalType;
42 import org.openhab.core.library.types.HSBType;
43 import org.openhab.core.library.types.OnOffType;
44 import org.openhab.core.library.types.PercentType;
45 import org.openhab.core.library.types.QuantityType;
46 import org.openhab.core.library.types.StringType;
47 import org.openhab.core.library.unit.Units;
48 import org.openhab.core.types.StateOption;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import com.google.gson.Gson;
53 import com.google.gson.JsonSyntaxException;
54
55 /**
56  * The {@link WledApiV084} is the json Api methods for firmware version 0.8.4 and newer
57  * as newer firmwares come out with breaking changes, extend this class into a newer firmware version class.
58  *
59  * @author Matthew Skinner - Initial contribution
60  */
61 @NonNullByDefault
62 public class WledApiV084 implements WledApi {
63     protected final Logger logger = LoggerFactory.getLogger(this.getClass());
64     protected final Gson gson = new Gson();
65     protected final HttpClient httpClient;
66     protected final WLedBridgeHandler handler;
67     protected final String address;
68     protected WledState state = new WledState();
69     private int version = 0;
70
71     public WledApiV084(WLedBridgeHandler handler, HttpClient httpClient) {
72         this.handler = handler;
73         this.address = handler.config.address;
74         this.httpClient = httpClient;
75     }
76
77     @Override
78     public void initialize() throws ApiException {
79         state.jsonResponse = getJson();
80         state.infoResponse = getInfo();
81         @Nullable
82         LedInfo localLedInfo = gson.fromJson(state.infoResponse.leds.toString(), LedInfo.class);
83         if (localLedInfo != null) {
84             state.ledInfo = localLedInfo;
85         }
86         handler.hasWhite = state.ledInfo.rgbw;
87     }
88
89     @Override
90     public String sendGetRequest(String url) throws ApiException {
91         Request request = httpClient.newRequest(address + url);
92         request.timeout(3, TimeUnit.SECONDS);
93         request.method(HttpMethod.GET);
94         request.header(HttpHeader.ACCEPT_ENCODING, "gzip");
95         logger.trace("Sending WLED GET:{}", url);
96         String errorReason = "";
97         try {
98             ContentResponse contentResponse = request.send();
99             if (contentResponse.getStatus() == 200) {
100                 return contentResponse.getContentAsString();
101             } else {
102                 errorReason = String.format("WLED request failed with %d: %s", contentResponse.getStatus(),
103                         contentResponse.getReason());
104             }
105         } catch (TimeoutException e) {
106             errorReason = "TimeoutException: WLED was not reachable on your network";
107         } catch (ExecutionException e) {
108             errorReason = String.format("ExecutionException: %s", e.getMessage());
109         } catch (InterruptedException e) {
110             Thread.currentThread().interrupt();
111             errorReason = String.format("InterruptedException: %s", e.getMessage());
112         }
113         throw new ApiException(errorReason);
114     }
115
116     protected String postState(String json) throws ApiException {
117         return sendPostRequest("/json/state", json);
118     }
119
120     protected String sendPostRequest(String url, String json) throws ApiException {
121         logger.debug("Sending WLED POST:{} Message:{}", url, json);
122         Request request = httpClient.POST(address + url);
123         request.timeout(3, TimeUnit.SECONDS);
124         request.header(HttpHeader.CONTENT_TYPE, "application/json");
125         request.content(new StringContentProvider(json), "application/json");
126         String errorReason = "";
127         try {
128             ContentResponse contentResponse = request.send();
129             if (contentResponse.getStatus() == 200) {
130                 return contentResponse.getContentAsString();
131             } else {
132                 errorReason = String.format("WLED request failed with %d: %s", contentResponse.getStatus(),
133                         contentResponse.getReason());
134             }
135         } catch (InterruptedException e) {
136             errorReason = String.format("InterruptedException: %s", e.getMessage());
137         } catch (TimeoutException e) {
138             errorReason = "TimeoutException: WLED was not reachable on your network";
139         } catch (ExecutionException e) {
140             errorReason = String.format("ExecutionException: %s", e.getMessage());
141         }
142         throw new ApiException(errorReason);
143     }
144
145     protected void updateStateFromReply(String jsonState) {
146         try {
147             StateResponse response = gson.fromJson(jsonState, StateResponse.class);
148             if (response == null) {
149                 throw new ApiException("Reply back from WLED when command was made is not valid JSON");
150             }
151             state.stateResponse = response;
152             state.unpackJsonObjects();
153             processBridgeStates();
154             for (int count = 0; count < state.stateResponse.seg.length; count++) {
155                 processState(count);
156             }
157         } catch (JsonSyntaxException | ApiException e) {
158             logger.debug("Reply back when a command was sent triggered an exception:{}", jsonState);
159         }
160     }
161
162     protected StateResponse getState() throws ApiException {
163         try {
164             String returnContent = sendGetRequest("/json/state");
165             StateResponse response = gson.fromJson(returnContent, StateResponse.class);
166             if (response == null) {
167                 throw new ApiException("Could not GET:/json/state");
168             }
169             logger.trace("json/state:{}", returnContent);
170             return response;
171         } catch (JsonSyntaxException e) {
172             throw new ApiException("JsonSyntaxException:{}", e);
173         }
174     }
175
176     protected InfoResponse getInfo() throws ApiException {
177         try {
178             String returnContent = sendGetRequest("/json/info");
179             InfoResponse response = gson.fromJson(returnContent, InfoResponse.class);
180             if (response == null) {
181                 throw new ApiException("Could not GET:/json/info");
182             }
183             logger.trace("/json/info:{}", returnContent);
184             return response;
185         } catch (JsonSyntaxException e) {
186             throw new ApiException("JsonSyntaxException:{}", e);
187         }
188     }
189
190     protected JsonResponse getJson() throws ApiException {
191         try {
192             String returnContent = sendGetRequest("/json");
193             JsonResponse response = gson.fromJson(returnContent, JsonResponse.class);
194             if (response == null) {
195                 throw new ApiException("Could not GET:/json");
196             }
197             return response;
198         } catch (JsonSyntaxException e) {
199             throw new ApiException("JsonSyntaxException:{}", e);
200         }
201     }
202
203     @Override
204     public void update() throws ApiException {
205         state.stateResponse = getState();
206         state.unpackJsonObjects();
207         processBridgeStates();
208         for (int count = 0; count < state.stateResponse.seg.length; count++) {
209             processState(count);
210         }
211     }
212
213     @Override
214     public List<StateOption> getUpdatedFxList() {
215         List<StateOption> fxOptions = new ArrayList<>();
216         int counter = 0;
217         for (String value : state.jsonResponse.effects) {
218             fxOptions.add(new StateOption(Integer.toString(counter++), value));
219         }
220         if (handler.config.sortEffects) {
221             fxOptions.sort(Comparator.comparing(o -> "0".equals(o.getValue()) ? "" : o.getLabel()));
222         }
223         return fxOptions;
224     }
225
226     @Override
227     public List<StateOption> getUpdatedPaletteList() {
228         List<StateOption> palleteOptions = new ArrayList<>();
229         int counter = 0;
230         for (String value : state.jsonResponse.palettes) {
231             palleteOptions.add(new StateOption(Integer.toString(counter++), value));
232         }
233         if (handler.config.sortPalettes) {
234             palleteOptions.sort(Comparator.comparing(o -> "0".equals(o.getValue()) ? "" : o.getLabel()));
235         }
236         return palleteOptions;
237     }
238
239     @Override
240     public int getFirmwareVersion() throws ApiException {
241         state.infoResponse = getInfo();
242         String temp = state.infoResponse.ver;
243         logger.debug("Firmware for WLED is ver:{}", temp);
244         temp = temp.replace(".", "");
245         if (temp.length() > 4) {
246             temp = temp.substring(0, 4);
247         }
248         version = Integer.parseInt(temp);
249         return version;
250     }
251
252     protected void processBridgeStates() throws ApiException {
253         if (!state.stateResponse.on) {
254             handler.update(CHANNEL_GLOBAL_BRIGHTNESS, OnOffType.OFF);
255         } else {
256             handler.update(CHANNEL_GLOBAL_BRIGHTNESS, new PercentType(
257                     new BigDecimal(state.stateResponse.bri).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP)));
258         }
259         handler.update(CHANNEL_LIVE_OVERRIDE, new StringType(Integer.toString(state.stateResponse.lor)));
260         handler.update(CHANNEL_PRESETS, new StringType(Integer.toString(state.stateResponse.ps)));
261         if (state.nightLightState.on) {
262             handler.update(CHANNEL_SLEEP, OnOffType.ON);
263         } else {
264             handler.update(CHANNEL_SLEEP, OnOffType.OFF);
265         }
266         if (state.udpnState.recv) {
267             handler.update(CHANNEL_SYNC_RECEIVE, OnOffType.ON);
268         } else {
269             handler.update(CHANNEL_SYNC_RECEIVE, OnOffType.OFF);
270         }
271         if (state.udpnState.send) {
272             handler.update(CHANNEL_SYNC_SEND, OnOffType.ON);
273         } else {
274             handler.update(CHANNEL_SYNC_SEND, OnOffType.OFF);
275         }
276         if (state.stateResponse.pl == 0) {
277             handler.update(CHANNEL_PRESET_CYCLE, OnOffType.ON);
278         } else {
279             handler.update(CHANNEL_PRESET_CYCLE, OnOffType.OFF);
280         }
281         handler.update(CHANNEL_TRANS_TIME, new QuantityType<>(
282                 new BigDecimal(state.stateResponse.transition).divide(BigDecimal.TEN), Units.SECOND));
283         handler.update(CHANNEL_SLEEP_DURATION,
284                 new QuantityType<>(new BigDecimal(state.nightLightState.dur), Units.MINUTE));
285         handler.update(CHANNEL_SLEEP_BRIGHTNESS, new PercentType(
286                 new BigDecimal(state.nightLightState.tbri).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP)));
287         handler.update(CHANNEL_SLEEP_MODE, new StringType(Integer.toString(state.nightLightState.mode)));
288     }
289
290     protected void processState(int segmentIndex) throws ApiException {
291         if (state.stateResponse.seg.length <= segmentIndex) {
292             throw new ApiException(
293                     "Segment " + segmentIndex + " is not currently setup correctly in the WLED firmware");
294         }
295         if (handler.handlerMissing(segmentIndex)) {
296             // There is no thing setup for this segmentIndex.
297             return;
298         }
299         HSBType tempHSB = WLedHelper.parseToHSBType(state.stateResponse.seg[segmentIndex].col[0].toString());
300         handler.update(segmentIndex, CHANNEL_PRIMARY_COLOR, tempHSB);
301         handler.update(segmentIndex, CHANNEL_SECONDARY_COLOR,
302                 WLedHelper.parseToHSBType(state.stateResponse.seg[segmentIndex].col[1].toString()));
303         handler.update(segmentIndex, CHANNEL_THIRD_COLOR,
304                 WLedHelper.parseToHSBType(state.stateResponse.seg[segmentIndex].col[2].toString()));
305         if (state.ledInfo.rgbw) {
306             handler.update(segmentIndex, CHANNEL_PRIMARY_WHITE,
307                     WLedHelper.parseWhitePercent(state.stateResponse.seg[segmentIndex].col[0].toString()));
308             handler.update(segmentIndex, CHANNEL_SECONDARY_WHITE,
309                     WLedHelper.parseWhitePercent(state.stateResponse.seg[segmentIndex].col[1].toString()));
310             handler.update(segmentIndex, CHANNEL_THIRD_WHITE,
311                     WLedHelper.parseWhitePercent(state.stateResponse.seg[segmentIndex].col[2].toString()));
312         }
313         // Global OFF or Segment OFF needs to be treated as OFF
314         if (!state.stateResponse.seg[segmentIndex].on || !state.stateResponse.on) {
315             handler.update(segmentIndex, CHANNEL_MASTER_CONTROLS, OnOffType.OFF);
316             handler.update(segmentIndex, CHANNEL_SEGMENT_BRIGHTNESS, OnOffType.OFF);
317         } else {
318             handler.update(segmentIndex, CHANNEL_MASTER_CONTROLS, tempHSB);
319             handler.update(segmentIndex, CHANNEL_SEGMENT_BRIGHTNESS,
320                     new PercentType(new BigDecimal(state.stateResponse.seg[segmentIndex].bri).divide(BIG_DECIMAL_2_55,
321                             RoundingMode.HALF_UP)));
322         }
323         if (state.stateResponse.seg[segmentIndex].mi) {
324             handler.update(segmentIndex, CHANNEL_MIRROR, OnOffType.ON);
325         } else {
326             handler.update(segmentIndex, CHANNEL_MIRROR, OnOffType.OFF);
327         }
328         if (state.stateResponse.seg[segmentIndex].rev) {
329             handler.update(segmentIndex, CHANNEL_REVERSE, OnOffType.ON);
330         } else {
331             handler.update(segmentIndex, CHANNEL_REVERSE, OnOffType.OFF);
332         }
333         handler.update(segmentIndex, CHANNEL_FX,
334                 new StringType(Integer.toString(state.stateResponse.seg[segmentIndex].fx)));
335         handler.update(segmentIndex, CHANNEL_PALETTES,
336                 new StringType(Integer.toString(state.stateResponse.seg[segmentIndex].pal)));
337         handler.update(segmentIndex, CHANNEL_SPEED,
338                 new PercentType(new BigDecimal(state.stateResponse.seg[segmentIndex].sx).divide(BIG_DECIMAL_2_55,
339                         RoundingMode.HALF_UP)));
340         handler.update(segmentIndex, CHANNEL_INTENSITY,
341                 new PercentType(new BigDecimal(state.stateResponse.seg[segmentIndex].ix).divide(BIG_DECIMAL_2_55,
342                         RoundingMode.HALF_UP)));
343         handler.update(segmentIndex, CHANNEL_GROUPING, new DecimalType(state.stateResponse.seg[segmentIndex].grp));
344         handler.update(segmentIndex, CHANNEL_SPACING, new DecimalType(state.stateResponse.seg[segmentIndex].spc));
345     }
346
347     @Override
348     public void setGlobalOn(boolean bool) throws ApiException {
349         updateStateFromReply(postState("{\"on\":" + bool + ",\"v\":true,\"tt\":2}"));
350     }
351
352     @Override
353     public void setMasterOn(boolean bool, int segmentIndex) throws ApiException {
354         updateStateFromReply(
355                 postState("{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":" + bool + "}]}"));
356     }
357
358     @Override
359     public void setGlobalBrightness(PercentType percent) throws ApiException {
360         if (percent.equals(PercentType.ZERO)) {
361             updateStateFromReply(postState("{\"on\":false,\"v\":true}"));
362             return;
363         }
364         updateStateFromReply(postState("{\"on\":true,\"v\":true,\"tt\":2,\"bri\":"
365                 + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}"));
366     }
367
368     @Override
369     public void setMasterBrightness(PercentType percent, int segmentIndex) throws ApiException {
370         if (percent.equals(PercentType.ZERO)) {
371             updateStateFromReply(postState("{\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":false}]}"));
372             return;
373         }
374         updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":true,\"bri\":"
375                 + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"));
376     }
377
378     @Override
379     public void setMasterHSB(HSBType hsbType, int segmentIndex) throws ApiException {
380         if (hsbType.getBrightness().toBigDecimal().equals(BigDecimal.ZERO)) {
381             updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + segmentIndex
382                     + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue()
383                     + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
384                     + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"));
385             return;
386         }
387         updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + segmentIndex
388                 + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
389                 + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
390                 + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"));
391     }
392
393     @Override
394     public void setEffect(String string, int segmentIndex) throws ApiException {
395         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"fx\":" + string + "}]}");
396     }
397
398     @Override
399     public void setPreset(String string) throws ApiException {
400         updateStateFromReply(postState("{\"ps\":" + string + ",\"v\":true}"));
401     }
402
403     @Override
404     public void setPalette(String string, int segmentIndex) throws ApiException {
405         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"pal\":" + string + "}]}");
406     }
407
408     @Override
409     public void setFxIntencity(PercentType percentType, int segmentIndex) throws ApiException {
410         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"ix\":"
411                 + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}");
412     }
413
414     @Override
415     public void setFxSpeed(PercentType percentType, int segmentIndex) throws ApiException {
416         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"sx\":"
417                 + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}");
418     }
419
420     @Override
421     public void setSleep(boolean bool) throws ApiException {
422         postState("{\"nl\":{\"on\":" + bool + "}}");
423     }
424
425     @Override
426     public void setUdpSend(boolean bool) throws ApiException {
427         postState("{\"udpn\":{\"send\":" + bool + "}}");
428     }
429
430     @Override
431     public void setUdpRecieve(boolean bool) throws ApiException {
432         postState("{\"udpn\":{\"recv\":" + bool + "}}");
433     }
434
435     @Override
436     public void setTransitionTime(BigDecimal time) throws ApiException {
437         postState("{\"transition\":" + time + "}");
438     }
439
440     @Override
441     public void setPresetCycle(boolean bool) throws ApiException {
442         if (bool) {
443             postState("{\"pl\":0}");
444         } else {
445             postState("{\"pl\":-1}");
446         }
447     }
448
449     @Override
450     public void setPrimaryColor(HSBType hsbType, int segmentIndex) throws ApiException {
451         postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[["
452                 + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
453                 + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
454                 + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[],[]]}]}");
455     }
456
457     @Override
458     public void setSecondaryColor(HSBType hsbType, int segmentIndex) throws ApiException {
459         postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[[],["
460                 + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
461                 + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
462                 + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[]]}]}");
463     }
464
465     @Override
466     public void setTertiaryColor(HSBType hsbType, int segmentIndex) throws ApiException {
467         postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[[],[],["
468                 + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
469                 + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + ","
470                 + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}");
471     }
472
473     @Override
474     public void setWhiteOnly(PercentType percentType, int segmentIndex) throws ApiException {
475         postState("{\"seg\":[{\"on\":true,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[0,0,0,"
476                 + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}");
477     }
478
479     @Override
480     public void setMirror(boolean bool, int segmentIndex) throws ApiException {
481         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"mi\":" + bool + "}]}");
482     }
483
484     @Override
485     public void setReverse(boolean bool, int segmentIndex) throws ApiException {
486         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"rev\":" + bool + "}]}");
487     }
488
489     @Override
490     public void savePreset(int position, String presetName) throws ApiException {
491         // named presets not supported in older firmwares, and max of 16.
492         if (position > 16 || position < 1) {
493             logger.warn("Preset position {} is not supported in this firmware version", position);
494             return;
495         }
496         try {
497             sendGetRequest("/win&PS=" + position);
498         } catch (ApiException e) {
499             logger.warn("Preset failed to save:{}", e.getMessage());
500         }
501     }
502
503     @Override
504     public void setLiveOverride(String value) throws ApiException {
505         postState("{\"lor\":" + value + "}");
506     }
507
508     @Override
509     public void setGrouping(int value, int segmentIndex) throws ApiException {
510         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"grp\":" + value + "}]}");
511     }
512
513     @Override
514     public void setSpacing(int value, int segmentIndex) throws ApiException {
515         postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"spc\":" + value + "}]}");
516     }
517
518     @Override
519     public List<String> getSegmentNames() {
520         List<String> segmentNames = new ArrayList<>(state.stateResponse.seg.length);
521         for (int count = 0; count < state.stateResponse.seg.length; count++) {
522             segmentNames.add("Segment " + count);
523         }
524         return segmentNames;
525     }
526
527     @Override
528     public void setSleepMode(String value) throws ApiException {
529         // Binding requires firmware 0.11.0 and newer
530     }
531
532     @Override
533     public void setSleepDuration(BigDecimal time) throws ApiException {
534         postState("{\"nl\":{\"dur\":" + time + "}}");
535     }
536
537     @Override
538     public void setSleepTargetBrightness(PercentType percent) throws ApiException {
539         postState("{\"nl\":{\"tbri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}}");
540     }
541 }