From 548530294b3a1325414b05afefa9d2b093a9a566 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Mon, 1 Nov 2021 00:07:03 +1100 Subject: [PATCH 01/15] Change to json for states Signed-off-by: Matthew Skinner --- bundles/org.openhab.binding.wled/README.md | 2 +- .../binding/wled/internal/WLedHandler.java | 154 ++--------- .../wled/internal/WLedHandlerFactory.java | 11 +- .../binding/wled/internal/WledState.java | 110 ++++++++ .../wled/internal/api/ApiException.java | 42 +++ .../binding/wled/internal/api/WledApi.java | 27 ++ .../wled/internal/api/WledApiFactory.java | 53 ++++ .../wled/internal/api/WledApiV084.java | 239 ++++++++++++++++++ .../resources/OH-INF/thing/thing-types.xml | 6 +- 9 files changed, 508 insertions(+), 136 deletions(-) create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/ApiException.java create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java diff --git a/bundles/org.openhab.binding.wled/README.md b/bundles/org.openhab.binding.wled/README.md index 0f8f159f7bb2..c0689ddfb901 100644 --- a/bundles/org.openhab.binding.wled/README.md +++ b/bundles/org.openhab.binding.wled/README.md @@ -24,7 +24,7 @@ For additional segments, you can add them manually and set the `segmentIndex` co |-|-|-|-| | `address`| The full URL to your WLED device. Example is `http://192.168.0.2:80` | Y | | | `pollTime`| How often in seconds you want the states of the LED fetched in case you make changes with a non openHAB app, web browser, or the light is auto changing FX or presets. | Y | 10 | -| `segmentIndex` | The index number to the LED segment you wish these channels to control. Leave on -1 if you do not know what a segment is. | Y | -1 | +| `segmentIndex` | The index number to the LED segment you wish these channels to control. Leave on 0 if you do not know what a segment is. | Y | 0 | | `saturationThreshold` | Allows you to use a colorpicker control linked to the `masterControls` channel to trigger only using the pure white LEDs instead of creating fake white light from the RGB channels. Try setting the value to 12 or leave this on 0 for RGB strings. | Y | 0 | ## Channels diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index 0f08a076c961..287244bf7123 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -16,29 +16,22 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.api.ContentResponse; -import org.eclipse.jetty.client.api.Request; -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.wled.internal.api.ApiException; +import org.openhab.binding.wled.internal.api.WledApi; +import org.openhab.binding.wled.internal.api.WledApiFactory; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; -import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -49,7 +42,6 @@ import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; -import org.openhab.core.types.StateOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,8 +55,9 @@ @NonNullByDefault public class WLedHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final HttpClient httpClient; - private final WledDynamicStateDescriptionProvider stateDescriptionProvider; + public final WledDynamicStateDescriptionProvider stateDescriptionProvider; + private WledApiFactory apiFactory; + private @Nullable WledApi wledApi; private @Nullable ScheduledFuture pollingFuture = null; private BigDecimal hue65535 = BigDecimal.ZERO; private BigDecimal saturation255 = BigDecimal.ZERO; @@ -74,45 +67,16 @@ public class WLedHandler extends BaseThingHandler { private HSBType secondaryColor = new HSBType(); private BigDecimal secondaryWhite = BigDecimal.ZERO; private boolean hasWhite = false; - private WLedConfiguration config = new WLedConfiguration(); + public WLedConfiguration config = new WLedConfiguration(); - public WLedHandler(Thing thing, HttpClient httpClient, + public WLedHandler(Thing thing, WledApiFactory apiFactory, WledDynamicStateDescriptionProvider stateDescriptionProvider) { super(thing); - this.httpClient = httpClient; + this.apiFactory = apiFactory; this.stateDescriptionProvider = stateDescriptionProvider; } private void sendGetRequest(String url) { - Request request; - if (url.contains("json") || config.segmentIndex == -1) { - request = httpClient.newRequest(config.address + url); - } else { - request = httpClient.newRequest(config.address + url + "&SM=" + config.segmentIndex); - } - request.timeout(3, TimeUnit.SECONDS); - request.method(HttpMethod.GET); - request.header(HttpHeader.ACCEPT_ENCODING, "gzip"); - logger.trace("Sending WLED GET:{}", url); - String errorReason = ""; - try { - ContentResponse contentResponse = request.send(); - if (contentResponse.getStatus() == 200) { - processState(contentResponse.getContentAsString()); - return; - } else { - errorReason = String.format("WLED request failed with %d: %s", contentResponse.getStatus(), - contentResponse.getReason()); - } - } catch (TimeoutException e) { - errorReason = "TimeoutException: WLED was not reachable on your network"; - } catch (ExecutionException e) { - errorReason = String.format("ExecutionException: %s", e.getMessage()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - errorReason = String.format("InterruptedException: %s", e.getMessage()); - } - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorReason); } private HSBType parseToHSBType(String message, String element) { @@ -164,84 +128,6 @@ private void parseColours(String message) { } } - /** - * - * This function should prevent the need to keep updating the binding as more FX and Palettes are added to the - * firmware. - */ - private void scrapeChannelOptions(String message) { - List fxOptions = new ArrayList<>(); - List palleteOptions = new ArrayList<>(); - int counter = 0; - for (String value : WLedHelper.getValue(message, "\"effects\":[", "]").replace("\"", "").split(",")) { - fxOptions.add(new StateOption(Integer.toString(counter++), value)); - } - stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_FX), fxOptions); - counter = 0; - for (String value : (WLedHelper.getValue(message, "\"palettes\":[", "]").replace("\"", "")).split(",")) { - palleteOptions.add(new StateOption(Integer.toString(counter++), value)); - } - stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_PALETTES), palleteOptions); - } - - private void processState(String message) { - logger.trace("WLED states are:{}", message); - if (thing.getStatus() != ThingStatus.ONLINE) { - updateStatus(ThingStatus.ONLINE); - sendGetRequest("/json"); // fetch FX and Pallete names - } - if (message.contains("\"effects\":[")) {// JSON API reply - scrapeChannelOptions(message); - return; - } - if (message.contains("0")) { - updateState(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); - } else { - masterBrightness255 = new BigDecimal(WLedHelper.getValue(message, "", "<")); - updateState(CHANNEL_MASTER_CONTROLS, - new PercentType(masterBrightness255.divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); - } - if (message.contains("0")) { - updateState(CHANNEL_INTENSITY, OnOffType.OFF); - } else { - BigDecimal bigTemp = new BigDecimal(WLedHelper.getValue(message, "", "<")).divide(BIG_DECIMAL_2_55, - RoundingMode.HALF_UP); - updateState(CHANNEL_INTENSITY, new PercentType(bigTemp)); - } - if (message.contains("1")) { - updateState(CHANNEL_PRESET_CYCLE, OnOffType.ON); - } else { - updateState(CHANNEL_PRESET_CYCLE, OnOffType.OFF); - } - if (message.contains("1")) { - updateState(CHANNEL_SLEEP, OnOffType.ON); - } else { - updateState(CHANNEL_SLEEP, OnOffType.OFF); - } - if (message.contains("1")) { - updateState(CHANNEL_SYNC_SEND, OnOffType.ON); - } else { - updateState(CHANNEL_SYNC_SEND, OnOffType.OFF); - } - if (message.contains("1")) { - updateState(CHANNEL_SYNC_RECEIVE, OnOffType.ON); - } else { - updateState(CHANNEL_SYNC_RECEIVE, OnOffType.OFF); - } - if (message.contains("")) { - updateState(CHANNEL_FX, new StringType(WLedHelper.getValue(message, "", "<"))); - } - if (message.contains("")) { - BigDecimal bigTemp = new BigDecimal(WLedHelper.getValue(message, "", "<")).divide(BIG_DECIMAL_2_55, - RoundingMode.HALF_UP); - updateState(CHANNEL_SPEED, new PercentType(bigTemp)); - } - if (message.contains("")) { - updateState(CHANNEL_PALETTES, new StringType(WLedHelper.getValue(message, "", "<"))); - } - parseColours(message); - } - private void sendWhite() { if (hasWhite) { sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=hFF000000" + "&A=" + masterBrightness255); @@ -431,8 +317,23 @@ public void savePreset(int presetIndex) { sendGetRequest("/win&PS=" + presetIndex); } - private void pollLED() { - sendGetRequest("/win"); + public void update(String channelID, State state) { + updateState(channelID, state); + } + + private void pollState() { + WledApi localApi = wledApi; + try { + if (localApi == null) { + wledApi = apiFactory.getApi(this, config); + } else { + localApi.update(); + updateStatus(ThingStatus.ONLINE); + } + } catch (ApiException e) { + wledApi = null;// recheck the firmware as it may have been updated + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } } @Override @@ -442,7 +343,7 @@ public void initialize() { logger.debug("Address was not entered in correct format, it may be the raw IP so adding http:// to start"); config.address = "http://" + config.address; } - pollingFuture = scheduler.scheduleWithFixedDelay(this::pollLED, 1, config.pollTime, TimeUnit.SECONDS); + pollingFuture = scheduler.scheduleWithFixedDelay(this::pollState, 1, config.pollTime, TimeUnit.SECONDS); } @Override @@ -450,6 +351,7 @@ public void dispose() { Future future = pollingFuture; if (future != null) { future.cancel(true); + pollingFuture = null; } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java index 52d60c68e677..a9619c334f1a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java @@ -16,8 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; -import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.binding.wled.internal.api.WledApiFactory; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; @@ -36,13 +35,13 @@ @NonNullByDefault @Component(configurationPid = "binding.wled", service = ThingHandlerFactory.class) public class WLedHandlerFactory extends BaseThingHandlerFactory { - private final HttpClient httpClient; private final WledDynamicStateDescriptionProvider stateDescriptionProvider; + private final WledApiFactory apiFactory; @Activate - public WLedHandlerFactory(@Reference HttpClientFactory httpClientFactory, + public WLedHandlerFactory(@Reference WledApiFactory apiFactory, final @Reference WledDynamicStateDescriptionProvider stateDescriptionProvider) { - this.httpClient = httpClientFactory.getCommonHttpClient(); + this.apiFactory = apiFactory; this.stateDescriptionProvider = stateDescriptionProvider; } @@ -58,7 +57,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) { - return new WLedHandler(thing, httpClient, stateDescriptionProvider); + return new WLedHandler(thing, apiFactory, stateDescriptionProvider); } return null; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java new file mode 100644 index 000000000000..c9732e30b43f --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * The {@link WledState} class holds the state and replies for a WLED device. + * + * @author Matthew Skinner - Initial contribution + */ +@NonNullByDefault +public class WledState { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + protected final Gson gson = new Gson(); + public JsonResponse jsonResponse = new JsonResponse(); + public StateResponse stateResponse = new StateResponse(); + public InfoResponse infoResponse = new InfoResponse(); + public NightLightState nightLightState = new NightLightState(); + public UdpnState udpnState = new UdpnState(); + public SegmentState segmentState = new SegmentState(); + + public class JsonResponse { + public List effects = new ArrayList<>(); + public List palettes = new ArrayList<>(); + } + + public void unpackJsonObjects() { + @Nullable + NightLightState localNightLightState = gson.fromJson(stateResponse.nl.toString(), NightLightState.class); + if (localNightLightState != null) { + nightLightState = localNightLightState; + } + + @Nullable + UdpnState localUdpnState = gson.fromJson(stateResponse.udpn.toString(), UdpnState.class); + if (localUdpnState != null) { + udpnState = localUdpnState; + } + } + + public class StateResponse { + public boolean on = true; + public Object nl = "{}"; + public Object udpn = "{}"; + public SegmentState[] seg = new SegmentState[1]; + public int bri = 0; + public int transition = 7; + public int ps = -1; + public int pss = 0; + public int pl = -1; + public int lor = 0; + } + + public class UdpnState { + public boolean send = false; + public boolean recv = false; + } + + public class SegmentState { + public int id = 0; + public int start = 0; + public int stop = 0; + public int len = 0; + public int grp = 0; + public int spc = 0; + public boolean on = true; + public int bri = 0; + public Object[] col = new Object[1]; + public int fx = 0; + public int sx = 0; + public int ix = 0; + public int pal = 0; + public boolean sel = true; + public boolean rev = false; + public boolean mi = false; + } + + public class NightLightState { + public boolean on = true; + public boolean fade = true; + public int dur = 0; + public int mode = 0; + public int tbri = 0; + public int rem = 0; + } + + public class InfoResponse { + public String ver = "00000"; + public String mac = ""; + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/ApiException.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/ApiException.java new file mode 100644 index 000000000000..602afb8596d8 --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/ApiException.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ApiException} will be thrown whenever the wled API can not successfully communicate with the device. + * + * @author Matthew Skinner - Initial contribution + */ + +@NonNullByDefault +public class ApiException extends Exception { + /** + * Serial ID of this error class. + */ + private static final long serialVersionUID = 1238256795216449L; + + /** + * Basic constructor allowing the storing of a single message. + * + * @param message Descriptive message about the error. + */ + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable e) { + super(message, e); + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java new file mode 100644 index 000000000000..4b5443f1f8c5 --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link WledApi} is the json Api methods for different firmware versions + * + * @author Matthew Skinner - Initial contribution + */ +@NonNullByDefault +public interface WledApi { + public abstract void update() throws ApiException; + + public abstract int getFirmwareVersion() throws ApiException; +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java new file mode 100644 index 000000000000..8198de9fe935 --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.wled.internal.WLedConfiguration; +import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link WledApiFactory} is responsible for creating an instance of the API that will work with different + * firmware versions. + * + * @author Matthew Skinner - Initial contribution + */ +@Component(service = WledApiFactory.class) +@NonNullByDefault +public class WledApiFactory { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final HttpClient httpClient; + + @Activate + public WledApiFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + } + + public WledApi getApi(WLedHandler wLedHandler, WLedConfiguration config) throws ApiException { + // json api was first introduced in ver 0.8.4 + WledApi lowestSupportedApi = new WledApiV084(wLedHandler, config, httpClient); + int version = lowestSupportedApi.getFirmwareVersion(); + logger.debug("Firmware is {}", version); + if (version < 135) { + return new WledApiV084(wLedHandler, config, httpClient); + } + return lowestSupportedApi; + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java new file mode 100644 index 000000000000..67284e42ddd9 --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import static org.openhab.binding.wled.internal.WLedBindingConstants.*; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.wled.internal.WLedConfiguration; +import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.binding.wled.internal.WledState; +import org.openhab.binding.wled.internal.WledState.InfoResponse; +import org.openhab.binding.wled.internal.WledState.JsonResponse; +import org.openhab.binding.wled.internal.WledState.StateResponse; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateOption; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link WledApiV084} is the json Api methods for firmware version 0.8.4 and newer + * + * @author Matthew Skinner - Initial contribution + */ +@NonNullByDefault +public class WledApiV084 implements WledApi { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + protected final Gson gson = new Gson(); + protected final HttpClient httpClient; + protected final WLedConfiguration config; + protected final WLedHandler handler; + protected WledState state = new WledState(); + + public WledApiV084(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { + this.handler = handler; + this.config = config; + this.httpClient = httpClient; + } + + protected String sendGetRequest(String url) throws ApiException { + Request request = httpClient.newRequest(config.address + url); + request.timeout(3, TimeUnit.SECONDS); + request.method(HttpMethod.GET); + request.header(HttpHeader.ACCEPT_ENCODING, "gzip"); + logger.trace("Sending WLED GET:{}", url); + String errorReason = ""; + try { + ContentResponse contentResponse = request.send(); + if (contentResponse.getStatus() == 200) { + return contentResponse.getContentAsString(); + } else { + errorReason = String.format("WLED request failed with %d: %s", contentResponse.getStatus(), + contentResponse.getReason()); + } + } catch (TimeoutException e) { + errorReason = "TimeoutException: WLED was not reachable on your network"; + } catch (ExecutionException e) { + errorReason = String.format("ExecutionException: %s", e.getMessage()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + errorReason = String.format("InterruptedException: %s", e.getMessage()); + } + throw new ApiException(errorReason); + } + + protected void sendPostRequest(String url, String json) throws ApiException { + logger.trace("Sending WLED POST:{}", url); + Request request = httpClient.POST(config.address + url); + request.timeout(3, TimeUnit.SECONDS); + request.header(HttpHeader.ACCEPT, "application/json"); + request.header(HttpHeader.CONTENT_TYPE, "application/json"); + request.content(new StringContentProvider(json), "application/json"); + String errorReason = ""; + try { + request.send(); + return; + } catch (InterruptedException e) { + errorReason = String.format("InterruptedException: %s", e.getMessage()); + } catch (TimeoutException e) { + errorReason = "TimeoutException: WLED was not reachable on your network"; + } catch (ExecutionException e) { + errorReason = String.format("ExecutionException: %s", e.getMessage()); + } + throw new ApiException(errorReason); + } + + protected StateResponse getState() throws ApiException { + try { + String returnContent = sendGetRequest("/json/state"); + StateResponse response = gson.fromJson(returnContent, StateResponse.class); + if (response == null) { + throw new ApiException("Could not GET:/json/state"); + } + return response; + } catch (JsonSyntaxException e) { + throw new ApiException("JsonSyntaxException:{}", e); + } + } + + protected InfoResponse getInfo() throws ApiException { + try { + String returnContent = sendGetRequest("/json/info"); + InfoResponse response = gson.fromJson(returnContent, InfoResponse.class); + if (response == null) { + throw new ApiException("Could not GET:/json/info"); + } + return response; + } catch (JsonSyntaxException e) { + throw new ApiException("JsonSyntaxException:{}", e); + } + } + + protected JsonResponse getJson() throws ApiException { + try { + String returnContent = sendGetRequest("/json"); + JsonResponse response = gson.fromJson(returnContent, JsonResponse.class); + if (response == null) { + throw new ApiException("Could not GET:/json"); + } + return response; + } catch (JsonSyntaxException e) { + throw new ApiException("JsonSyntaxException:{}", e); + } + } + + @Override + public void update() throws ApiException { + state.stateResponse = getState(); + state.unpackJsonObjects(); + processState(); + } + + protected void getUpdatedFxList() { + List fxOptions = new ArrayList<>(); + int counter = 0; + for (String value : state.jsonResponse.effects) { + fxOptions.add(new StateOption(Integer.toString(counter++), value)); + } + handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_FX), + fxOptions); + } + + protected void getUpdatedPaletteList() { + List palleteOptions = new ArrayList<>(); + int counter = 0; + for (String value : state.jsonResponse.palettes) { + palleteOptions.add(new StateOption(Integer.toString(counter++), value)); + } + handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PALETTES), + palleteOptions); + } + + @Override + public int getFirmwareVersion() throws ApiException { + state.jsonResponse = getJson(); + state.infoResponse = getInfo(); + getUpdatedFxList(); + getUpdatedPaletteList(); + String temp = state.infoResponse.ver; + temp = temp.replaceAll("\\.", ""); + if (temp.length() > 4) { + temp = temp.substring(0, 4); + } + int version = Integer.parseInt(temp); + return version; + } + + protected void processState() { + if (!state.stateResponse.on) { + handler.update(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); + } else { + handler.update(CHANNEL_MASTER_CONTROLS, new PercentType( + new BigDecimal(state.stateResponse.bri).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + } + if (state.nightLightState.on) { + handler.update(CHANNEL_SLEEP, OnOffType.ON); + } else { + handler.update(CHANNEL_SLEEP, OnOffType.OFF); + } + if (state.stateResponse.pl == 0) { + handler.update(CHANNEL_PRESET_CYCLE, OnOffType.ON); + } else { + handler.update(CHANNEL_PRESET_CYCLE, OnOffType.OFF); + } + if (state.udpnState.recv) { + handler.update(CHANNEL_SYNC_RECEIVE, OnOffType.ON); + } else { + handler.update(CHANNEL_SYNC_RECEIVE, OnOffType.OFF); + } + if (state.udpnState.send) { + handler.update(CHANNEL_SYNC_SEND, OnOffType.ON); + } else { + handler.update(CHANNEL_SYNC_SEND, OnOffType.OFF); + } + handler.update(CHANNEL_TRANS_TIME, new PercentType( + new BigDecimal(state.stateResponse.transition).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + handler.update(CHANNEL_PRESETS, new StringType("" + state.stateResponse.ps)); + handler.update(CHANNEL_FX, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].fx)); + handler.update(CHANNEL_PALETTES, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].pal)); + handler.update(CHANNEL_SPEED, + new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].sx) + .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + handler.update(CHANNEL_INTENSITY, + new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].ix) + .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + // logger.debug("col 1:{}", state.stateResponse.seg[handler.config.segmentIndex].col[0]); + // logger.debug("col 2:{}", state.stateResponse.seg[handler.config.segmentIndex].col[1]); + // logger.debug("col 3:{}", state.stateResponse.seg[handler.config.segmentIndex].col[2]); + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index 4ae88dbcf4f1..629e11199a2d 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -36,11 +36,11 @@ Time in seconds of how often to fetch the state of the LEDs. 10 - + - Leave this as -1 if you are not using segments, otherwise set this to the segment index number that you + Leave this as 0 if you are not using segments, otherwise set this to the segment index number that you wish to control. - -1 + 0 From a3c8fcc72cbce9ead7bbf16bead4fbe219ef56ec Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Mon, 1 Nov 2021 15:19:07 +1100 Subject: [PATCH 02/15] Add 3rd colours. Signed-off-by: Matthew Skinner --- .../wled/internal/WLedBindingConstants.java | 2 + .../wled/internal/WLedDiscoveryService.java | 2 +- .../binding/wled/internal/WLedHandler.java | 300 ++++++------------ .../binding/wled/internal/WLedHelper.java | 49 +-- .../binding/wled/internal/api/WledApi.java | 28 ++ .../wled/internal/api/WledApiFactory.java | 7 +- .../wled/internal/api/WledApiV084.java | 135 +++++++- .../resources/OH-INF/thing/thing-types.xml | 16 + 8 files changed, 308 insertions(+), 231 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java index 72106cea5616..1085ca87a349 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java @@ -45,8 +45,10 @@ public class WLedBindingConstants { public static final String CHANNEL_MASTER_CONTROLS = "masterControls"; public static final String CHANNEL_PRIMARY_COLOR = "primaryColor"; public static final String CHANNEL_SECONDARY_COLOR = "secondaryColor"; + public static final String CHANNEL_THIRD_COLOR = "tertiaryColor"; public static final String CHANNEL_PRIMARY_WHITE = "primaryWhite"; public static final String CHANNEL_SECONDARY_WHITE = "secondaryWhite"; + public static final String CHANNEL_THIRD_WHITE = "tertiaryWhite"; public static final String CHANNEL_PALETTES = "palettes"; public static final String CHANNEL_PRESETS = "presets"; public static final String CHANNEL_PRESET_DURATION = "presetDuration"; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedDiscoveryService.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedDiscoveryService.java index f9a3185eb20b..34114b1f32e3 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedDiscoveryService.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedDiscoveryService.java @@ -106,7 +106,7 @@ private String sendGetRequest(String address, String url) { properties.put(Thing.PROPERTY_MAC_ADDRESS, macAddress); properties.put(Thing.PROPERTY_FIRMWARE_VERSION, firmware); return DiscoveryResultBuilder.create(thingUID).withProperty(CONFIG_ADDRESS, address[0]) - .withProperty(CONFIG_SEGMENT_INDEX, -1).withLabel(label).withProperties(properties) + .withProperty(CONFIG_SEGMENT_INDEX, 0).withLabel(label).withProperties(properties) .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build(); } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index 287244bf7123..f3d9106d7efd 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -15,7 +15,6 @@ import static org.openhab.binding.wled.internal.WLedBindingConstants.*; import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Future; @@ -57,15 +56,13 @@ public class WLedHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); public final WledDynamicStateDescriptionProvider stateDescriptionProvider; private WledApiFactory apiFactory; - private @Nullable WledApi wledApi; + private @Nullable WledApi api; private @Nullable ScheduledFuture pollingFuture = null; private BigDecimal hue65535 = BigDecimal.ZERO; private BigDecimal saturation255 = BigDecimal.ZERO; private BigDecimal masterBrightness255 = BigDecimal.ZERO; private HSBType primaryColor = new HSBType(); - private BigDecimal primaryWhite = BigDecimal.ZERO; private HSBType secondaryColor = new HSBType(); - private BigDecimal secondaryWhite = BigDecimal.ZERO; private boolean hasWhite = false; public WLedConfiguration config = new WLedConfiguration(); @@ -79,55 +76,6 @@ public WLedHandler(Thing thing, WledApiFactory apiFactory, private void sendGetRequest(String url) { } - private HSBType parseToHSBType(String message, String element) { - int startIndex = message.indexOf(element); - if (startIndex == -1) { - return new HSBType(); - } - int endIndex = message.indexOf("<", startIndex + element.length()); - int r = 0, g = 0, b = 0; - try { - r = Integer.parseInt(message.substring(startIndex + element.length(), endIndex)); - // look for second element - startIndex = message.indexOf(element, endIndex); - if (startIndex == -1) { - return new HSBType(); - } - endIndex = message.indexOf("<", startIndex + element.length()); - g = Integer.parseInt(message.substring(startIndex + element.length(), endIndex)); - // look for third element called - startIndex = message.indexOf(element, endIndex); - if (startIndex == -1) { - return new HSBType(); - } - endIndex = message.indexOf("<", startIndex + element.length()); - b = Integer.parseInt(message.substring(startIndex + element.length(), endIndex)); - } catch (NumberFormatException e) { - logger.warn("NumberFormatException when parsing the WLED color fields:{}", e.getMessage()); - } - return HSBType.fromRGB(r, g, b); - } - - private void parseColours(String message) { - primaryColor = parseToHSBType(message, ""); - updateState(CHANNEL_PRIMARY_COLOR, primaryColor); - secondaryColor = parseToHSBType(message, ""); - updateState(CHANNEL_SECONDARY_COLOR, secondaryColor); - try { - primaryWhite = new BigDecimal(WLedHelper.getValue(message, "", "<")); - if (primaryWhite.intValue() > -1) { - hasWhite = true; - updateState(CHANNEL_PRIMARY_WHITE, - new PercentType(primaryWhite.divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); - secondaryWhite = new BigDecimal(WLedHelper.getValue(message, "", "<")); - updateState(CHANNEL_SECONDARY_WHITE, - new PercentType(secondaryWhite.divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); - } - } catch (IllegalArgumentException e) { - logger.warn("IllegalArgumentException when parsing the WLED colour and white fields:{}", e.getMessage()); - } - } - private void sendWhite() { if (hasWhite) { sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=hFF000000" + "&A=" + masterBrightness255); @@ -157,155 +105,111 @@ public void handleCommand(ChannelUID channelUID, Command command) { return;// no need to check for refresh below } logger.debug("command {} sent to {}", command, channelUID.getId()); - switch (channelUID.getId()) { - case CHANNEL_SYNC_SEND: - if (OnOffType.OFF.equals(command)) { - sendGetRequest("/win&NS=0"); - } else { - sendGetRequest("/win&NS=1"); - } - break; - case CHANNEL_SYNC_RECEIVE: - if (OnOffType.OFF.equals(command)) { - sendGetRequest("/win&NR=0"); - } else { - sendGetRequest("/win&NR=1"); - } - break; - case CHANNEL_PRIMARY_WHITE: - if (command instanceof PercentType) { - sendGetRequest("/win&W=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); - } - break; - case CHANNEL_SECONDARY_WHITE: - if (command instanceof PercentType) { - sendGetRequest("/win&W2=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); - } - break; - case CHANNEL_MASTER_CONTROLS: - if (command instanceof OnOffType) { - if (OnOffType.OFF.equals(command)) { - sendGetRequest("/win&TT=250&T=0"); - } else { - sendGetRequest("/win&TT=1000&T=1"); + try { + switch (channelUID.getId()) { + case CHANNEL_SYNC_SEND: + api.setUdpSend(OnOffType.ON.equals(command)); + break; + case CHANNEL_SYNC_RECEIVE: + api.setUdpRecieve(OnOffType.ON.equals(command)); + break; + case CHANNEL_PRIMARY_WHITE: + if (command instanceof PercentType) { + sendGetRequest("/win&W=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); } - } else if (command instanceof IncreaseDecreaseType) { - if (IncreaseDecreaseType.INCREASE.equals(command)) { - if (masterBrightness255.intValue() < 240) { - sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 different levels - } else { - sendGetRequest("/win&TT=1000&A=255"); - } - } else { - if (masterBrightness255.intValue() > 15) { - sendGetRequest("/win&TT=1000&A=~-15"); + break; + case CHANNEL_SECONDARY_WHITE: + if (command instanceof PercentType) { + sendGetRequest("/win&W2=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); + } + break; + case CHANNEL_MASTER_CONTROLS: + if (command instanceof OnOffType) { + api.setMasterOn(OnOffType.ON.equals(command)); + } else if (command instanceof IncreaseDecreaseType) { + if (IncreaseDecreaseType.INCREASE.equals(command)) { + if (masterBrightness255.intValue() < 240) { + sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 different levels + } else { + sendGetRequest("/win&TT=1000&A=255"); + } } else { - sendGetRequest("/win&TT=1000&A=0"); + if (masterBrightness255.intValue() > 15) { + sendGetRequest("/win&TT=1000&A=~-15"); + } else { + sendGetRequest("/win&TT=1000&A=0"); + } } + } else if (command instanceof HSBType) { + api.setMasterHSB((HSBType) command); + } else if (command instanceof PercentType) { + api.setMasterBrightness((PercentType) command); } - } else if (command instanceof HSBType) { - if ((((HSBType) command).getBrightness()) == PercentType.ZERO) { - sendGetRequest("/win&TT=500&T=0"); + return; + case CHANNEL_PRIMARY_COLOR: + if (command instanceof HSBType) { + primaryColor = (HSBType) command; + sendGetRequest("/win&CL=" + createColorHex(primaryColor)); + } else if (command instanceof PercentType) { + primaryColor = new HSBType(primaryColor.getHue(), primaryColor.getSaturation(), + ((PercentType) command)); + sendGetRequest("/win&CL=" + createColorHex(primaryColor)); } - primaryColor = (HSBType) command; - hue65535 = primaryColor.getHue().toBigDecimal().multiply(BIG_DECIMAL_182_04); - saturation255 = primaryColor.getSaturation().toBigDecimal().multiply(BIG_DECIMAL_2_55); - masterBrightness255 = primaryColor.getBrightness().toBigDecimal().multiply(BIG_DECIMAL_2_55); - if (primaryColor.getSaturation().intValue() < config.saturationThreshold) { - sendWhite(); - } else if (primaryColor.getSaturation().intValue() == 32 && primaryColor.getHue().intValue() == 36 - && hasWhite) { - // Google sends this when it wants white - sendWhite(); - } else { - if (config.segmentIndex == -1) { - sendGetRequest("/win&TT=1000&FX=0&CY=0&HU=" + hue65535 + "&SA=" + saturation255 + "&A=" - + masterBrightness255); - } else { - sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=" + createColorHex(primaryColor) + "&A=" - + masterBrightness255); + return; + case CHANNEL_SECONDARY_COLOR: + if (command instanceof HSBType) { + secondaryColor = (HSBType) command; + sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); + } else if (command instanceof PercentType) { + secondaryColor = new HSBType(secondaryColor.getHue(), secondaryColor.getSaturation(), + ((PercentType) command)); + sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); + } + return; + case CHANNEL_PALETTES: + api.setPalette(command.toString()); + break; + case CHANNEL_FX: + api.setPreset(command.toString()); + break; + case CHANNEL_SPEED: + api.setFxSpeed((PercentType) command); + break; + case CHANNEL_INTENSITY: + api.setFxIntencity((PercentType) command); + break; + case CHANNEL_SLEEP: + api.setSleep(OnOffType.ON.equals(command)); + break; + case CHANNEL_PRESETS: + sendGetRequest("/win&PL=" + command); + break; + case CHANNEL_PRESET_DURATION: + if (command instanceof QuantityType) { + QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); + if (seconds != null) { + bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)); + sendGetRequest("/win&PT=" + bigTemp.intValue()); } } - } else if (command instanceof PercentType) { - masterBrightness255 = ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55); - sendGetRequest("/win&TT=1000&A=" + masterBrightness255); - } - return; - case CHANNEL_PRIMARY_COLOR: - if (command instanceof HSBType) { - primaryColor = (HSBType) command; - sendGetRequest("/win&CL=" + createColorHex(primaryColor)); - } else if (command instanceof PercentType) { - primaryColor = new HSBType(primaryColor.getHue(), primaryColor.getSaturation(), - ((PercentType) command)); - sendGetRequest("/win&CL=" + createColorHex(primaryColor)); - } - return; - case CHANNEL_SECONDARY_COLOR: - if (command instanceof HSBType) { - secondaryColor = (HSBType) command; - sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); - } else if (command instanceof PercentType) { - secondaryColor = new HSBType(secondaryColor.getHue(), secondaryColor.getSaturation(), - ((PercentType) command)); - sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); - } - return; - case CHANNEL_PALETTES: - sendGetRequest("/win&FP=" + command); - break; - case CHANNEL_FX: - sendGetRequest("/win&FX=" + command); - break; - case CHANNEL_SPEED: - localPercentType = ((State) command).as(PercentType.class); - if (localPercentType != null) { - bigTemp = localPercentType.toBigDecimal().multiply(BIG_DECIMAL_2_55); - sendGetRequest("/win&SX=" + bigTemp); - } - break; - case CHANNEL_INTENSITY: - localPercentType = ((State) command).as(PercentType.class); - if (localPercentType != null) { - bigTemp = localPercentType.toBigDecimal().multiply(BIG_DECIMAL_2_55); - sendGetRequest("/win&IX=" + bigTemp); - } - break; - case CHANNEL_SLEEP: - if (OnOffType.ON.equals(command)) { - sendGetRequest("/win&ND"); - } else { - sendGetRequest("/win&NL=0"); - } - break; - case CHANNEL_PRESETS: - sendGetRequest("/win&PL=" + command); - break; - case CHANNEL_PRESET_DURATION: - if (command instanceof QuantityType) { - QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); - if (seconds != null) { - bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)); - sendGetRequest("/win&PT=" + bigTemp.intValue()); + break; + case CHANNEL_TRANS_TIME: + if (command instanceof QuantityType) { + QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); + if (seconds != null) { + api.setTransitionTime( + new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)).intValue()); + } } - } - break; - case CHANNEL_TRANS_TIME: - if (command instanceof QuantityType) { - QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); - if (seconds != null) { - bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)); - sendGetRequest("/win&TT=" + bigTemp.intValue()); + break; + case CHANNEL_PRESET_CYCLE: + if (command instanceof OnOffType) { + api.setPresetCycle(OnOffType.ON.equals(command)); } - } - break; - case CHANNEL_PRESET_CYCLE: - if (OnOffType.ON.equals(command)) { - sendGetRequest("/win&CY=1"); - } else { - sendGetRequest("/win&CY=0"); - } - break; + break; + } + } catch (ApiException e) { + logger.debug("Exception occured:{}", e.getMessage()); } } @@ -322,16 +226,17 @@ public void update(String channelID, State state) { } private void pollState() { - WledApi localApi = wledApi; + WledApi localApi = api; try { if (localApi == null) { - wledApi = apiFactory.getApi(this, config); + api = apiFactory.getApi(this, config); + api.initialize(); } else { localApi.update(); updateStatus(ThingStatus.ONLINE); } } catch (ApiException e) { - wledApi = null;// recheck the firmware as it may have been updated + api = null;// recheck the firmware as it may have been updated updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } @@ -339,6 +244,9 @@ private void pollState() { @Override public void initialize() { config = getConfigAs(WLedConfiguration.class); + if (config.segmentIndex == -1) { + config.segmentIndex = 0; + } if (!config.address.contains("://")) { logger.debug("Address was not entered in correct format, it may be the raw IP so adding http:// to start"); config.address = "http://" + config.address; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHelper.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHelper.java index a50a08e32fac..cf36cb6a757e 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHelper.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHelper.java @@ -12,10 +12,13 @@ */ package org.openhab.binding.wled.internal; -import java.util.LinkedList; +import java.math.BigDecimal; +import java.util.Arrays; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.PercentType; /** * The {@link WLedHelper} Provides helper classes that are used from multiple classes in the binding. @@ -24,6 +27,28 @@ */ @NonNullByDefault public class WLedHelper { + public static HSBType parseToHSBType(String message) { + // example message rgb in array brackets [255.0, 255.0, 255.0] + List colors = Arrays.asList(message.replaceAll("\\[|\\]", "").split("\\s*,\\s*")); + try { + int r = new BigDecimal(colors.get(0)).intValue(); + int g = new BigDecimal(colors.get(1)).intValue(); + int b = new BigDecimal(colors.get(2)).intValue(); + return HSBType.fromRGB(r, g, b); + } catch (NumberFormatException e) { + return new HSBType(); + } + } + + public static PercentType parseWhitePercent(String message) { + // example message rgb in array brackets [255.0, 255.0, 255.0, 255.0] + List colors = Arrays.asList(message.replaceAll("\\[|\\]", "").split("\\s*,\\s*")); + try { + return new PercentType(new BigDecimal(colors.get(2))); + } catch (IllegalArgumentException e) { + return new PercentType(); + } + } /** * @return A string that starts after finding the element and terminates when it finds the first occurrence of the @@ -40,26 +65,4 @@ static String getValue(String message, String element, String end) { } return ""; } - - /** - * @return A List that holds the values from a heading/element that re-occurs in a message multiple times. - * - */ - static List listOfResults(String message, String element, String end) { - List results = new LinkedList<>(); - String temp = ""; - for (int startLookingFromIndex = 0; startLookingFromIndex != -1;) { - startLookingFromIndex = message.indexOf(element, startLookingFromIndex); - if (startLookingFromIndex >= 0) { - temp = getValue(message.substring(startLookingFromIndex), element, end); - if (!temp.isEmpty()) { - results.add(temp); - } else { - return results;// end string must not exist so stop looking. - } - startLookingFromIndex += temp.length(); - } - } - return results; - } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index 4b5443f1f8c5..02d98826fdde 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -13,6 +13,8 @@ package org.openhab.binding.wled.internal.api; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.PercentType; /** * The {@link WledApi} is the json Api methods for different firmware versions @@ -23,5 +25,31 @@ public interface WledApi { public abstract void update() throws ApiException; + public abstract void initialize() throws ApiException; + public abstract int getFirmwareVersion() throws ApiException; + + public abstract void setMasterOn(boolean bool) throws ApiException; + + public abstract void setMasterBrightness(PercentType percent) throws ApiException; + + public abstract void setMasterHSB(HSBType hsbType) throws ApiException; + + public abstract void setPreset(String string) throws ApiException; + + public abstract void setPalette(String string) throws ApiException; + + public abstract void setFxIntencity(PercentType percentType) throws ApiException; + + public abstract void setFxSpeed(PercentType percentType) throws ApiException; + + public abstract void setSleep(boolean b) throws ApiException; + + public abstract void setUdpSend(boolean bool) throws ApiException; + + public abstract void setUdpRecieve(boolean bool) throws ApiException; + + public abstract void setTransitionTime(int milliseconds) throws ApiException; + + public abstract void setPresetCycle(boolean bool) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java index 8198de9fe935..13fe0dc79303 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java @@ -41,13 +41,14 @@ public WledApiFactory(@Reference HttpClientFactory httpClientFactory) { } public WledApi getApi(WLedHandler wLedHandler, WLedConfiguration config) throws ApiException { - // json api was first introduced in ver 0.8.4 WledApi lowestSupportedApi = new WledApiV084(wLedHandler, config, httpClient); int version = lowestSupportedApi.getFirmwareVersion(); - logger.debug("Firmware is {}", version); - if (version < 135) { + logger.debug("Treating firmware as int:{}", version); + // json api was in ver 0.8.4 but may lack testing until 0.10.0 aka 100 + if (version > 100) { return new WledApiV084(wLedHandler, config, httpClient); } + logger.warn("Your WLED firmware is very old, upgrade to at least 0.10.0"); return lowestSupportedApi; } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index 67284e42ddd9..ca5a900de278 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -31,10 +31,12 @@ import org.eclipse.jetty.http.HttpMethod; import org.openhab.binding.wled.internal.WLedConfiguration; import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.binding.wled.internal.WLedHelper; import org.openhab.binding.wled.internal.WledState; import org.openhab.binding.wled.internal.WledState.InfoResponse; import org.openhab.binding.wled.internal.WledState.JsonResponse; import org.openhab.binding.wled.internal.WledState.StateResponse; +import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StringType; @@ -59,6 +61,7 @@ public class WledApiV084 implements WledApi { protected final WLedConfiguration config; protected final WLedHandler handler; protected WledState state = new WledState(); + private int version = 0; public WledApiV084(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { this.handler = handler; @@ -92,11 +95,15 @@ protected String sendGetRequest(String url) throws ApiException { throw new ApiException(errorReason); } + protected void postState(String json) throws ApiException { + sendPostRequest("/json/state", json); + } + protected void sendPostRequest(String url, String json) throws ApiException { - logger.trace("Sending WLED POST:{}", url); + logger.debug("Sending WLED POST:{} Message:{}", url, json); Request request = httpClient.POST(config.address + url); request.timeout(3, TimeUnit.SECONDS); - request.header(HttpHeader.ACCEPT, "application/json"); + // request.header(HttpHeader.ACCEPT, "application/json"); request.header(HttpHeader.CONTENT_TYPE, "application/json"); request.content(new StringContentProvider(json), "application/json"); String errorReason = ""; @@ -180,17 +187,22 @@ protected void getUpdatedPaletteList() { } @Override - public int getFirmwareVersion() throws ApiException { + public void initialize() throws ApiException { state.jsonResponse = getJson(); - state.infoResponse = getInfo(); getUpdatedFxList(); getUpdatedPaletteList(); + } + + @Override + public int getFirmwareVersion() throws ApiException { + state.infoResponse = getInfo(); String temp = state.infoResponse.ver; + logger.debug("Firmware for WLED is ver:{}", temp); temp = temp.replaceAll("\\.", ""); if (temp.length() > 4) { temp = temp.substring(0, 4); } - int version = Integer.parseInt(temp); + version = Integer.parseInt(temp); return version; } @@ -232,8 +244,115 @@ protected void processState() { handler.update(CHANNEL_INTENSITY, new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].ix) .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); - // logger.debug("col 1:{}", state.stateResponse.seg[handler.config.segmentIndex].col[0]); - // logger.debug("col 2:{}", state.stateResponse.seg[handler.config.segmentIndex].col[1]); - // logger.debug("col 3:{}", state.stateResponse.seg[handler.config.segmentIndex].col[2]); + + handler.update(CHANNEL_PRIMARY_COLOR, + WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); + handler.update(CHANNEL_SECONDARY_COLOR, + WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); + handler.update(CHANNEL_THIRD_COLOR, + WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + // white LEDs for RGBW strings + handler.update(CHANNEL_PRIMARY_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); + handler.update(CHANNEL_SECONDARY_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); + handler.update(CHANNEL_THIRD_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + } + + @Override + public void setMasterOn(boolean bool) throws ApiException { + postState("{\"on\":" + bool + "}"); + } + + @Override + public void setMasterBrightness(PercentType percent) throws ApiException { + if (percent.equals(PercentType.ZERO)) { + postState("{\"on\":false}"); + return; + } + postState("{\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55) + "}"); + } + + @Override + public void setMasterHSB(HSBType hsbType) throws ApiException { + /* + * primaryColor = (HSBType) command; + * hue65535 = primaryColor.getHue().toBigDecimal().multiply(BIG_DECIMAL_182_04); + * saturation255 = primaryColor.getSaturation().toBigDecimal().multiply(BIG_DECIMAL_2_55); + * masterBrightness255 = primaryColor.getBrightness().toBigDecimal().multiply(BIG_DECIMAL_2_55); + * if (primaryColor.getSaturation().intValue() < config.saturationThreshold) { + * sendWhite(); + * } else if (primaryColor.getSaturation().intValue() == 32 && primaryColor.getHue().intValue() == 36 + * && hasWhite) { + * // Google sends this when it wants white + * sendWhite(); + * } else { + * if (config.segmentIndex == -1) { + * sendGetRequest( + * "/win&TT=1000&FX=0&CY=0&HU=" + hue65535 + "&SA=" + saturation255 + "&A=" + masterBrightness255); + * } else { + * sendGetRequest( + * "/win&TT=1000&FX=0&CY=0&CL=" + createColorHex(primaryColor) + "&A=" + masterBrightness255); + * } + * } + */ + if (hsbType.getBrightness().equals(PercentType.ZERO)) { + postState("{\"on\":false}"); + return; + } + postState("{\"on\":true,\"seg\":[{\"col\":[[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); + } + + @Override + public void setPreset(String string) throws ApiException { + postState("{\"seg\":[{\"fx\":" + string + "}]}"); + } + + @Override + public void setPalette(String string) throws ApiException { + postState("{\"seg\":[{\"pal\":" + string + "}]}"); + } + + @Override + public void setFxIntencity(PercentType percentType) throws ApiException { + postState("{\"seg\":[{\"ix\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); + } + + @Override + public void setFxSpeed(PercentType percentType) throws ApiException { + postState("{\"seg\":[{\"sx\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); + } + + @Override + public void setSleep(boolean b) throws ApiException { + postState("{\"nl\":{\"on\":" + b + "}}"); + } + + @Override + public void setUdpSend(boolean bool) throws ApiException { + postState("{\"udpn\":{\"send\":" + bool + "}}"); + } + + @Override + public void setUdpRecieve(boolean bool) throws ApiException { + postState("{\"udpn\":{\"recv\":" + bool + "}}"); + } + + @Override + public void setTransitionTime(int milliseconds) throws ApiException { + postState("{\"transition\":" + milliseconds + "}"); + } + + @Override + public void setPresetCycle(boolean bool) throws ApiException { + if (bool) { + postState("{\"pl\":0}"); + } else { + postState("{\"pl\":-1}"); + } } } diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index 629e11199a2d..bb0e2e9a858a 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -14,6 +14,8 @@ + + @@ -90,6 +92,20 @@ DimmableLight + + Color + + Allows you to change the third color used in FX + ColorLight + + + + Dimmer + + Changes the brightness of the third white LED + DimmableLight + + String From f5a52191fb9e8a6ce01a77fc3f6aa7d54cc320b8 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Tue, 2 Nov 2021 00:34:13 +1100 Subject: [PATCH 03/15] Segments now mostly work Signed-off-by: Matthew Skinner --- .../binding/wled/internal/WLedHandler.java | 112 +++++++------ .../binding/wled/internal/api/WledApi.java | 14 ++ .../wled/internal/api/WledApiV084.java | 157 +++++++++++------- 3 files changed, 173 insertions(+), 110 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index f3d9106d7efd..efaacf7d7d0a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -58,12 +58,11 @@ public class WLedHandler extends BaseThingHandler { private WledApiFactory apiFactory; private @Nullable WledApi api; private @Nullable ScheduledFuture pollingFuture = null; - private BigDecimal hue65535 = BigDecimal.ZERO; - private BigDecimal saturation255 = BigDecimal.ZERO; private BigDecimal masterBrightness255 = BigDecimal.ZERO; + public boolean hasWhite = false; private HSBType primaryColor = new HSBType(); private HSBType secondaryColor = new HSBType(); - private boolean hasWhite = false; + private HSBType thirdColor = new HSBType(); public WLedConfiguration config = new WLedConfiguration(); public WLedHandler(Thing thing, WledApiFactory apiFactory, @@ -73,123 +72,128 @@ public WLedHandler(Thing thing, WledApiFactory apiFactory, this.stateDescriptionProvider = stateDescriptionProvider; } - private void sendGetRequest(String url) { - } - - private void sendWhite() { - if (hasWhite) { - sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=hFF000000" + "&A=" + masterBrightness255); - } else { - sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=hFFFFFF" + "&A=" + masterBrightness255); - } - } - - /** - * - * @param hsb - * @return WLED needs the letter h followed by 2 digit HEX code for RRGGBB - */ - private String createColorHex(HSBType hsb) { - return String.format("h%06X", hsb.getRGB() & 0x00FFFFFF); - } - @Override public void handleCommand(ChannelUID channelUID, Command command) { + WledApi localApi = api; + if (localApi == null) { + return; + } BigDecimal bigTemp; - PercentType localPercentType; if (command instanceof RefreshType) { - switch (channelUID.getId()) { - case CHANNEL_MASTER_CONTROLS: - sendGetRequest("/win"); - } return;// no need to check for refresh below } logger.debug("command {} sent to {}", command, channelUID.getId()); try { switch (channelUID.getId()) { case CHANNEL_SYNC_SEND: - api.setUdpSend(OnOffType.ON.equals(command)); + localApi.setUdpSend(OnOffType.ON.equals(command)); break; case CHANNEL_SYNC_RECEIVE: - api.setUdpRecieve(OnOffType.ON.equals(command)); + localApi.setUdpRecieve(OnOffType.ON.equals(command)); break; case CHANNEL_PRIMARY_WHITE: if (command instanceof PercentType) { - sendGetRequest("/win&W=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); + localApi.sendGetRequest( + "/win&W=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); } break; case CHANNEL_SECONDARY_WHITE: if (command instanceof PercentType) { - sendGetRequest("/win&W2=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); + localApi.sendGetRequest( + "/win&W2=" + ((PercentType) command).toBigDecimal().multiply(BIG_DECIMAL_2_55)); } break; case CHANNEL_MASTER_CONTROLS: if (command instanceof OnOffType) { - api.setMasterOn(OnOffType.ON.equals(command)); + localApi.setMasterOn(OnOffType.ON.equals(command)); } else if (command instanceof IncreaseDecreaseType) { if (IncreaseDecreaseType.INCREASE.equals(command)) { if (masterBrightness255.intValue() < 240) { - sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 different levels + localApi.sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 different + // levels } else { - sendGetRequest("/win&TT=1000&A=255"); + localApi.sendGetRequest("/win&TT=1000&A=255"); } } else { if (masterBrightness255.intValue() > 15) { - sendGetRequest("/win&TT=1000&A=~-15"); + localApi.sendGetRequest("/win&TT=1000&A=~-15"); } else { - sendGetRequest("/win&TT=1000&A=0"); + localApi.sendGetRequest("/win&TT=1000&A=0"); } } } else if (command instanceof HSBType) { - api.setMasterHSB((HSBType) command); + if ((((HSBType) command).getBrightness()) == PercentType.ZERO) { + localApi.setMasterOn(false); + } + // PercentType savedLevel = primaryColor.getBrightness(); + primaryColor = (HSBType) command; + // hue65535 = primaryColor.getHue().toBigDecimal().multiply(BIG_DECIMAL_182_04); + // saturation255 = primaryColor.getSaturation().toBigDecimal().multiply(BIG_DECIMAL_2_55); + // masterBrightness255 = primaryColor.getBrightness().toBigDecimal().multiply(BIG_DECIMAL_2_55); + if (primaryColor.getSaturation().intValue() < config.saturationThreshold) { + // sendWhite(); + } else if (primaryColor.getSaturation().intValue() == 32 + && primaryColor.getHue().intValue() == 36 && hasWhite) { + // Google sends this when it wants white + // sendWhite(); + } else { + localApi.setMasterHSB((HSBType) command); + } } else if (command instanceof PercentType) { - api.setMasterBrightness((PercentType) command); + localApi.setMasterBrightness((PercentType) command); } return; case CHANNEL_PRIMARY_COLOR: if (command instanceof HSBType) { primaryColor = (HSBType) command; - sendGetRequest("/win&CL=" + createColorHex(primaryColor)); } else if (command instanceof PercentType) { primaryColor = new HSBType(primaryColor.getHue(), primaryColor.getSaturation(), ((PercentType) command)); - sendGetRequest("/win&CL=" + createColorHex(primaryColor)); } + localApi.setPrimaryColor(primaryColor); return; case CHANNEL_SECONDARY_COLOR: if (command instanceof HSBType) { secondaryColor = (HSBType) command; - sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); } else if (command instanceof PercentType) { secondaryColor = new HSBType(secondaryColor.getHue(), secondaryColor.getSaturation(), ((PercentType) command)); - sendGetRequest("/win&C2=" + createColorHex(secondaryColor)); } + localApi.setSecondaryColor(secondaryColor); + return; + case CHANNEL_THIRD_COLOR: + if (command instanceof HSBType) { + thirdColor = (HSBType) command; + } else if (command instanceof PercentType) { + thirdColor = new HSBType(thirdColor.getHue(), thirdColor.getSaturation(), + ((PercentType) command)); + } + localApi.setTertiaryColor(thirdColor); return; case CHANNEL_PALETTES: - api.setPalette(command.toString()); + localApi.setPalette(command.toString()); break; case CHANNEL_FX: - api.setPreset(command.toString()); + localApi.setEffect(command.toString()); break; case CHANNEL_SPEED: - api.setFxSpeed((PercentType) command); + localApi.setFxSpeed((PercentType) command); break; case CHANNEL_INTENSITY: - api.setFxIntencity((PercentType) command); + localApi.setFxIntencity((PercentType) command); break; case CHANNEL_SLEEP: - api.setSleep(OnOffType.ON.equals(command)); + localApi.setSleep(OnOffType.ON.equals(command)); break; case CHANNEL_PRESETS: - sendGetRequest("/win&PL=" + command); + localApi.setPreset(command.toString()); break; case CHANNEL_PRESET_DURATION: if (command instanceof QuantityType) { QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); if (seconds != null) { bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)); - sendGetRequest("/win&PT=" + bigTemp.intValue()); + localApi.sendGetRequest("/win&PT=" + bigTemp.intValue()); } } break; @@ -197,14 +201,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof QuantityType) { QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); if (seconds != null) { - api.setTransitionTime( + localApi.setTransitionTime( new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)).intValue()); } } break; case CHANNEL_PRESET_CYCLE: if (command instanceof OnOffType) { - api.setPresetCycle(OnOffType.ON.equals(command)); + localApi.setPresetCycle(OnOffType.ON.equals(command)); } break; } @@ -218,7 +222,7 @@ public void savePreset(int presetIndex) { logger.warn("Presets above 16 do not exist, and the action sent {}", presetIndex); return; } - sendGetRequest("/win&PS=" + presetIndex); + // sendGetRequest("/win&PS=" + presetIndex); } public void update(String channelID, State state) { @@ -236,7 +240,7 @@ private void pollState() { updateStatus(ThingStatus.ONLINE); } } catch (ApiException e) { - api = null;// recheck the firmware as it may have been updated + api = null;// recheck the firmware was not just updated updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index 02d98826fdde..dce92433a812 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -29,12 +29,18 @@ public interface WledApi { public abstract int getFirmwareVersion() throws ApiException; + public abstract void setGlobalOn(boolean bool) throws ApiException; + public abstract void setMasterOn(boolean bool) throws ApiException; + public abstract void setGlobalBrightness(PercentType percent) throws ApiException; + public abstract void setMasterBrightness(PercentType percent) throws ApiException; public abstract void setMasterHSB(HSBType hsbType) throws ApiException; + public abstract void setEffect(String string) throws ApiException; + public abstract void setPreset(String string) throws ApiException; public abstract void setPalette(String string) throws ApiException; @@ -52,4 +58,12 @@ public interface WledApi { public abstract void setTransitionTime(int milliseconds) throws ApiException; public abstract void setPresetCycle(boolean bool) throws ApiException; + + public abstract void setPrimaryColor(HSBType hsbType) throws ApiException; + + public abstract void setSecondaryColor(HSBType hsbType) throws ApiException; + + public abstract void setTertiaryColor(HSBType hsbType) throws ApiException; + + public abstract String sendGetRequest(String string) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index ca5a900de278..9f5fb9b1f190 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -69,7 +69,15 @@ public WledApiV084(WLedHandler handler, WLedConfiguration config, HttpClient htt this.httpClient = httpClient; } - protected String sendGetRequest(String url) throws ApiException { + @Override + public void initialize() throws ApiException { + state.jsonResponse = getJson(); + getUpdatedFxList(); + getUpdatedPaletteList(); + } + + @Override + public String sendGetRequest(String url) throws ApiException { Request request = httpClient.newRequest(config.address + url); request.timeout(3, TimeUnit.SECONDS); request.method(HttpMethod.GET); @@ -100,10 +108,9 @@ protected void postState(String json) throws ApiException { } protected void sendPostRequest(String url, String json) throws ApiException { - logger.debug("Sending WLED POST:{} Message:{}", url, json); + logger.trace("Sending WLED POST:{} Message:{}", url, json); Request request = httpClient.POST(config.address + url); request.timeout(3, TimeUnit.SECONDS); - // request.header(HttpHeader.ACCEPT, "application/json"); request.header(HttpHeader.CONTENT_TYPE, "application/json"); request.content(new StringContentProvider(json), "application/json"); String errorReason = ""; @@ -186,13 +193,6 @@ protected void getUpdatedPaletteList() { palleteOptions); } - @Override - public void initialize() throws ApiException { - state.jsonResponse = getJson(); - getUpdatedFxList(); - getUpdatedPaletteList(); - } - @Override public int getFirmwareVersion() throws ApiException { state.infoResponse = getInfo(); @@ -207,11 +207,30 @@ public int getFirmwareVersion() throws ApiException { } protected void processState() { + HSBType tempHSB = WLedHelper + .parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString()); + handler.update(CHANNEL_MASTER_CONTROLS, tempHSB); + handler.update(CHANNEL_PRIMARY_COLOR, tempHSB); + handler.update(CHANNEL_SECONDARY_COLOR, + WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); + handler.update(CHANNEL_THIRD_COLOR, + WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + // white LEDs for RGBW strings + handler.update(CHANNEL_PRIMARY_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); + handler.update(CHANNEL_SECONDARY_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); + handler.update(CHANNEL_THIRD_WHITE, + WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); if (!state.stateResponse.on) { - handler.update(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); + // global ch needs adding } else { - handler.update(CHANNEL_MASTER_CONTROLS, new PercentType( - new BigDecimal(state.stateResponse.bri).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + // global ch needs adding + } + + // state can say it is on but the lights are off due to 0,0,0 for r,g,b + if (!state.stateResponse.seg[handler.config.segmentIndex].on) { + handler.update(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); } if (state.nightLightState.on) { handler.update(CHANNEL_SLEEP, OnOffType.ON); @@ -244,29 +263,29 @@ protected void processState() { handler.update(CHANNEL_INTENSITY, new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].ix) .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + } - handler.update(CHANNEL_PRIMARY_COLOR, - WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); - handler.update(CHANNEL_SECONDARY_COLOR, - WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); - handler.update(CHANNEL_THIRD_COLOR, - WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); - // white LEDs for RGBW strings - handler.update(CHANNEL_PRIMARY_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); - handler.update(CHANNEL_SECONDARY_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); - handler.update(CHANNEL_THIRD_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + /** + * Turns on/off ALL segments at the SAME TIME + */ + @Override + public void setGlobalOn(boolean bool) throws ApiException { + postState("{\"on\":" + bool + "}"); } + /** + * Turns on/off just THIS segment + */ @Override public void setMasterOn(boolean bool) throws ApiException { - postState("{\"on\":" + bool + "}"); + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":" + bool + "}]}"); } + /** + * Sets the brightness of ALL segments at the SAME TIME + */ @Override - public void setMasterBrightness(PercentType percent) throws ApiException { + public void setGlobalBrightness(PercentType percent) throws ApiException { if (percent.equals(PercentType.ZERO)) { postState("{\"on\":false}"); return; @@ -274,57 +293,59 @@ public void setMasterBrightness(PercentType percent) throws ApiException { postState("{\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55) + "}"); } + /** + * Sets the brightness of just THIS segment + */ + @Override + public void setMasterBrightness(PercentType percent) throws ApiException { + if (percent.equals(PercentType.ZERO)) { + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":false}]}"); + return; + } + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":true,\"bri\":" + + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55) + "}]}"); + } + @Override public void setMasterHSB(HSBType hsbType) throws ApiException { - /* - * primaryColor = (HSBType) command; - * hue65535 = primaryColor.getHue().toBigDecimal().multiply(BIG_DECIMAL_182_04); - * saturation255 = primaryColor.getSaturation().toBigDecimal().multiply(BIG_DECIMAL_2_55); - * masterBrightness255 = primaryColor.getBrightness().toBigDecimal().multiply(BIG_DECIMAL_2_55); - * if (primaryColor.getSaturation().intValue() < config.saturationThreshold) { - * sendWhite(); - * } else if (primaryColor.getSaturation().intValue() == 32 && primaryColor.getHue().intValue() == 36 - * && hasWhite) { - * // Google sends this when it wants white - * sendWhite(); - * } else { - * if (config.segmentIndex == -1) { - * sendGetRequest( - * "/win&TT=1000&FX=0&CY=0&HU=" + hue65535 + "&SA=" + saturation255 + "&A=" + masterBrightness255); - * } else { - * sendGetRequest( - * "/win&TT=1000&FX=0&CY=0&CL=" + createColorHex(primaryColor) + "&A=" + masterBrightness255); - * } - * } - */ - if (hsbType.getBrightness().equals(PercentType.ZERO)) { - postState("{\"on\":false}"); + if (hsbType.getBrightness().toBigDecimal().equals(BigDecimal.ZERO)) { + postState("{\"seg\":[{\"on\":false,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); return; } - postState("{\"on\":true,\"seg\":[{\"col\":[[" + postState("{\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); } + @Override + public void setEffect(String string) throws ApiException { + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"fx\":" + string + "}]}"); + } + @Override public void setPreset(String string) throws ApiException { - postState("{\"seg\":[{\"fx\":" + string + "}]}"); + postState("{\"ps\":" + string + "}"); } @Override public void setPalette(String string) throws ApiException { - postState("{\"seg\":[{\"pal\":" + string + "}]}"); + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"pal\":" + string + "}]}"); } @Override public void setFxIntencity(PercentType percentType) throws ApiException { - postState("{\"seg\":[{\"ix\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"ix\":" + + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); } @Override public void setFxSpeed(PercentType percentType) throws ApiException { - postState("{\"seg\":[{\"sx\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"sx\":" + + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); } @Override @@ -355,4 +376,28 @@ public void setPresetCycle(boolean bool) throws ApiException { postState("{\"pl\":-1}"); } } + + @Override + public void setPrimaryColor(HSBType hsbType) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[],[]]}]}"); + } + + @Override + public void setSecondaryColor(HSBType hsbType) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[],[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[]]}]}"); + } + + @Override + public void setTertiaryColor(HSBType hsbType) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[],[],[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); + } } From fa07d3cee3406aa178f783c523a35fb7e3a4a6a2 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Tue, 2 Nov 2021 10:38:59 +1100 Subject: [PATCH 04/15] changes to json api fully made Signed-off-by: Matthew Skinner --- .../binding/wled/internal/WLedHandler.java | 14 +++---- .../binding/wled/internal/WledState.java | 9 +++-- .../binding/wled/internal/api/WledApi.java | 4 +- .../wled/internal/api/WledApiV084.java | 39 +++++++++++++------ 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index efaacf7d7d0a..decee63bdcaa 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -122,20 +122,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } } else if (command instanceof HSBType) { - if ((((HSBType) command).getBrightness()) == PercentType.ZERO) { + if ((((HSBType) command).getBrightness()).equals(PercentType.ZERO)) { localApi.setMasterOn(false); + return; } - // PercentType savedLevel = primaryColor.getBrightness(); primaryColor = (HSBType) command; - // hue65535 = primaryColor.getHue().toBigDecimal().multiply(BIG_DECIMAL_182_04); - // saturation255 = primaryColor.getSaturation().toBigDecimal().multiply(BIG_DECIMAL_2_55); - // masterBrightness255 = primaryColor.getBrightness().toBigDecimal().multiply(BIG_DECIMAL_2_55); - if (primaryColor.getSaturation().intValue() < config.saturationThreshold) { - // sendWhite(); + if (primaryColor.getSaturation().intValue() < config.saturationThreshold && hasWhite) { + localApi.setWhiteOnly((PercentType) command); } else if (primaryColor.getSaturation().intValue() == 32 && primaryColor.getHue().intValue() == 36 && hasWhite) { - // Google sends this when it wants white - // sendWhite(); + localApi.setWhiteOnly((PercentType) command); } else { localApi.setMasterHSB((HSBType) command); } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java index c9732e30b43f..a8569e0f4d6f 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java @@ -17,8 +17,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.gson.Gson; @@ -29,11 +27,11 @@ */ @NonNullByDefault public class WledState { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); protected final Gson gson = new Gson(); public JsonResponse jsonResponse = new JsonResponse(); public StateResponse stateResponse = new StateResponse(); public InfoResponse infoResponse = new InfoResponse(); + public LedInfo ledInfo = new LedInfo(); public NightLightState nightLightState = new NightLightState(); public UdpnState udpnState = new UdpnState(); public SegmentState segmentState = new SegmentState(); @@ -106,5 +104,10 @@ public class NightLightState { public class InfoResponse { public String ver = "00000"; public String mac = ""; + public Object leds = "{}"; + } + + public class LedInfo { + public boolean rgbw = false; } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index dce92433a812..f071b3258aed 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -49,7 +49,7 @@ public interface WledApi { public abstract void setFxSpeed(PercentType percentType) throws ApiException; - public abstract void setSleep(boolean b) throws ApiException; + public abstract void setSleep(boolean bool) throws ApiException; public abstract void setUdpSend(boolean bool) throws ApiException; @@ -66,4 +66,6 @@ public interface WledApi { public abstract void setTertiaryColor(HSBType hsbType) throws ApiException; public abstract String sendGetRequest(String string) throws ApiException; + + public abstract void setWhiteOnly(PercentType percentType) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index 9f5fb9b1f190..c373693126d3 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeoutException; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; @@ -35,6 +36,7 @@ import org.openhab.binding.wled.internal.WledState; import org.openhab.binding.wled.internal.WledState.InfoResponse; import org.openhab.binding.wled.internal.WledState.JsonResponse; +import org.openhab.binding.wled.internal.WledState.LedInfo; import org.openhab.binding.wled.internal.WledState.StateResponse; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; @@ -50,6 +52,7 @@ /** * The {@link WledApiV084} is the json Api methods for firmware version 0.8.4 and newer + * as newer firmwares come out with breaking changes, extend this class into a newer firmware version class. * * @author Matthew Skinner - Initial contribution */ @@ -74,6 +77,13 @@ public void initialize() throws ApiException { state.jsonResponse = getJson(); getUpdatedFxList(); getUpdatedPaletteList(); + + @Nullable + LedInfo localLedInfo = gson.fromJson(state.infoResponse.leds.toString(), LedInfo.class); + if (localLedInfo != null) { + state.ledInfo = localLedInfo; + } + handler.hasWhite = state.ledInfo.rgbw; } @Override @@ -108,7 +118,7 @@ protected void postState(String json) throws ApiException { } protected void sendPostRequest(String url, String json) throws ApiException { - logger.trace("Sending WLED POST:{} Message:{}", url, json); + logger.debug("Sending WLED POST:{} Message:{}", url, json); Request request = httpClient.POST(config.address + url); request.timeout(3, TimeUnit.SECONDS); request.header(HttpHeader.CONTENT_TYPE, "application/json"); @@ -215,20 +225,21 @@ protected void processState() { WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); handler.update(CHANNEL_THIRD_COLOR, WLedHelper.parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); - // white LEDs for RGBW strings - handler.update(CHANNEL_PRIMARY_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); - handler.update(CHANNEL_SECONDARY_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); - handler.update(CHANNEL_THIRD_WHITE, - WLedHelper.parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + if (state.ledInfo.rgbw) { + handler.update(CHANNEL_PRIMARY_WHITE, WLedHelper + .parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString())); + handler.update(CHANNEL_SECONDARY_WHITE, WLedHelper + .parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[1].toString())); + handler.update(CHANNEL_THIRD_WHITE, WLedHelper + .parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); + } + if (!state.stateResponse.on) { // global ch needs adding } else { // global ch needs adding } - // state can say it is on but the lights are off due to 0,0,0 for r,g,b if (!state.stateResponse.seg[handler.config.segmentIndex].on) { handler.update(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); } @@ -349,8 +360,8 @@ public void setFxSpeed(PercentType percentType) throws ApiException { } @Override - public void setSleep(boolean b) throws ApiException { - postState("{\"nl\":{\"on\":" + b + "}}"); + public void setSleep(boolean bool) throws ApiException { + postState("{\"nl\":{\"on\":" + bool + "}}"); } @Override @@ -400,4 +411,10 @@ public void setTertiaryColor(HSBType hsbType) throws ApiException { + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); } + + @Override + public void setWhiteOnly(PercentType percentType) throws ApiException { + postState("{\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[0,0,0," + + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); + } } From 1745474b79d7a53d42c8e82b915b6e787102c8c6 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Tue, 2 Nov 2021 11:13:54 +1100 Subject: [PATCH 05/15] Mirror and Reverse channels added. Signed-off-by: Matthew Skinner --- .../wled/internal/WLedBindingConstants.java | 2 ++ .../binding/wled/internal/WLedHandler.java | 29 ++++++++++++++----- .../binding/wled/internal/api/WledApi.java | 4 +++ .../wled/internal/api/WledApiV084.java | 20 +++++++++++++ .../resources/OH-INF/thing/thing-types.xml | 14 +++++++++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java index 1085ca87a349..d01f0d8d1423 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java @@ -57,6 +57,8 @@ public class WLedBindingConstants { public static final String CHANNEL_FX = "fx"; public static final String CHANNEL_SPEED = "speed"; public static final String CHANNEL_INTENSITY = "intensity"; + public static final String CHANNEL_MIRROR = "mirror"; + public static final String CHANNEL_REVERSE = "reverse"; public static final String CHANNEL_SLEEP = "sleep"; public static final String CHANNEL_SYNC_SEND = "syncSend"; public static final String CHANNEL_SYNC_RECEIVE = "syncReceive"; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index decee63bdcaa..0526d2ac2b56 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -85,6 +85,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("command {} sent to {}", command, channelUID.getId()); try { switch (channelUID.getId()) { + case CHANNEL_MIRROR: + localApi.setMirror(OnOffType.ON.equals(command)); + break; + case CHANNEL_REVERSE: + localApi.setReverse(OnOffType.ON.equals(command)); + break; case CHANNEL_SYNC_SEND: localApi.setUdpSend(OnOffType.ON.equals(command)); break; @@ -109,8 +115,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else if (command instanceof IncreaseDecreaseType) { if (IncreaseDecreaseType.INCREASE.equals(command)) { if (masterBrightness255.intValue() < 240) { - localApi.sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 different - // levels + localApi.sendGetRequest("/win&TT=1000&A=~15"); // 255 divided by 15 = 17 levels } else { localApi.sendGetRequest("/win&TT=1000&A=255"); } @@ -218,7 +223,13 @@ public void savePreset(int presetIndex) { logger.warn("Presets above 16 do not exist, and the action sent {}", presetIndex); return; } - // sendGetRequest("/win&PS=" + presetIndex); + try { + if (api != null) { + api.sendGetRequest("/win&PS=" + presetIndex); + } + } catch (ApiException e) { + logger.warn("Preset failed to save due to:{}", e.getMessage()); + } } public void update(String channelID, State state) { @@ -231,10 +242,12 @@ private void pollState() { if (localApi == null) { api = apiFactory.getApi(this, config); api.initialize(); - } else { - localApi.update(); - updateStatus(ThingStatus.ONLINE); } + if (localApi == null) { + return; + } + localApi.update(); + updateStatus(ThingStatus.ONLINE); } catch (ApiException e) { api = null;// recheck the firmware was not just updated updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); @@ -244,14 +257,14 @@ private void pollState() { @Override public void initialize() { config = getConfigAs(WLedConfiguration.class); - if (config.segmentIndex == -1) { + if (config.segmentIndex < 0) { config.segmentIndex = 0; } if (!config.address.contains("://")) { logger.debug("Address was not entered in correct format, it may be the raw IP so adding http:// to start"); config.address = "http://" + config.address; } - pollingFuture = scheduler.scheduleWithFixedDelay(this::pollState, 1, config.pollTime, TimeUnit.SECONDS); + pollingFuture = scheduler.scheduleWithFixedDelay(this::pollState, 0, config.pollTime, TimeUnit.SECONDS); } @Override diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index f071b3258aed..161209238246 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -68,4 +68,8 @@ public interface WledApi { public abstract String sendGetRequest(String string) throws ApiException; public abstract void setWhiteOnly(PercentType percentType) throws ApiException; + + public abstract void setMirror(boolean bool) throws ApiException; + + public abstract void setReverse(boolean bool) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index c373693126d3..0e83b7e0a49a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -263,6 +263,16 @@ protected void processState() { } else { handler.update(CHANNEL_SYNC_SEND, OnOffType.OFF); } + if (state.stateResponse.seg[handler.config.segmentIndex].mi) { + handler.update(CHANNEL_MIRROR, OnOffType.ON); + } else { + handler.update(CHANNEL_MIRROR, OnOffType.OFF); + } + if (state.stateResponse.seg[handler.config.segmentIndex].rev) { + handler.update(CHANNEL_REVERSE, OnOffType.ON); + } else { + handler.update(CHANNEL_REVERSE, OnOffType.OFF); + } handler.update(CHANNEL_TRANS_TIME, new PercentType( new BigDecimal(state.stateResponse.transition).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); handler.update(CHANNEL_PRESETS, new StringType("" + state.stateResponse.ps)); @@ -417,4 +427,14 @@ public void setWhiteOnly(PercentType percentType) throws ApiException { postState("{\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[0,0,0," + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); } + + @Override + public void setMirror(boolean bool) throws ApiException { + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"mi\":" + bool + "}]}"); + } + + @Override + public void setReverse(boolean bool) throws ApiException { + postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"rev\":" + bool + "}]}"); + } } diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index bb0e2e9a858a..57013e057dc0 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -24,6 +24,8 @@ + + @@ -172,6 +174,18 @@ Change the intensity of the FX + + Switch + + Mirror the effect for this segment + + + + Switch + + Reverse the direction of the current segment + + Switch From 502dca87d8db175c53fa83d961cb749b2de61427 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Thu, 4 Nov 2021 22:01:14 +1100 Subject: [PATCH 06/15] Remove old channels when needed. Signed-off-by: Matthew Skinner --- bundles/org.openhab.binding.wled/README.md | 21 ++-- .../wled/internal/WLedBindingConstants.java | 1 + .../binding/wled/internal/WLedHandler.java | 29 ++++- .../binding/wled/internal/WledState.java | 1 - .../binding/wled/internal/api/WledApi.java | 4 +- .../wled/internal/api/WledApiFactory.java | 5 +- .../wled/internal/api/WledApiV0130.java | 53 +++++++++ .../wled/internal/api/WledApiV084.java | 110 +++++++++++++----- .../resources/OH-INF/thing/thing-types.xml | 52 ++++++--- 9 files changed, 213 insertions(+), 63 deletions(-) create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java diff --git a/bundles/org.openhab.binding.wled/README.md b/bundles/org.openhab.binding.wled/README.md index c0689ddfb901..c3450b5462fb 100644 --- a/bundles/org.openhab.binding.wled/README.md +++ b/bundles/org.openhab.binding.wled/README.md @@ -31,22 +31,27 @@ For additional segments, you can add them manually and set the `segmentIndex` co | Channel | Type | Description | |-|-|-| -| `masterControls` | Color | Gives you control over the WLED like it is any normal light. Tag this control for Alexa or Google/Nest to change the lights instantly to any colour, brightness or on/off state that you ask for regardless of what mode the light is in. | +| `masterControls` | Color | Gives you control over the WLED segment like it is any normal light. Tag this control for Alexa or Google/Nest to change the lights instantly to any colour, brightness or on/off state that you ask for regardless of what mode the light is in. | +| `segmentBrightness` | Dimmer | Allows you to Dim and turn the entire segment ON and OFF. | | `primaryColor` | Color | The primary colour used in FX. | -| `primaryWhite` | Dimmer | The amount of white light used in the primary colour if you have RGBW LEDs. | +| `primaryWhite` | Dimmer | The amount of white light used in the primary colour. Only available if you have RGBW LEDs. | | `secondaryColor` | Color | The secondary colour used in FX. | -| `secondaryWhite` | Dimmer | The amount of white light used in the secondary colour if you have RGBW LEDs. | +| `secondaryWhite` | Dimmer | The amount of white light used in the second colour. Only available if you have RGBW LEDs. | +| `tertiaryColor` | Color | The third colour used in FX. | +| `tertiaryWhite` | Dimmer | The amount of white light used in the third colour. Only available if you have RGBW LEDs. | | `palettes` | String | A list of colour palettes you can select from that are used in the FX. | | `fx` | String | A list of Effects you can select from. | | `speed` | Dimmer | Changes the speed of the loaded effect. | | `intensity` | Dimmer | Changes the intensity of the loaded effect. | | `presets` | String | A list of presets that you can select from. | -| `presetCycle` | Switch | Turns ON/OFF the automatic changing from one preset to the next. | -| `presetDuration` | Number:Time | How long in seconds it will display a preset for, before it begins to change from one preset to the next with `presetCycle` turned ON. | +| `presetCycle` | Switch | Turns ON/OFF the automatic changing from one preset to the next. Only in V0.12.0 and older firmwares. | +| `presetDuration` | Number:Time | How long in seconds it will display a preset for, before it begins to change from one preset to the next with `presetCycle` turned ON. Only in V0.12.0 and older firmwares. | | `transformTime` | Number:Time | How long in seconds it takes to transform/morph from one look to the next. | | `sleep` | Switch | Turns on the sleep or 'night light' timer which can be configured to work in many different ways. Refer to WLED documentation for how this can be setup. The default action is the light will fade to OFF over the next 60 minutes. | | `syncSend` | Switch | Sends UDP packets that tell other WLED lights to follow this one. | | `syncReceive` | Switch | Allows UDP packets from other WLED lights to control this one. | +| `mirror` | Switch | Mirror the effect for this segment. | +| `reverse` | Switch | Reverse the effect for this segment. | ## Rule Actions @@ -68,7 +73,7 @@ If you use the ADMIN>MODEL>`Create equipment from thing` feature you can use the ``` Text label="XmasLights" icon="rgb"{ Switch item=XmasTree_MasterControls - Slider item=XmasTree_MasterControls + Slider item=XmasTree_SegmentBrightness Colorpicker item=XmasTree_MasterControls Switch item=XmasTree_SleepTimer Colorpicker item=XmasTree_PrimaryColor @@ -77,9 +82,7 @@ If you use the ADMIN>MODEL>`Create equipment from thing` feature you can use the Selection item=XmasTree_Palettes Selection item=XmasTree_Presets Default item=XmasTree_FXSpeed - Default item=XmasTree_FXIntensity - Default item=XmasTree_PresetCycle - Selection item=XmasTree_PresetDuration mappings=[2 ='2 seconds', 10='10 seconds', 30='30 seconds', 60='60 seconds'] + Default item=XmasTree_FXIntensity Selection item=XmasTree_TransformTime mappings=[0='0 seconds', 2 ='2 seconds', 10='10 seconds', 30='30 seconds', 60='60 seconds'] } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java index d01f0d8d1423..b31676323d79 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java @@ -43,6 +43,7 @@ public class WLedBindingConstants { // Channels public static final String CHANNEL_MASTER_CONTROLS = "masterControls"; + public static final String CHANNEL_SEGMENT_BRIGHTNESS = "segmentBrightness"; public static final String CHANNEL_PRIMARY_COLOR = "primaryColor"; public static final String CHANNEL_SECONDARY_COLOR = "secondaryColor"; public static final String CHANNEL_THIRD_COLOR = "tertiaryColor"; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index 0526d2ac2b56..61a2e71bddb3 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -15,6 +15,7 @@ import static org.openhab.binding.wled.internal.WLedBindingConstants.*; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Future; @@ -32,12 +33,14 @@ import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -85,6 +88,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("command {} sent to {}", command, channelUID.getId()); try { switch (channelUID.getId()) { + case CHANNEL_SEGMENT_BRIGHTNESS: + if (command instanceof OnOffType) { + localApi.setMasterOn(OnOffType.ON.equals(command)); + } else if (command instanceof PercentType) { + if (PercentType.ZERO.equals(command)) { + localApi.setMasterOn(false); + return; + } + localApi.setMasterBrightness((PercentType) command); + } + break; case CHANNEL_MIRROR: localApi.setMirror(OnOffType.ON.equals(command)); break; @@ -189,7 +203,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_PRESETS: localApi.setPreset(command.toString()); break; - case CHANNEL_PRESET_DURATION: + case CHANNEL_PRESET_DURATION:// ch removed in firmware 0.13.0 and newer if (command instanceof QuantityType) { QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); if (seconds != null) { @@ -202,12 +216,11 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof QuantityType) { QuantityType seconds = ((QuantityType) command).toUnit(Units.SECOND); if (seconds != null) { - localApi.setTransitionTime( - new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000)).intValue()); + localApi.setTransitionTime(new BigDecimal(seconds.multiply(BigDecimal.TEN).intValue())); } } break; - case CHANNEL_PRESET_CYCLE: + case CHANNEL_PRESET_CYCLE: // ch removed in firmware 0.13.0 and newer if (command instanceof OnOffType) { localApi.setPresetCycle(OnOffType.ON.equals(command)); } @@ -232,6 +245,14 @@ public void savePreset(int presetIndex) { } } + public void removeChannels(ArrayList removeChannels) { + if (!removeChannels.isEmpty()) { + ThingBuilder thingBuilder = editThing(); + thingBuilder.withoutChannels(removeChannels); + updateThing(thingBuilder.build()); + } + } + public void update(String channelID, State state) { updateState(channelID, state); } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java index a8569e0f4d6f..49e39f29cc8a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java @@ -94,7 +94,6 @@ public class SegmentState { public class NightLightState { public boolean on = true; - public boolean fade = true; public int dur = 0; public int mode = 0; public int tbri = 0; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index 161209238246..82200665d405 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.wled.internal.api; +import java.math.BigDecimal; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.PercentType; @@ -55,7 +57,7 @@ public interface WledApi { public abstract void setUdpRecieve(boolean bool) throws ApiException; - public abstract void setTransitionTime(int milliseconds) throws ApiException; + public abstract void setTransitionTime(BigDecimal time) throws ApiException; public abstract void setPresetCycle(boolean bool) throws ApiException; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java index 13fe0dc79303..70b2fe22418d 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java @@ -44,8 +44,9 @@ public WledApi getApi(WLedHandler wLedHandler, WLedConfiguration config) throws WledApi lowestSupportedApi = new WledApiV084(wLedHandler, config, httpClient); int version = lowestSupportedApi.getFirmwareVersion(); logger.debug("Treating firmware as int:{}", version); - // json api was in ver 0.8.4 but may lack testing until 0.10.0 aka 100 - if (version > 100) { + if (version >= 130) { + return new WledApiV0130(wLedHandler, config, httpClient); + } else if (version > 100) { return new WledApiV084(wLedHandler, config, httpClient); } logger.warn("Your WLED firmware is very old, upgrade to at least 0.10.0"); diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java new file mode 100644 index 000000000000..69934a01927e --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import static org.openhab.binding.wled.internal.WLedBindingConstants.*; + +import java.util.ArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.wled.internal.WLedConfiguration; +import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.core.thing.Channel; + +/** + * The {@link WledApiV0130} is the json Api methods for firmware version 0.13.0 and newer + * as newer firmwares come out with breaking changes, extend this class into a newer firmware version class. + * + * @author Matthew Skinner - Initial contribution + */ +@NonNullByDefault +public class WledApiV0130 extends WledApiV084 { + + public WledApiV0130(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { + super(handler, config, httpClient); + } + + @Override + public void initialize() throws ApiException { + super.initialize(); + ArrayList removeChannels = new ArrayList<>(); + // This version of firmware removed these channels + Channel channel = handler.getThing().getChannel(CHANNEL_PRESET_DURATION); + if (channel != null) { + removeChannels.add(channel); + } + channel = handler.getThing().getChannel(CHANNEL_PRESET_CYCLE); + if (channel != null) { + removeChannels.add(channel); + } + handler.removeChannels(removeChannels); + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index 0e83b7e0a49a..f98bdf807cd2 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -41,7 +41,10 @@ import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.types.StateOption; import org.slf4j.Logger; @@ -58,7 +61,7 @@ */ @NonNullByDefault public class WledApiV084 implements WledApi { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); protected final Gson gson = new Gson(); protected final HttpClient httpClient; protected final WLedConfiguration config; @@ -83,7 +86,25 @@ public void initialize() throws ApiException { if (localLedInfo != null) { state.ledInfo = localLedInfo; } + handler.hasWhite = state.ledInfo.rgbw; + ArrayList removeChannels = new ArrayList<>(); + if (!state.ledInfo.rgbw) { + logger.debug("WLED is not setup to use RGBW, so removing un-needed white channels"); + Channel channel = handler.getThing().getChannel(CHANNEL_PRIMARY_WHITE); + if (channel != null) { + removeChannels.add(channel); + } + channel = handler.getThing().getChannel(CHANNEL_SECONDARY_WHITE); + if (channel != null) { + removeChannels.add(channel); + } + channel = handler.getThing().getChannel(CHANNEL_THIRD_WHITE); + if (channel != null) { + removeChannels.add(channel); + } + } + handler.removeChannels(removeChannels); } @Override @@ -113,11 +134,11 @@ public String sendGetRequest(String url) throws ApiException { throw new ApiException(errorReason); } - protected void postState(String json) throws ApiException { - sendPostRequest("/json/state", json); + protected String postState(String json) throws ApiException { + return sendPostRequest("/json/state", json); } - protected void sendPostRequest(String url, String json) throws ApiException { + protected String sendPostRequest(String url, String json) throws ApiException { logger.debug("Sending WLED POST:{} Message:{}", url, json); Request request = httpClient.POST(config.address + url); request.timeout(3, TimeUnit.SECONDS); @@ -125,8 +146,13 @@ protected void sendPostRequest(String url, String json) throws ApiException { request.content(new StringContentProvider(json), "application/json"); String errorReason = ""; try { - request.send(); - return; + ContentResponse contentResponse = request.send(); + if (contentResponse.getStatus() == 200) { + return contentResponse.getContentAsString(); + } else { + errorReason = String.format("WLED request failed with %d: %s", contentResponse.getStatus(), + contentResponse.getReason()); + } } catch (InterruptedException e) { errorReason = String.format("InterruptedException: %s", e.getMessage()); } catch (TimeoutException e) { @@ -137,6 +163,20 @@ protected void sendPostRequest(String url, String json) throws ApiException { throw new ApiException(errorReason); } + protected void setState(String jsonState) { + try { + StateResponse response = gson.fromJson(jsonState, StateResponse.class); + if (response == null) { + throw new ApiException("Reply back from WLED when command was made is not valid JSON"); + } + state.stateResponse = response; + state.unpackJsonObjects(); + processState(); + } catch (JsonSyntaxException | ApiException e) { + logger.debug("Reply back when a command was sent triggered an exception:{}", jsonState); + } + } + protected StateResponse getState() throws ApiException { try { String returnContent = sendGetRequest("/json/state"); @@ -216,7 +256,11 @@ public int getFirmwareVersion() throws ApiException { return version; } - protected void processState() { + protected void processState() throws ApiException { + if (state.stateResponse.seg.length <= handler.config.segmentIndex) { + throw new ApiException("Segment " + handler.config.segmentIndex + + " is not currently setup correctly in the WLED firmware"); + } HSBType tempHSB = WLedHelper .parseToHSBType(state.stateResponse.seg[handler.config.segmentIndex].col[0].toString()); handler.update(CHANNEL_MASTER_CONTROLS, tempHSB); @@ -234,14 +278,13 @@ protected void processState() { .parseWhitePercent(state.stateResponse.seg[handler.config.segmentIndex].col[2].toString())); } - if (!state.stateResponse.on) { - // global ch needs adding - } else { - // global ch needs adding - } - if (!state.stateResponse.seg[handler.config.segmentIndex].on) { handler.update(CHANNEL_MASTER_CONTROLS, OnOffType.OFF); + handler.update(CHANNEL_SEGMENT_BRIGHTNESS, OnOffType.OFF); + } else { + handler.update(CHANNEL_SEGMENT_BRIGHTNESS, + new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].bri) + .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); } if (state.nightLightState.on) { handler.update(CHANNEL_SLEEP, OnOffType.ON); @@ -273,8 +316,8 @@ protected void processState() { } else { handler.update(CHANNEL_REVERSE, OnOffType.OFF); } - handler.update(CHANNEL_TRANS_TIME, new PercentType( - new BigDecimal(state.stateResponse.transition).divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + handler.update(CHANNEL_TRANS_TIME, new QuantityType<>( + new BigDecimal(state.stateResponse.transition).divide(BigDecimal.TEN), Units.SECOND)); handler.update(CHANNEL_PRESETS, new StringType("" + state.stateResponse.ps)); handler.update(CHANNEL_FX, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].fx)); handler.update(CHANNEL_PALETTES, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].pal)); @@ -291,7 +334,7 @@ protected void processState() { */ @Override public void setGlobalOn(boolean bool) throws ApiException { - postState("{\"on\":" + bool + "}"); + setState(postState("{\"on\":" + bool + ",\"v\":true,\"tt\":2}")); } /** @@ -299,7 +342,8 @@ public void setGlobalOn(boolean bool) throws ApiException { */ @Override public void setMasterOn(boolean bool) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":" + bool + "}]}"); + setState(postState( + "{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":" + bool + "}]}")); } /** @@ -308,10 +352,11 @@ public void setMasterOn(boolean bool) throws ApiException { @Override public void setGlobalBrightness(PercentType percent) throws ApiException { if (percent.equals(PercentType.ZERO)) { - postState("{\"on\":false}"); + setState(postState("{\"on\":false,\"v\":true}")); return; } - postState("{\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55) + "}"); + setState(postState("{\"on\":true,\"v\":true,\"tt\":2,\"bri\":" + + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}")); } /** @@ -320,26 +365,26 @@ public void setGlobalBrightness(PercentType percent) throws ApiException { @Override public void setMasterBrightness(PercentType percent) throws ApiException { if (percent.equals(PercentType.ZERO)) { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":false}]}"); + setState(postState("{\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":false}]}")); return; } - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":true,\"bri\":" - + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55) + "}]}"); + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + + ",\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}")); } @Override public void setMasterHSB(HSBType hsbType) throws ApiException { if (hsbType.getBrightness().toBigDecimal().equals(BigDecimal.ZERO)) { - postState("{\"seg\":[{\"on\":false,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[" - + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," - + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," - + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + handler.config.segmentIndex + + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); return; } - postState("{\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[" - + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," - + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); + + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); } @Override @@ -349,7 +394,8 @@ public void setEffect(String string) throws ApiException { @Override public void setPreset(String string) throws ApiException { - postState("{\"ps\":" + string + "}"); + setState( + postState("{\"ps\":" + string + ",\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + "}]}")); } @Override @@ -385,8 +431,8 @@ public void setUdpRecieve(boolean bool) throws ApiException { } @Override - public void setTransitionTime(int milliseconds) throws ApiException { - postState("{\"transition\":" + milliseconds + "}"); + public void setTransitionTime(BigDecimal time) throws ApiException { + postState("{\"transition\":" + time + "}"); } @Override diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index 57013e057dc0..bf47b4ce6fe9 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -10,6 +10,7 @@ ColorLight + @@ -66,6 +67,13 @@ + + Dimmer + + Changes the brightness of the whole segment + DimmableLight + + Color @@ -159,7 +167,23 @@ Time it takes to change/fade from one look to the next. Time - + + + + @@ -174,18 +198,18 @@ Change the intensity of the FX - - Switch - - Mirror the effect for this segment - - - - Switch - - Reverse the direction of the current segment - - + + Switch + + Mirror the effect for this segment + + + + Switch + + Reverse the direction of the current segment + + Switch @@ -193,7 +217,7 @@ Time - + Switch Cycle through the saved presets From 5692ac349de67f94f902a530814400e95cd51091 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Thu, 4 Nov 2021 22:40:55 +1100 Subject: [PATCH 07/15] Simplify return Signed-off-by: Matthew Skinner --- .../openhab/binding/wled/internal/WLedHandlerFactory.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java index a9619c334f1a..9c41546fd01f 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandlerFactory.java @@ -47,10 +47,7 @@ public WLedHandlerFactory(@Reference WledApiFactory apiFactory, @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) { - return true; - } - return false; + return SUPPORTED_THING_TYPES.contains(thingTypeUID); } @Override From 5a5a28686b87fbcd1ea49082b53b0805db799b9c Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Sat, 6 Nov 2021 12:30:55 +1100 Subject: [PATCH 08/15] Add support for named presets Signed-off-by: Matthew Skinner --- .../binding/wled/internal/WledState.java | 6 ++ .../wled/internal/api/WledApiFactory.java | 4 +- .../wled/internal/api/WledApiV0110.java | 71 +++++++++++++++++++ .../wled/internal/api/WledApiV0130.java | 2 +- 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java index 49e39f29cc8a..a472b2a63992 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java @@ -35,6 +35,7 @@ public class WledState { public NightLightState nightLightState = new NightLightState(); public UdpnState udpnState = new UdpnState(); public SegmentState segmentState = new SegmentState(); + public PresetState[] presetState = new PresetState[1]; public class JsonResponse { public List effects = new ArrayList<>(); @@ -109,4 +110,9 @@ public class InfoResponse { public class LedInfo { public boolean rgbw = false; } + + public class PresetState { + public String n = "";// Name of preset + public int bri = 0;// brightness in 255 + } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java index 70b2fe22418d..5823fe67f7b1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java @@ -46,7 +46,9 @@ public WledApi getApi(WLedHandler wLedHandler, WLedConfiguration config) throws logger.debug("Treating firmware as int:{}", version); if (version >= 130) { return new WledApiV0130(wLedHandler, config, httpClient); - } else if (version > 100) { + } else if (version >= 110) { + return new WledApiV0110(wLedHandler, config, httpClient); + } else if (version >= 100) { return new WledApiV084(wLedHandler, config, httpClient); } logger.warn("Your WLED firmware is very old, upgrade to at least 0.10.0"); diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java new file mode 100644 index 000000000000..be1e777eed4b --- /dev/null +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.wled.internal.api; + +import static org.openhab.binding.wled.internal.WLedBindingConstants.CHANNEL_PRESETS; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.wled.internal.WLedConfiguration; +import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.binding.wled.internal.WledState.PresetState; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateOption; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link WledApiV0130} is the json Api methods for firmware version 0.11.0 and newer + * as newer firmwares come out with breaking changes, extend this class into a newer firmware version class. + * + * @author Matthew Skinner - Initial contribution + */ +@NonNullByDefault +public class WledApiV0110 extends WledApiV084 { + + public WledApiV0110(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { + super(handler, config, httpClient); + } + + @Override + public void initialize() throws ApiException { + super.initialize(); + getPresets(); + } + + protected void getPresets() throws JsonSyntaxException, ApiException { + List presetsOptions = new ArrayList<>(); + JsonObject obj = gson.fromJson(sendGetRequest("/presets.json"), JsonObject.class); + if (obj == null) { + return; + } + Set> set = obj.entrySet(); + int counter = 0; + for (Entry presetEntry : set) { + logger.trace("Preset:{} json:{}", presetEntry.getKey(), presetEntry.getValue()); + PresetState preset = gson.fromJson(presetEntry.getValue(), PresetState.class); + if (preset != null) { + presetsOptions.add(new StateOption(Integer.toString(counter++), preset.n)); + } + } + handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PRESETS), + presetsOptions); + } +} diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java index 69934a01927e..508990de9872 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java @@ -29,7 +29,7 @@ * @author Matthew Skinner - Initial contribution */ @NonNullByDefault -public class WledApiV0130 extends WledApiV084 { +public class WledApiV0130 extends WledApiV0110 { public WledApiV0130(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { super(handler, config, httpClient); From ac5269aac6ccd50dc64256890a3ad35099683eda Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Sat, 6 Nov 2021 13:11:16 +1100 Subject: [PATCH 09/15] Dont add empty preset 0 to list Signed-off-by: Matthew Skinner --- .../org/openhab/binding/wled/internal/api/WledApiV0110.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java index be1e777eed4b..ee5f88e8920a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -61,9 +61,10 @@ protected void getPresets() throws JsonSyntaxException, ApiException { for (Entry presetEntry : set) { logger.trace("Preset:{} json:{}", presetEntry.getKey(), presetEntry.getValue()); PresetState preset = gson.fromJson(presetEntry.getValue(), PresetState.class); - if (preset != null) { - presetsOptions.add(new StateOption(Integer.toString(counter++), preset.n)); + if (preset != null && counter > 0) { + presetsOptions.add(new StateOption(Integer.toString(counter), preset.n)); } + counter++; } handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PRESETS), presetsOptions); From 1f5500a5e189ba1564af4b5ef8bdeb9614b31503 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Sat, 6 Nov 2021 16:40:04 +1100 Subject: [PATCH 10/15] Add preset saving with custom names Signed-off-by: Matthew Skinner --- bundles/org.openhab.binding.wled/README.md | 5 ++-- .../binding/wled/internal/WLedActions.java | 23 +++++++++++++++---- .../binding/wled/internal/WLedHandler.java | 9 ++------ .../binding/wled/internal/api/WledApi.java | 2 ++ .../wled/internal/api/WledApiV0110.java | 9 ++++++++ .../wled/internal/api/WledApiV084.java | 14 +++++++++++ 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/bundles/org.openhab.binding.wled/README.md b/bundles/org.openhab.binding.wled/README.md index c3450b5462fb..1a9307647a23 100644 --- a/bundles/org.openhab.binding.wled/README.md +++ b/bundles/org.openhab.binding.wled/README.md @@ -55,13 +55,12 @@ For additional segments, you can add them manually and set the `segmentIndex` co ## Rule Actions -This binding has a rule Action `savePreset(int presetNumber)` which can save the current state of the WLED string into a preset slot that you can specify. -Currently 1 to 16 are valid preset slots. +This binding has two rule Actions `savePreset(int presetNumber)` and `savePreset(int presetNumber, String presetName)` which can save the current state of the WLED string into a preset slot that you can specify. In Xtend rules, you can use the Actions like this. ``` -getActions("wled", "wled:wled:XmasTree").savePreset(5) +getActions("wled", "wled:wled:XmasTree").savePreset(5,"Flashy Preset") ``` ## Sitemap Example diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java index f1e302ca1e57..a268e1612bb0 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java @@ -47,15 +47,30 @@ public void setThingHandler(@Nullable ThingHandler handler) { @RuleAction(label = "save state to preset", description = "Save a WLED state to a preset slot") public void savePreset( @ActionInput(name = "presetNumber", label = "Preset Slot", description = "Number for the preset slot you wish to use") int presetNumber) { + savePreset(presetNumber, "Preset " + presetNumber); + } + + public static void savePreset(@Nullable ThingActions actions, int presetNumber) { + if (actions instanceof WLedActions) { + ((WLedActions) actions).savePreset(presetNumber, "Preset " + presetNumber); + } else { + throw new IllegalArgumentException("Instance is not a WLED class."); + } + } + + @RuleAction(label = "save state to preset", description = "Save a WLED state to a preset slot") + public void savePreset( + @ActionInput(name = "presetNumber", label = "Preset Slot", description = "Number for the preset slot you wish to use") int presetNumber, + @ActionInput(name = "presetName", label = "Preset Name", description = "Name for the preset that you wish to use") String presetName) { WLedHandler localHandler = handler; - if (presetNumber > 0 && localHandler != null) { - localHandler.savePreset(presetNumber); + if (localHandler != null) { + localHandler.savePreset(presetNumber, presetName); } } - public static void savePreset(@Nullable ThingActions actions, int presetNumber) { + public static void savePreset(@Nullable ThingActions actions, int presetNumber, String presetName) { if (actions instanceof WLedActions) { - ((WLedActions) actions).savePreset(presetNumber); + ((WLedActions) actions).savePreset(presetNumber, presetName); } else { throw new IllegalArgumentException("Instance is not a WLED class."); } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index 61a2e71bddb3..df3350b3b29d 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -231,17 +231,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } - public void savePreset(int presetIndex) { - if (presetIndex > 16) { - logger.warn("Presets above 16 do not exist, and the action sent {}", presetIndex); - return; - } + public void savePreset(int position, String presetName) { try { if (api != null) { - api.sendGetRequest("/win&PS=" + presetIndex); + api.savePreset(position, presetName); } } catch (ApiException e) { - logger.warn("Preset failed to save due to:{}", e.getMessage()); } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index 82200665d405..f702d2f5a372 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -74,4 +74,6 @@ public interface WledApi { public abstract void setMirror(boolean bool) throws ApiException; public abstract void setReverse(boolean bool) throws ApiException; + + public abstract void savePreset(int position, String presetName) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java index ee5f88e8920a..f39c412b29c1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -69,4 +69,13 @@ protected void getPresets() throws JsonSyntaxException, ApiException { handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PRESETS), presetsOptions); } + + @Override + public void savePreset(int position, String presetName) throws ApiException { + if (position < 1) { + logger.warn("Preset position {} is not supported in this firmware version", position); + return; + } + postState("{\"psave\":" + position + ",\"n\":\"" + presetName + "\",\"ib\":true,\"sb\":true}"); + } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index f98bdf807cd2..49bdd93ecfd1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -483,4 +483,18 @@ public void setMirror(boolean bool) throws ApiException { public void setReverse(boolean bool) throws ApiException { postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"rev\":" + bool + "}]}"); } + + @Override + public void savePreset(int position, String presetName) throws ApiException { + // named presets not supported in older firmwares, and max of 16. + if (position > 16 || position < 1) { + logger.warn("Preset position {} is not supported in this firmware version", position); + return; + } + try { + sendGetRequest("/win&PS=" + position); + } catch (ApiException e) { + logger.warn("Preset failed to save:{}", e.getMessage()); + } + } } From b9f8f57ae892fb23a96a8b6e8b025c1ceac1c897 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Mon, 8 Nov 2021 19:29:17 +1100 Subject: [PATCH 11/15] Tidy up Signed-off-by: Matthew Skinner --- .../binding/wled/internal/WLedActions.java | 4 +- .../binding/wled/internal/WLedHandler.java | 40 ++++----- .../binding/wled/internal/api/WledApi.java | 52 +++++++---- .../wled/internal/api/WledApiFactory.java | 13 ++- .../wled/internal/api/WledApiV0110.java | 12 ++- .../wled/internal/api/WledApiV0130.java | 5 +- .../wled/internal/api/WledApiV084.java | 87 ++++++++----------- 7 files changed, 110 insertions(+), 103 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java index a268e1612bb0..8c2c53712d1f 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedActions.java @@ -47,12 +47,12 @@ public void setThingHandler(@Nullable ThingHandler handler) { @RuleAction(label = "save state to preset", description = "Save a WLED state to a preset slot") public void savePreset( @ActionInput(name = "presetNumber", label = "Preset Slot", description = "Number for the preset slot you wish to use") int presetNumber) { - savePreset(presetNumber, "Preset " + presetNumber); + savePreset(presetNumber, ""); } public static void savePreset(@Nullable ThingActions actions, int presetNumber) { if (actions instanceof WLedActions) { - ((WLedActions) actions).savePreset(presetNumber, "Preset " + presetNumber); + ((WLedActions) actions).savePreset(presetNumber, ""); } else { throw new IllegalArgumentException("Instance is not a WLED class."); } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index df3350b3b29d..da5cd14f98b1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -90,20 +90,20 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_SEGMENT_BRIGHTNESS: if (command instanceof OnOffType) { - localApi.setMasterOn(OnOffType.ON.equals(command)); + localApi.setMasterOn(OnOffType.ON.equals(command), config.segmentIndex); } else if (command instanceof PercentType) { if (PercentType.ZERO.equals(command)) { - localApi.setMasterOn(false); + localApi.setMasterOn(false, config.segmentIndex); return; } - localApi.setMasterBrightness((PercentType) command); + localApi.setMasterBrightness((PercentType) command, config.segmentIndex); } break; case CHANNEL_MIRROR: - localApi.setMirror(OnOffType.ON.equals(command)); + localApi.setMirror(OnOffType.ON.equals(command), config.segmentIndex); break; case CHANNEL_REVERSE: - localApi.setReverse(OnOffType.ON.equals(command)); + localApi.setReverse(OnOffType.ON.equals(command), config.segmentIndex); break; case CHANNEL_SYNC_SEND: localApi.setUdpSend(OnOffType.ON.equals(command)); @@ -125,7 +125,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_MASTER_CONTROLS: if (command instanceof OnOffType) { - localApi.setMasterOn(OnOffType.ON.equals(command)); + localApi.setMasterOn(OnOffType.ON.equals(command), config.segmentIndex); } else if (command instanceof IncreaseDecreaseType) { if (IncreaseDecreaseType.INCREASE.equals(command)) { if (masterBrightness255.intValue() < 240) { @@ -142,20 +142,20 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } else if (command instanceof HSBType) { if ((((HSBType) command).getBrightness()).equals(PercentType.ZERO)) { - localApi.setMasterOn(false); + localApi.setMasterOn(false, config.segmentIndex); return; } primaryColor = (HSBType) command; if (primaryColor.getSaturation().intValue() < config.saturationThreshold && hasWhite) { - localApi.setWhiteOnly((PercentType) command); + localApi.setWhiteOnly((PercentType) command, config.segmentIndex); } else if (primaryColor.getSaturation().intValue() == 32 && primaryColor.getHue().intValue() == 36 && hasWhite) { - localApi.setWhiteOnly((PercentType) command); + localApi.setWhiteOnly((PercentType) command, config.segmentIndex); } else { - localApi.setMasterHSB((HSBType) command); + localApi.setMasterHSB((HSBType) command, config.segmentIndex); } } else if (command instanceof PercentType) { - localApi.setMasterBrightness((PercentType) command); + localApi.setMasterBrightness((PercentType) command, config.segmentIndex); } return; case CHANNEL_PRIMARY_COLOR: @@ -165,7 +165,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { primaryColor = new HSBType(primaryColor.getHue(), primaryColor.getSaturation(), ((PercentType) command)); } - localApi.setPrimaryColor(primaryColor); + localApi.setPrimaryColor(primaryColor, config.segmentIndex); return; case CHANNEL_SECONDARY_COLOR: if (command instanceof HSBType) { @@ -174,7 +174,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { secondaryColor = new HSBType(secondaryColor.getHue(), secondaryColor.getSaturation(), ((PercentType) command)); } - localApi.setSecondaryColor(secondaryColor); + localApi.setSecondaryColor(secondaryColor, config.segmentIndex); return; case CHANNEL_THIRD_COLOR: if (command instanceof HSBType) { @@ -183,19 +183,19 @@ public void handleCommand(ChannelUID channelUID, Command command) { thirdColor = new HSBType(thirdColor.getHue(), thirdColor.getSaturation(), ((PercentType) command)); } - localApi.setTertiaryColor(thirdColor); + localApi.setTertiaryColor(thirdColor, config.segmentIndex); return; case CHANNEL_PALETTES: - localApi.setPalette(command.toString()); + localApi.setPalette(command.toString(), config.segmentIndex); break; case CHANNEL_FX: - localApi.setEffect(command.toString()); + localApi.setEffect(command.toString(), config.segmentIndex); break; case CHANNEL_SPEED: - localApi.setFxSpeed((PercentType) command); + localApi.setFxSpeed((PercentType) command, config.segmentIndex); break; case CHANNEL_INTENSITY: - localApi.setFxIntencity((PercentType) command); + localApi.setFxIntencity((PercentType) command, config.segmentIndex); break; case CHANNEL_SLEEP: localApi.setSleep(OnOffType.ON.equals(command)); @@ -256,7 +256,7 @@ private void pollState() { WledApi localApi = api; try { if (localApi == null) { - api = apiFactory.getApi(this, config); + api = apiFactory.getApi(this); api.initialize(); } if (localApi == null) { @@ -265,7 +265,7 @@ private void pollState() { localApi.update(); updateStatus(ThingStatus.ONLINE); } catch (ApiException e) { - api = null;// recheck the firmware was not just updated + api = null;// Firmware may be updated so need to check next connect updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index f702d2f5a372..1e1d6b2d62c2 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -19,7 +19,7 @@ import org.openhab.core.library.types.PercentType; /** - * The {@link WledApi} is the json Api methods for different firmware versions + * The {@link WledApi} is the JSON API methods that can be extended for different firmware versions. * * @author Matthew Skinner - Initial contribution */ @@ -31,25 +31,42 @@ public interface WledApi { public abstract int getFirmwareVersion() throws ApiException; + public abstract String sendGetRequest(String string) throws ApiException; + + /** + * Turns on/off ALL segments + */ public abstract void setGlobalOn(boolean bool) throws ApiException; - public abstract void setMasterOn(boolean bool) throws ApiException; + /** + * Turns on/off just THIS segment + */ + public abstract void setMasterOn(boolean bool, int segmentIndex) throws ApiException; + /** + * Sets the brightness of ALL segments + */ public abstract void setGlobalBrightness(PercentType percent) throws ApiException; - public abstract void setMasterBrightness(PercentType percent) throws ApiException; + /** + * Sets the brightness of just THIS segment + */ + public abstract void setMasterBrightness(PercentType percent, int segmentIndex) throws ApiException; - public abstract void setMasterHSB(HSBType hsbType) throws ApiException; + /** + * Stops any running FX and instantly changes the segment to the desired colour + */ + public abstract void setMasterHSB(HSBType hsbType, int segmentIndex) throws ApiException; - public abstract void setEffect(String string) throws ApiException; + public abstract void setEffect(String string, int segmentIndex) throws ApiException; public abstract void setPreset(String string) throws ApiException; - public abstract void setPalette(String string) throws ApiException; + public abstract void setPalette(String string, int segmentIndex) throws ApiException; - public abstract void setFxIntencity(PercentType percentType) throws ApiException; + public abstract void setFxIntencity(PercentType percentType, int segmentIndex) throws ApiException; - public abstract void setFxSpeed(PercentType percentType) throws ApiException; + public abstract void setFxSpeed(PercentType percentType, int segmentIndex) throws ApiException; public abstract void setSleep(boolean bool) throws ApiException; @@ -61,19 +78,22 @@ public interface WledApi { public abstract void setPresetCycle(boolean bool) throws ApiException; - public abstract void setPrimaryColor(HSBType hsbType) throws ApiException; + public abstract void setPrimaryColor(HSBType hsbType, int segmentIndex) throws ApiException; - public abstract void setSecondaryColor(HSBType hsbType) throws ApiException; + public abstract void setSecondaryColor(HSBType hsbType, int segmentIndex) throws ApiException; - public abstract void setTertiaryColor(HSBType hsbType) throws ApiException; - - public abstract String sendGetRequest(String string) throws ApiException; + public abstract void setTertiaryColor(HSBType hsbType, int segmentIndex) throws ApiException; - public abstract void setWhiteOnly(PercentType percentType) throws ApiException; + public abstract void setWhiteOnly(PercentType percentType, int segmentIndex) throws ApiException; - public abstract void setMirror(boolean bool) throws ApiException; + public abstract void setMirror(boolean bool, int segmentIndex) throws ApiException; - public abstract void setReverse(boolean bool) throws ApiException; + public abstract void setReverse(boolean bool, int segmentIndex) throws ApiException; + /** + * Saves a preset to the position number with the supplied name. If the supplied name is an empty String then the + * name 'Preset x' will be used by default using the position number given. + * + */ public abstract void savePreset(int position, String presetName) throws ApiException; } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java index 5823fe67f7b1..f71cac034443 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiFactory.java @@ -14,7 +14,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; -import org.openhab.binding.wled.internal.WLedConfiguration; import org.openhab.binding.wled.internal.WLedHandler; import org.openhab.core.io.net.http.HttpClientFactory; import org.osgi.service.component.annotations.Activate; @@ -24,7 +23,7 @@ import org.slf4j.LoggerFactory; /** - * The {@link WledApiFactory} is responsible for creating an instance of the API that will work with different + * The {@link WledApiFactory} is responsible for creating an instance of the API that is optimized for different * firmware versions. * * @author Matthew Skinner - Initial contribution @@ -40,16 +39,16 @@ public WledApiFactory(@Reference HttpClientFactory httpClientFactory) { this.httpClient = httpClientFactory.getCommonHttpClient(); } - public WledApi getApi(WLedHandler wLedHandler, WLedConfiguration config) throws ApiException { - WledApi lowestSupportedApi = new WledApiV084(wLedHandler, config, httpClient); + public WledApi getApi(WLedHandler handler) throws ApiException { + WledApi lowestSupportedApi = new WledApiV084(handler, httpClient); int version = lowestSupportedApi.getFirmwareVersion(); logger.debug("Treating firmware as int:{}", version); if (version >= 130) { - return new WledApiV0130(wLedHandler, config, httpClient); + return new WledApiV0130(handler, httpClient); } else if (version >= 110) { - return new WledApiV0110(wLedHandler, config, httpClient); + return new WledApiV0110(handler, httpClient); } else if (version >= 100) { - return new WledApiV084(wLedHandler, config, httpClient); + return new WledApiV084(handler, httpClient); } logger.warn("Your WLED firmware is very old, upgrade to at least 0.10.0"); return lowestSupportedApi; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java index f39c412b29c1..a18f33620c96 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -21,7 +21,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; -import org.openhab.binding.wled.internal.WLedConfiguration; import org.openhab.binding.wled.internal.WLedHandler; import org.openhab.binding.wled.internal.WledState.PresetState; import org.openhab.core.thing.ChannelUID; @@ -40,8 +39,8 @@ @NonNullByDefault public class WledApiV0110 extends WledApiV084 { - public WledApiV0110(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { - super(handler, config, httpClient); + public WledApiV0110(WLedHandler handler, HttpClient httpClient) { + super(handler, httpClient); } @Override @@ -76,6 +75,11 @@ public void savePreset(int position, String presetName) throws ApiException { logger.warn("Preset position {} is not supported in this firmware version", position); return; } - postState("{\"psave\":" + position + ",\"n\":\"" + presetName + "\",\"ib\":true,\"sb\":true}"); + + String name = presetName; + if (name.isEmpty()) { + name = "Preset " + position; + } + postState("{\"psave\":" + position + ",\"n\":\"" + name + "\",\"ib\":true,\"sb\":true}"); } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java index 508990de9872..d6b09d8fc3f0 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; -import org.openhab.binding.wled.internal.WLedConfiguration; import org.openhab.binding.wled.internal.WLedHandler; import org.openhab.core.thing.Channel; @@ -31,8 +30,8 @@ @NonNullByDefault public class WledApiV0130 extends WledApiV0110 { - public WledApiV0130(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { - super(handler, config, httpClient); + public WledApiV0130(WLedHandler handler, HttpClient httpClient) { + super(handler, httpClient); } @Override diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index 49bdd93ecfd1..04823b5e7ea7 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -30,7 +30,6 @@ import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.wled.internal.WLedConfiguration; import org.openhab.binding.wled.internal.WLedHandler; import org.openhab.binding.wled.internal.WLedHelper; import org.openhab.binding.wled.internal.WledState; @@ -64,14 +63,14 @@ public class WledApiV084 implements WledApi { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); protected final Gson gson = new Gson(); protected final HttpClient httpClient; - protected final WLedConfiguration config; protected final WLedHandler handler; + protected final String address; protected WledState state = new WledState(); private int version = 0; - public WledApiV084(WLedHandler handler, WLedConfiguration config, HttpClient httpClient) { + public WledApiV084(WLedHandler handler, HttpClient httpClient) { this.handler = handler; - this.config = config; + this.address = handler.config.address; this.httpClient = httpClient; } @@ -109,7 +108,7 @@ public void initialize() throws ApiException { @Override public String sendGetRequest(String url) throws ApiException { - Request request = httpClient.newRequest(config.address + url); + Request request = httpClient.newRequest(address + url); request.timeout(3, TimeUnit.SECONDS); request.method(HttpMethod.GET); request.header(HttpHeader.ACCEPT_ENCODING, "gzip"); @@ -140,7 +139,7 @@ protected String postState(String json) throws ApiException { protected String sendPostRequest(String url, String json) throws ApiException { logger.debug("Sending WLED POST:{} Message:{}", url, json); - Request request = httpClient.POST(config.address + url); + Request request = httpClient.POST(address + url); request.timeout(3, TimeUnit.SECONDS); request.header(HttpHeader.CONTENT_TYPE, "application/json"); request.content(new StringContentProvider(json), "application/json"); @@ -329,26 +328,16 @@ protected void processState() throws ApiException { .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); } - /** - * Turns on/off ALL segments at the SAME TIME - */ @Override public void setGlobalOn(boolean bool) throws ApiException { setState(postState("{\"on\":" + bool + ",\"v\":true,\"tt\":2}")); } - /** - * Turns on/off just THIS segment - */ @Override - public void setMasterOn(boolean bool) throws ApiException { - setState(postState( - "{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":" + bool + "}]}")); + public void setMasterOn(boolean bool, int segmentIndex) throws ApiException { + setState(postState("{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":" + bool + "}]}")); } - /** - * Sets the brightness of ALL segments at the SAME TIME - */ @Override public void setGlobalBrightness(PercentType percent) throws ApiException { if (percent.equals(PercentType.ZERO)) { @@ -359,59 +348,55 @@ public void setGlobalBrightness(PercentType percent) throws ApiException { + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}")); } - /** - * Sets the brightness of just THIS segment - */ @Override - public void setMasterBrightness(PercentType percent) throws ApiException { + public void setMasterBrightness(PercentType percent, int segmentIndex) throws ApiException { if (percent.equals(PercentType.ZERO)) { - setState(postState("{\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"on\":false}]}")); + setState(postState("{\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":false}]}")); return; } - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex - + ",\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}")); + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":true,\"bri\":" + + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}")); } @Override - public void setMasterHSB(HSBType hsbType) throws ApiException { + public void setMasterHSB(HSBType hsbType, int segmentIndex) throws ApiException { if (hsbType.getBrightness().toBigDecimal().equals(BigDecimal.ZERO)) { - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + handler.config.segmentIndex + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); return; } - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex - + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[" + + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); } @Override - public void setEffect(String string) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"fx\":" + string + "}]}"); + public void setEffect(String string, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"fx\":" + string + "}]}"); } @Override public void setPreset(String string) throws ApiException { - setState( - postState("{\"ps\":" + string + ",\"v\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + "}]}")); + setState(postState("{\"ps\":" + string + "}")); } @Override - public void setPalette(String string) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"pal\":" + string + "}]}"); + public void setPalette(String string, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"pal\":" + string + "}]}"); } @Override - public void setFxIntencity(PercentType percentType) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"ix\":" + public void setFxIntencity(PercentType percentType, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"ix\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); } @Override - public void setFxSpeed(PercentType percentType) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"sx\":" + public void setFxSpeed(PercentType percentType, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"sx\":" + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}"); } @@ -445,43 +430,43 @@ public void setPresetCycle(boolean bool) throws ApiException { } @Override - public void setPrimaryColor(HSBType hsbType) throws ApiException { - postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[" + public void setPrimaryColor(HSBType hsbType, int segmentIndex) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[],[]]}]}"); } @Override - public void setSecondaryColor(HSBType hsbType) throws ApiException { - postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[],[" + public void setSecondaryColor(HSBType hsbType, int segmentIndex) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[[],[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "],[]]}]}"); } @Override - public void setTertiaryColor(HSBType hsbType) throws ApiException { - postState("{\"on\":true,\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"col\":[[],[],[" + public void setTertiaryColor(HSBType hsbType, int segmentIndex) throws ApiException { + postState("{\"on\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"col\":[[],[],[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); } @Override - public void setWhiteOnly(PercentType percentType) throws ApiException { - postState("{\"seg\":[{\"on\":true,\"id\":" + handler.config.segmentIndex + ",\"fx\":0,\"col\":[[0,0,0," + public void setWhiteOnly(PercentType percentType, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"on\":true,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[0,0,0," + percentType.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}"); } @Override - public void setMirror(boolean bool) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"mi\":" + bool + "}]}"); + public void setMirror(boolean bool, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"mi\":" + bool + "}]}"); } @Override - public void setReverse(boolean bool) throws ApiException { - postState("{\"seg\":[{\"id\":" + handler.config.segmentIndex + ",\"rev\":" + bool + "}]}"); + public void setReverse(boolean bool, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"rev\":" + bool + "}]}"); } @Override From 2f9d37b588951c152ed4c888f4078ff7d31b1122 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Mon, 8 Nov 2021 21:22:10 +1100 Subject: [PATCH 12/15] Rename function for clarity Signed-off-by: Matthew Skinner --- .../wled/internal/api/WledApiV084.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index 04823b5e7ea7..d02c167f8793 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -162,7 +162,7 @@ protected String sendPostRequest(String url, String json) throws ApiException { throw new ApiException(errorReason); } - protected void setState(String jsonState) { + protected void updateStateFromReply(String jsonState) { try { StateResponse response = gson.fromJson(jsonState, StateResponse.class); if (response == null) { @@ -330,45 +330,46 @@ protected void processState() throws ApiException { @Override public void setGlobalOn(boolean bool) throws ApiException { - setState(postState("{\"on\":" + bool + ",\"v\":true,\"tt\":2}")); + updateStateFromReply(postState("{\"on\":" + bool + ",\"v\":true,\"tt\":2}")); } @Override public void setMasterOn(boolean bool, int segmentIndex) throws ApiException { - setState(postState("{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":" + bool + "}]}")); + updateStateFromReply( + postState("{\"v\":true,\"tt\":2,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":" + bool + "}]}")); } @Override public void setGlobalBrightness(PercentType percent) throws ApiException { if (percent.equals(PercentType.ZERO)) { - setState(postState("{\"on\":false,\"v\":true}")); + updateStateFromReply(postState("{\"on\":false,\"v\":true}")); return; } - setState(postState("{\"on\":true,\"v\":true,\"tt\":2,\"bri\":" + updateStateFromReply(postState("{\"on\":true,\"v\":true,\"tt\":2,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}")); } @Override public void setMasterBrightness(PercentType percent, int segmentIndex) throws ApiException { if (percent.equals(PercentType.ZERO)) { - setState(postState("{\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":false}]}")); + updateStateFromReply(postState("{\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":false}]}")); return; } - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":true,\"bri\":" + updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"id\":" + segmentIndex + ",\"on\":true,\"bri\":" + percent.toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "}]}")); } @Override public void setMasterHSB(HSBType hsbType, int segmentIndex) throws ApiException { if (hsbType.getBrightness().toBigDecimal().equals(BigDecimal.ZERO)) { - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + segmentIndex + updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":false,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); return; } - setState(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + segmentIndex + ",\"fx\":0,\"col\":[[" - + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + updateStateFromReply(postState("{\"tt\":2,\"v\":true,\"seg\":[{\"on\":true,\"id\":" + segmentIndex + + ",\"fx\":0,\"col\":[[" + hsbType.getRed().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getGreen().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "," + hsbType.getBlue().toBigDecimal().multiply(BIG_DECIMAL_2_55).intValue() + "]]}]}")); } @@ -380,7 +381,7 @@ public void setEffect(String string, int segmentIndex) throws ApiException { @Override public void setPreset(String string) throws ApiException { - setState(postState("{\"ps\":" + string + "}")); + updateStateFromReply(postState("{\"ps\":" + string + ",\"v\":true}")); } @Override From e1454cf2c15ec2f571bda0515f0acc948b44259a Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Tue, 9 Nov 2021 21:53:35 +1100 Subject: [PATCH 13/15] Add more channels Signed-off-by: Matthew Skinner --- .../wled/internal/WLedBindingConstants.java | 4 ++ .../binding/wled/internal/WLedHandler.java | 12 ++++++ .../binding/wled/internal/WledState.java | 2 +- .../binding/wled/internal/api/WledApi.java | 6 +++ .../wled/internal/api/WledApiV0110.java | 13 ++++++- .../wled/internal/api/WledApiV0130.java | 7 ++++ .../wled/internal/api/WledApiV084.java | 19 ++++++++++ .../resources/OH-INF/thing/thing-types.xml | 38 ++++++++++++++++++- 8 files changed, 97 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java index b31676323d79..62883bcb5098 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedBindingConstants.java @@ -52,6 +52,7 @@ public class WLedBindingConstants { public static final String CHANNEL_THIRD_WHITE = "tertiaryWhite"; public static final String CHANNEL_PALETTES = "palettes"; public static final String CHANNEL_PRESETS = "presets"; + public static final String CHANNEL_PLAYLISTS = "playlists"; public static final String CHANNEL_PRESET_DURATION = "presetDuration"; public static final String CHANNEL_TRANS_TIME = "transformTime"; public static final String CHANNEL_PRESET_CYCLE = "presetCycle"; @@ -60,6 +61,9 @@ public class WLedBindingConstants { public static final String CHANNEL_INTENSITY = "intensity"; public static final String CHANNEL_MIRROR = "mirror"; public static final String CHANNEL_REVERSE = "reverse"; + public static final String CHANNEL_GROUPING = "grouping"; + public static final String CHANNEL_SPACING = "spacing"; + public static final String CHANNEL_LIVE_OVERRIDE = "liveOverride"; public static final String CHANNEL_SLEEP = "sleep"; public static final String CHANNEL_SYNC_SEND = "syncSend"; public static final String CHANNEL_SYNC_RECEIVE = "syncReceive"; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index da5cd14f98b1..cdba0e382dc0 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -27,6 +27,7 @@ import org.openhab.binding.wled.internal.api.ApiException; import org.openhab.binding.wled.internal.api.WledApi; import org.openhab.binding.wled.internal.api.WledApiFactory; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; @@ -101,6 +102,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_MIRROR: localApi.setMirror(OnOffType.ON.equals(command), config.segmentIndex); + case CHANNEL_LIVE_OVERRIDE: + localApi.setLiveOverride(command.toString()); + case CHANNEL_SPACING: + if (command instanceof DecimalType) { + localApi.setSpacing(((DecimalType) command).intValue(), config.segmentIndex); + } + case CHANNEL_GROUPING: + if (command instanceof DecimalType) { + localApi.setGrouping(((DecimalType) command).intValue(), config.segmentIndex); + } break; case CHANNEL_REVERSE: localApi.setReverse(OnOffType.ON.equals(command), config.segmentIndex); @@ -200,6 +211,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_SLEEP: localApi.setSleep(OnOffType.ON.equals(command)); break; + case CHANNEL_PLAYLISTS: case CHANNEL_PRESETS: localApi.setPreset(command.toString()); break; diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java index a472b2a63992..dfec00167857 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WledState.java @@ -113,6 +113,6 @@ public class LedInfo { public class PresetState { public String n = "";// Name of preset - public int bri = 0;// brightness in 255 + public int bri = 0;// brightness in 255, 0 means it is a playlist as bri was not defined } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java index 1e1d6b2d62c2..658b6f8a74ae 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApi.java @@ -90,6 +90,12 @@ public interface WledApi { public abstract void setReverse(boolean bool, int segmentIndex) throws ApiException; + public abstract void setLiveOverride(String value) throws ApiException; + + public abstract void setGrouping(int value, int segmentIndex) throws ApiException; + + public abstract void setSpacing(int value, int segmentIndex) throws ApiException; + /** * Saves a preset to the position number with the supplied name. If the supplied name is an empty String then the * name 'Preset x' will be used by default using the position number given. diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java index a18f33620c96..d636f914f6e8 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.wled.internal.api; -import static org.openhab.binding.wled.internal.WLedBindingConstants.CHANNEL_PRESETS; +import static org.openhab.binding.wled.internal.WLedBindingConstants.*; import java.util.ArrayList; import java.util.List; @@ -51,22 +51,31 @@ public void initialize() throws ApiException { protected void getPresets() throws JsonSyntaxException, ApiException { List presetsOptions = new ArrayList<>(); + List playlistsOptions = new ArrayList<>(); JsonObject obj = gson.fromJson(sendGetRequest("/presets.json"), JsonObject.class); if (obj == null) { return; } Set> set = obj.entrySet(); + // presetsOptions.add(new StateOption("-1", "None")); + // playlistsOptions.add(new StateOption("-1", "None")); int counter = 0; for (Entry presetEntry : set) { logger.trace("Preset:{} json:{}", presetEntry.getKey(), presetEntry.getValue()); PresetState preset = gson.fromJson(presetEntry.getValue(), PresetState.class); if (preset != null && counter > 0) { - presetsOptions.add(new StateOption(Integer.toString(counter), preset.n)); + if (preset.bri == 0) { + playlistsOptions.add(new StateOption(Integer.toString(counter), preset.n)); + } else { + presetsOptions.add(new StateOption(Integer.toString(counter), preset.n)); + } } counter++; } handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PRESETS), presetsOptions); + handler.stateDescriptionProvider.setStateOptions(new ChannelUID(handler.getThing().getUID(), CHANNEL_PLAYLISTS), + playlistsOptions); } @Override diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java index d6b09d8fc3f0..41bf6dd7378a 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java @@ -19,6 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.wled.internal.WLedHandler; +import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Channel; /** @@ -49,4 +50,10 @@ public void initialize() throws ApiException { } handler.removeChannels(removeChannels); } + + @Override + protected void processState() throws ApiException { + super.processState(); + handler.update(CHANNEL_PLAYLISTS, new StringType("" + state.stateResponse.pl)); + } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index d02c167f8793..a4b660e89352 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -37,6 +37,7 @@ import org.openhab.binding.wled.internal.WledState.JsonResponse; import org.openhab.binding.wled.internal.WledState.LedInfo; import org.openhab.binding.wled.internal.WledState.StateResponse; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -326,6 +327,9 @@ protected void processState() throws ApiException { handler.update(CHANNEL_INTENSITY, new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].ix) .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); + handler.update(CHANNEL_LIVE_OVERRIDE, new StringType("" + state.stateResponse.lor)); + handler.update(CHANNEL_GROUPING, new DecimalType(state.stateResponse.seg[handler.config.segmentIndex].grp)); + handler.update(CHANNEL_SPACING, new DecimalType(state.stateResponse.seg[handler.config.segmentIndex].spc)); } @Override @@ -483,4 +487,19 @@ public void savePreset(int position, String presetName) throws ApiException { logger.warn("Preset failed to save:{}", e.getMessage()); } } + + @Override + public void setLiveOverride(String value) throws ApiException { + postState("{\"lor\":" + value + "}"); + } + + @Override + public void setGrouping(int value, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"grp\":" + value + "}]}"); + } + + @Override + public void setSpacing(int value, int segmentIndex) throws ApiException { + postState("{\"seg\":[{\"id\":" + segmentIndex + ",\"spc\":" + value + "}]}"); + } } diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index bf47b4ce6fe9..4c7821b41ff9 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -18,6 +18,7 @@ + @@ -26,7 +27,9 @@ - + + + @@ -154,6 +157,12 @@ + + String + + The currently playing play list + + Number:Time @@ -162,6 +171,33 @@ + + Number + + How many consecutive LEDs of the same segment will be grouped to the same color + + + + + Number + + How many LEDs are turned off and skipped between each group + + + + + String + + Live data override. 0 is off, 1 is override until live data ends, 2 is override until ESP reboot + + + + + + + + + Number:Time From eaf111098ecf1276b1efa8204f344b364e165406 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Tue, 9 Nov 2021 22:21:31 +1100 Subject: [PATCH 14/15] Clean up Signed-off-by: Matthew Skinner --- .../binding/wled/internal/api/WledApiV0110.java | 2 -- .../binding/wled/internal/api/WledApiV0130.java | 2 +- .../openhab/binding/wled/internal/api/WledApiV084.java | 10 ++++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java index d636f914f6e8..8c6444305c57 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0110.java @@ -57,8 +57,6 @@ protected void getPresets() throws JsonSyntaxException, ApiException { return; } Set> set = obj.entrySet(); - // presetsOptions.add(new StateOption("-1", "None")); - // playlistsOptions.add(new StateOption("-1", "None")); int counter = 0; for (Entry presetEntry : set) { logger.trace("Preset:{} json:{}", presetEntry.getKey(), presetEntry.getValue()); diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java index 41bf6dd7378a..e69bbc823365 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV0130.java @@ -54,6 +54,6 @@ public void initialize() throws ApiException { @Override protected void processState() throws ApiException { super.processState(); - handler.update(CHANNEL_PLAYLISTS, new StringType("" + state.stateResponse.pl)); + handler.update(CHANNEL_PLAYLISTS, new StringType(Integer.toString(state.stateResponse.pl))); } } diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index a4b660e89352..b507cfaf96e1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -318,16 +318,18 @@ protected void processState() throws ApiException { } handler.update(CHANNEL_TRANS_TIME, new QuantityType<>( new BigDecimal(state.stateResponse.transition).divide(BigDecimal.TEN), Units.SECOND)); - handler.update(CHANNEL_PRESETS, new StringType("" + state.stateResponse.ps)); - handler.update(CHANNEL_FX, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].fx)); - handler.update(CHANNEL_PALETTES, new StringType("" + state.stateResponse.seg[handler.config.segmentIndex].pal)); + handler.update(CHANNEL_PRESETS, new StringType(Integer.toString(state.stateResponse.ps))); + handler.update(CHANNEL_FX, + new StringType(Integer.toString(state.stateResponse.seg[handler.config.segmentIndex].fx))); + handler.update(CHANNEL_PALETTES, + new StringType(Integer.toString(state.stateResponse.seg[handler.config.segmentIndex].pal))); handler.update(CHANNEL_SPEED, new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].sx) .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); handler.update(CHANNEL_INTENSITY, new PercentType(new BigDecimal(state.stateResponse.seg[handler.config.segmentIndex].ix) .divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP))); - handler.update(CHANNEL_LIVE_OVERRIDE, new StringType("" + state.stateResponse.lor)); + handler.update(CHANNEL_LIVE_OVERRIDE, new StringType(Integer.toString(state.stateResponse.lor))); handler.update(CHANNEL_GROUPING, new DecimalType(state.stateResponse.seg[handler.config.segmentIndex].grp)); handler.update(CHANNEL_SPACING, new DecimalType(state.stateResponse.seg[handler.config.segmentIndex].spc)); } From c10409f8b42c5473e9b52bb540e7f1a33b78a765 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Wed, 10 Nov 2021 18:14:37 +1100 Subject: [PATCH 15/15] Fix bugs and update readme for new channels Signed-off-by: Matthew Skinner --- bundles/org.openhab.binding.wled/README.md | 6 +++++- .../java/org/openhab/binding/wled/internal/WLedHandler.java | 3 +++ .../org/openhab/binding/wled/internal/api/WledApiV084.java | 1 + .../src/main/resources/OH-INF/thing/thing-types.xml | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.wled/README.md b/bundles/org.openhab.binding.wled/README.md index 1a9307647a23..46e5451e98c3 100644 --- a/bundles/org.openhab.binding.wled/README.md +++ b/bundles/org.openhab.binding.wled/README.md @@ -43,7 +43,8 @@ For additional segments, you can add them manually and set the `segmentIndex` co | `fx` | String | A list of Effects you can select from. | | `speed` | Dimmer | Changes the speed of the loaded effect. | | `intensity` | Dimmer | Changes the intensity of the loaded effect. | -| `presets` | String | A list of presets that you can select from. | +| `presets` | String | A list of presets that you can select from and will display -1 when no presets are running. | +| `playlists` | String | A list of playlists that you can select from and will display -1 when none are running. | | `presetCycle` | Switch | Turns ON/OFF the automatic changing from one preset to the next. Only in V0.12.0 and older firmwares. | | `presetDuration` | Number:Time | How long in seconds it will display a preset for, before it begins to change from one preset to the next with `presetCycle` turned ON. Only in V0.12.0 and older firmwares. | | `transformTime` | Number:Time | How long in seconds it takes to transform/morph from one look to the next. | @@ -52,6 +53,9 @@ For additional segments, you can add them manually and set the `segmentIndex` co | `syncReceive` | Switch | Allows UDP packets from other WLED lights to control this one. | | `mirror` | Switch | Mirror the effect for this segment. | | `reverse` | Switch | Reverse the effect for this segment. | +| `liveOverride` | String | A value of "0" turns off, "1" will override live data to display what you want, and "2" overrides until you reboot the ESP device. | +| `grouping` | Number | The number of LEDs that are grouped together to display as one pixel in FX. Use metadata to display a list widget slider. | +| `spacing` | Number | The number of LEDs that will not light up in between FX pixels. Use metadata to display a list widget slider. | ## Rule Actions diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java index cdba0e382dc0..7088441bcbc1 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/WLedHandler.java @@ -102,12 +102,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_MIRROR: localApi.setMirror(OnOffType.ON.equals(command), config.segmentIndex); + break; case CHANNEL_LIVE_OVERRIDE: localApi.setLiveOverride(command.toString()); + break; case CHANNEL_SPACING: if (command instanceof DecimalType) { localApi.setSpacing(((DecimalType) command).intValue(), config.segmentIndex); } + break; case CHANNEL_GROUPING: if (command instanceof DecimalType) { localApi.setGrouping(((DecimalType) command).intValue(), config.segmentIndex); diff --git a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java index b507cfaf96e1..bd6d00ddef79 100644 --- a/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java +++ b/bundles/org.openhab.binding.wled/src/main/java/org/openhab/binding/wled/internal/api/WledApiV084.java @@ -184,6 +184,7 @@ protected StateResponse getState() throws ApiException { if (response == null) { throw new ApiException("Could not GET:/json/state"); } + logger.trace("json/state:{}", returnContent); return response; } catch (JsonSyntaxException e) { throw new ApiException("JsonSyntaxException:{}", e); diff --git a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml index 4c7821b41ff9..1d565066ff4d 100644 --- a/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.wled/src/main/resources/OH-INF/thing/thing-types.xml @@ -27,6 +27,7 @@ +