[WLed] Initial contribution - Binding for LED strings with a large range of built in FX. (#8669)
* V3 * Fix null compiler warnings. Signed-off-by: Matthew Skinner <matt@pcmus.com> Co-authored-by: Fabian Wolter <github@fabian-wolter.de> Co-authored-by: Connor Petty <mistercpp2000+gitsignoff@gmail.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.wled-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features</repository>
|
||||
<feature name="openhab-binding-wled" description="WLED Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.wled/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WLedActions} is responsible for Actions.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
|
||||
@ThingActionsScope(name = "wled")
|
||||
@NonNullByDefault
|
||||
public class WLedActions implements ThingActions {
|
||||
public final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private @Nullable WLedHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
this.handler = (WLedHandler) handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return 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) {
|
||||
WLedHandler localHandler = handler;
|
||||
if (presetNumber > 0 && localHandler != null) {
|
||||
localHandler.savePreset(presetNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public static void savePreset(@Nullable ThingActions actions, int presetNumber) {
|
||||
if (actions instanceof WLedActions) {
|
||||
((WLedActions) actions).savePreset(presetNumber);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Instance is not a WLED class.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.math.BigDecimal;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link WLedBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WLedBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "wled";
|
||||
public static final BigDecimal BIG_DECIMAL_2_55 = new BigDecimal(2.55);
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_WLED = new ThingTypeUID(BINDING_ID, "wled");
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_WLED);
|
||||
|
||||
// Configs
|
||||
public static final String CONFIG_ADDRESS = "address";
|
||||
public static final String CONFIG_POLL_TIME = "pollTime";
|
||||
public static final String CONFIG_SEGMENT_INDEX = "segmentIndex";
|
||||
public static final String CONFIG_SAT_THRESHOLD = "saturationThreshold";
|
||||
|
||||
// Channels
|
||||
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_PRIMARY_WHITE = "primaryWhite";
|
||||
public static final String CHANNEL_SECONDARY_WHITE = "secondaryWhite";
|
||||
public static final String CHANNEL_PALETTES = "palettes";
|
||||
public static final String CHANNEL_PRESETS = "presets";
|
||||
public static final String CHANNEL_PRESET_DURATION = "presetDuration";
|
||||
public static final String CHANNEL_TRANS_TIME = "transformTime";
|
||||
public static final String CHANNEL_PRESET_CYCLE = "presetCycle";
|
||||
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_SLEEP = "sleep";
|
||||
public static final String CHANNEL_SYNC_SEND = "syncSend";
|
||||
public static final String CHANNEL_SYNC_RECEIVE = "syncReceive";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WLedConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WLedConfiguration {
|
||||
public String address = "";
|
||||
public int pollTime;
|
||||
public int segmentIndex;
|
||||
public int saturationThreshold;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 static org.openhab.binding.wled.internal.WLedBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.jmdns.ServiceInfo;
|
||||
|
||||
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.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
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 WLedDiscoveryService} Discovers and adds any Wled devices found.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = MDNSDiscoveryParticipant.class)
|
||||
public class WLedDiscoveryService implements MDNSDiscoveryParticipant {
|
||||
private final Logger logger = LoggerFactory.getLogger(WLedDiscoveryService.class);
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@Activate
|
||||
public WLedDiscoveryService(@Reference HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
private String sendGetRequest(String address, String url) {
|
||||
Request request = httpClient.newRequest(address + url);
|
||||
request.timeout(3, TimeUnit.SECONDS);
|
||||
request.method(HttpMethod.GET);
|
||||
request.header(HttpHeader.ACCEPT_ENCODING, "gzip");
|
||||
logger.trace("Sending WLED GET:{}", url);
|
||||
try {
|
||||
ContentResponse contentResponse = request.send();
|
||||
if (contentResponse.getStatus() == 200) {
|
||||
return contentResponse.getContentAsString();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
logger.debug(
|
||||
"WLED discovery hit a TimeoutException | ExecutionException which may have blocked a device from getting discovered:{}",
|
||||
e.getMessage());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(ServiceInfo service) {
|
||||
String name = service.getName().toLowerCase();
|
||||
if (!name.contains("wled")) {
|
||||
return null;
|
||||
}
|
||||
String address[] = service.getURLs();
|
||||
if ((address == null) || address.length < 1) {
|
||||
logger.debug("WLED discovered with empty IP address-{}", service);
|
||||
return null;
|
||||
}
|
||||
String response = sendGetRequest(address[0], "/json");
|
||||
// LinkedList<String> segmentIndexList = WLedHelper.listOfResults(response, "{\"id\":", ",");
|
||||
// How to create multiple things from the returned list of segments?
|
||||
String label = WLedHelper.getValue(response, "\"name\":\"", "\"");
|
||||
if (label.isEmpty()) {
|
||||
label = "WLED @ " + address[0];
|
||||
}
|
||||
String macAddress = WLedHelper.getValue(response, "\"mac\":\"", "\"");
|
||||
String firmware = WLedHelper.getValue(response, "\"ver\":\"", "\"");
|
||||
ThingTypeUID thingtypeuid = new ThingTypeUID("wled", "wled");
|
||||
ThingUID thingUID = new ThingUID(thingtypeuid, macAddress);
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
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)
|
||||
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(ServiceInfo service) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceType() {
|
||||
return "_http._tcp.local.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 static org.openhab.binding.wled.internal.WLedBindingConstants.*;
|
||||
|
||||
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.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.SmartHomeUnits;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* The {@link WLedHandler} is responsible for handling commands and states, which are
|
||||
* sent to one of the channels or http replies back.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class WLedHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private final HttpClient httpClient;
|
||||
private final WledDynamicStateDescriptionProvider stateDescriptionProvider;
|
||||
private @Nullable ScheduledFuture<?> pollingFuture = null;
|
||||
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;
|
||||
private WLedConfiguration config = new WLedConfiguration();
|
||||
|
||||
public WLedHandler(Thing thing, HttpClient httpClient,
|
||||
WledDynamicStateDescriptionProvider stateDescriptionProvider) {
|
||||
super(thing);
|
||||
this.httpClient = httpClient;
|
||||
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) {
|
||||
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 <cl>
|
||||
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, "<cl>");
|
||||
updateState(CHANNEL_PRIMARY_COLOR, primaryColor);
|
||||
secondaryColor = parseToHSBType(message, "<cs>");
|
||||
updateState(CHANNEL_SECONDARY_COLOR, secondaryColor);
|
||||
try {
|
||||
primaryWhite = new BigDecimal(WLedHelper.getValue(message, "<wv>", "<"));
|
||||
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, "<ws>", "<"));
|
||||
updateState(CHANNEL_SECONDARY_WHITE,
|
||||
new PercentType(secondaryWhite.divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP)));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("NumberFormatException when parsing the WLED colour and white fields:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 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<StateOption> fxOptions = new ArrayList<>();
|
||||
List<StateOption> 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("<ac>0</ac>")) {
|
||||
updateState(CHANNEL_MASTER_CONTROLS, OnOffType.OFF);
|
||||
} else {
|
||||
masterBrightness255 = new BigDecimal(WLedHelper.getValue(message, "<ac>", "<"));
|
||||
updateState(CHANNEL_MASTER_CONTROLS,
|
||||
new PercentType(masterBrightness255.divide(BIG_DECIMAL_2_55, RoundingMode.HALF_UP)));
|
||||
}
|
||||
if (message.contains("<ix>0</ix>")) {
|
||||
updateState(CHANNEL_INTENSITY, OnOffType.OFF);
|
||||
} else {
|
||||
BigDecimal bigTemp = new BigDecimal(WLedHelper.getValue(message, "<ix>", "<")).divide(BIG_DECIMAL_2_55,
|
||||
RoundingMode.HALF_UP);
|
||||
updateState(CHANNEL_INTENSITY, new PercentType(bigTemp));
|
||||
}
|
||||
if (message.contains("<cy>1</cy>")) {
|
||||
updateState(CHANNEL_PRESET_CYCLE, OnOffType.ON);
|
||||
} else {
|
||||
updateState(CHANNEL_PRESET_CYCLE, OnOffType.OFF);
|
||||
}
|
||||
if (message.contains("<nl>1</nl>")) {
|
||||
updateState(CHANNEL_SLEEP, OnOffType.ON);
|
||||
} else {
|
||||
updateState(CHANNEL_SLEEP, OnOffType.OFF);
|
||||
}
|
||||
if (message.contains("<ns>1</ns>")) {
|
||||
updateState(CHANNEL_SYNC_SEND, OnOffType.ON);
|
||||
} else {
|
||||
updateState(CHANNEL_SYNC_SEND, OnOffType.OFF);
|
||||
}
|
||||
if (message.contains("<nr>1</nr>")) {
|
||||
updateState(CHANNEL_SYNC_RECEIVE, OnOffType.ON);
|
||||
} else {
|
||||
updateState(CHANNEL_SYNC_RECEIVE, OnOffType.OFF);
|
||||
}
|
||||
if (message.contains("<fx>")) {
|
||||
updateState(CHANNEL_FX, new StringType(WLedHelper.getValue(message, "<fx>", "<")));
|
||||
}
|
||||
if (message.contains("<sx>")) {
|
||||
BigDecimal bigTemp = new BigDecimal(WLedHelper.getValue(message, "<sx>", "<")).divide(BIG_DECIMAL_2_55,
|
||||
RoundingMode.HALF_UP);
|
||||
updateState(CHANNEL_SPEED, new PercentType(bigTemp));
|
||||
}
|
||||
if (message.contains("<fp>")) {
|
||||
updateState(CHANNEL_PALETTES, new StringType(WLedHelper.getValue(message, "<fp>", "<")));
|
||||
}
|
||||
parseColours(message);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
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());
|
||||
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");
|
||||
}
|
||||
} 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");
|
||||
} else {
|
||||
sendGetRequest("/win&TT=1000&A=0");
|
||||
}
|
||||
}
|
||||
} else if (command instanceof HSBType) {
|
||||
if ((((HSBType) command).getBrightness()) == PercentType.ZERO) {
|
||||
sendGetRequest("/win&TT=500&T=0");
|
||||
}
|
||||
primaryColor = (HSBType) command;
|
||||
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 {
|
||||
sendGetRequest("/win&TT=1000&FX=0&CY=0&CL=" + createColorHex(primaryColor) + "&A="
|
||||
+ masterBrightness255);
|
||||
}
|
||||
} 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&NL=1");
|
||||
} 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(SmartHomeUnits.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(SmartHomeUnits.SECOND);
|
||||
if (seconds != null) {
|
||||
bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000));
|
||||
sendGetRequest("/win&TT=" + bigTemp.intValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHANNEL_PRESET_CYCLE:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
sendGetRequest("/win&CY=1");
|
||||
} else {
|
||||
sendGetRequest("/win&CY=0");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void savePreset(int presetIndex) {
|
||||
if (presetIndex > 16) {
|
||||
logger.warn("Presets above 16 do not exist, and the action sent {}", presetIndex);
|
||||
return;
|
||||
}
|
||||
sendGetRequest("/win&PS=" + presetIndex);
|
||||
}
|
||||
|
||||
private void pollLED() {
|
||||
sendGetRequest("/win");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(WLedConfiguration.class);
|
||||
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::pollLED, 1, config.pollTime, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Future<?> future = pollingFuture;
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(WLedActions.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 static org.openhab.binding.wled.internal.WLedBindingConstants.SUPPORTED_THING_TYPES;
|
||||
|
||||
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.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link WLedHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.wled", service = ThingHandlerFactory.class)
|
||||
public class WLedHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final HttpClient httpClient;
|
||||
private final WledDynamicStateDescriptionProvider stateDescriptionProvider;
|
||||
|
||||
@Activate
|
||||
public WLedHandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference WledDynamicStateDescriptionProvider stateDescriptionProvider) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
|
||||
return new WLedHandler(thing, httpClient, stateDescriptionProvider);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WLedHelper} Provides helper classes that are used from multiple classes in the binding.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WLedHelper {
|
||||
|
||||
/**
|
||||
* @return A string that starts after finding the element and terminates when it finds the first occurrence of the
|
||||
* end string after the element.
|
||||
*/
|
||||
static String getValue(String message, String element, String end) {
|
||||
int startIndex = message.indexOf(element);
|
||||
if (startIndex != -1) // -1 means "not found"
|
||||
{
|
||||
int endIndex = message.indexOf(end, startIndex + element.length());
|
||||
if (endIndex != -1) {
|
||||
return message.substring(startIndex + element.length(), endIndex);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A List that holds the values from a heading/element that re-occurs in a message multiple times.
|
||||
*
|
||||
*/
|
||||
static List<String> listOfResults(String message, String element, String end) {
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link WledDynamicStateDescriptionProvider} Allows the dynamic updating of the FX and Palletes that WLED keep
|
||||
* changing between firmware versions.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, WledDynamicStateDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class WledDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
@Activate
|
||||
public WledDynamicStateDescriptionProvider(
|
||||
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="wled" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>WLED Binding</name>
|
||||
<description>This is the binding for WLED</description>
|
||||
<author>Matthew Skinner</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="wled"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="wled">
|
||||
<label>WLED String</label>
|
||||
<description>A WLED string of LEDs</description>
|
||||
<category>ColorLight</category>
|
||||
<channels>
|
||||
<channel id="masterControls" typeId="masterControls"/>
|
||||
<channel id="primaryColor" typeId="primaryColor"/>
|
||||
<channel id="primaryWhite" typeId="primaryWhite"/>
|
||||
<channel id="secondaryColor" typeId="secondaryColor"/>
|
||||
<channel id="secondaryWhite" typeId="secondaryWhite"/>
|
||||
<channel id="presets" typeId="presets"/>
|
||||
<channel id="presetDuration" typeId="presetDuration"/>
|
||||
<channel id="transformTime" typeId="transformTime"/>
|
||||
<channel id="presetCycle" typeId="presetCycle"/>
|
||||
<channel id="palettes" typeId="palettes"/>
|
||||
<channel id="fx" typeId="fx"/>
|
||||
<channel id="speed" typeId="speed"/>
|
||||
<channel id="intensity" typeId="intensity"/>
|
||||
<channel id="sleep" typeId="sleep"/>
|
||||
<channel id="syncSend" typeId="syncSend"/>
|
||||
<channel id="syncReceive" typeId="syncReceive"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="address" type="text" required="true">
|
||||
<label>Address</label>
|
||||
<description>Use this format http://192.168.1.2:80</description>
|
||||
</parameter>
|
||||
<parameter name="pollTime" type="integer" required="true" min="1" unit="s">
|
||||
<label>Poll States</label>
|
||||
<description>Time in seconds of how often to fetch the state of the LEDs.</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
<parameter name="segmentIndex" type="integer" required="true" min="-1">
|
||||
<label>Segment Index</label>
|
||||
<description>Leave this as -1 if you are not using segments, otherwise set this to the segment index number that you
|
||||
wish to control.</description>
|
||||
<default>-1</default>
|
||||
</parameter>
|
||||
<parameter name="saturationThreshold" type="integer" required="true" min="0" max="99">
|
||||
<label>Saturation Threshold</label>
|
||||
<description>This feature allows you to specify a number that if the saturation drops below, will trigger white.
|
||||
</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="masterControls">
|
||||
<item-type>Color</item-type>
|
||||
<label>Master Controls</label>
|
||||
<description>Allows you to exit FX mode and use the LEDS like a normal light</description>
|
||||
<category>ColorLight</category>
|
||||
<tags>
|
||||
<tag>Lighting</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="primaryColor" advanced="true">
|
||||
<item-type>Color</item-type>
|
||||
<label>Primary Color</label>
|
||||
<description>Allows you to change the primary color used in FX</description>
|
||||
<category>ColorLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="primaryWhite" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Primary White</label>
|
||||
<description>Changes the brightness of the primary white LED</description>
|
||||
<category>DimmableLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="secondaryColor" advanced="true">
|
||||
<item-type>Color</item-type>
|
||||
<label>Secondary Color</label>
|
||||
<description>Allows you to change the secondary color used in FX</description>
|
||||
<category>ColorLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="secondaryWhite" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Secondary White</label>
|
||||
<description>Changes the brightness of the secondary white LED</description>
|
||||
<category>DimmableLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="palettes">
|
||||
<item-type>String</item-type>
|
||||
<label>Palettes</label>
|
||||
<description>Change the colours used by the FX</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fx">
|
||||
<item-type>String</item-type>
|
||||
<label>Effect</label>
|
||||
<description>Use the built in FX</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="presets">
|
||||
<item-type>String</item-type>
|
||||
<label>Presets</label>
|
||||
<description>Auto rotate or change to a saved preset</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="1">Preset 1</option>
|
||||
<option value="2">Preset 2</option>
|
||||
<option value="3">Preset 3</option>
|
||||
<option value="4">Preset 4</option>
|
||||
<option value="5">Preset 5</option>
|
||||
<option value="6">Preset 6</option>
|
||||
<option value="7">Preset 7</option>
|
||||
<option value="8">Preset 8</option>
|
||||
<option value="9">Preset 9</option>
|
||||
<option value="10">Preset 10</option>
|
||||
<option value="11">Preset 11</option>
|
||||
<option value="12">Preset 12</option>
|
||||
<option value="13">Preset 13</option>
|
||||
<option value="14">Preset 14</option>
|
||||
<option value="15">Preset 15</option>
|
||||
<option value="16">Preset 16</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="presetDuration" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Preset Duration</label>
|
||||
<description>Time for how long to show each preset for before moving to the next</description>
|
||||
<category>Time</category>
|
||||
<state min="0.1" max="65" step="0.1" pattern="%.1f %unit%" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="transformTime" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Transform Time</label>
|
||||
<description>Time it takes to change/fade from one look to the next.</description>
|
||||
<category>Time</category>
|
||||
<state min="0" max="65" step="0.1" pattern="%.1f %unit%" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="speed" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>FX Speed</label>
|
||||
<description>Change the speed of the FX</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="intensity" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>FX Intensity</label>
|
||||
<description>Change the intensity of the FX</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sleep" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Sleep Timer</label>
|
||||
<description>Fade the level of light and turn off after set time</description>
|
||||
<category>Time</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="presetCycle">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Preset Cycle</label>
|
||||
<description>Cycle through the saved presets</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="syncSend" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Sync Send</label>
|
||||
<description>Sends UDP packets that tell other WLED lights to follow this one.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="syncReceive" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Sync Receive</label>
|
||||
<description>Allows UDP packets from other WLED lights to control this one.</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user