2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.wled.internal.handlers;
15 import static org.openhab.binding.wled.internal.WLedBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
23 import java.util.concurrent.Future;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.wled.internal.WLedActions;
30 import org.openhab.binding.wled.internal.WLedConfiguration;
31 import org.openhab.binding.wled.internal.WLedSegmentDiscoveryService;
32 import org.openhab.binding.wled.internal.WledDynamicStateDescriptionProvider;
33 import org.openhab.binding.wled.internal.api.ApiException;
34 import org.openhab.binding.wled.internal.api.WledApi;
35 import org.openhab.binding.wled.internal.api.WledApiFactory;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.library.types.PercentType;
39 import org.openhab.core.library.types.QuantityType;
40 import org.openhab.core.library.unit.Units;
41 import org.openhab.core.thing.Bridge;
42 import org.openhab.core.thing.Channel;
43 import org.openhab.core.thing.ChannelUID;
44 import org.openhab.core.thing.Thing;
45 import org.openhab.core.thing.ThingStatus;
46 import org.openhab.core.thing.ThingStatusDetail;
47 import org.openhab.core.thing.binding.BaseBridgeHandler;
48 import org.openhab.core.thing.binding.ThingHandler;
49 import org.openhab.core.thing.binding.ThingHandlerService;
50 import org.openhab.core.thing.binding.builder.ThingBuilder;
51 import org.openhab.core.types.Command;
52 import org.openhab.core.types.State;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * The {@link WLedBridgeHandler} is responsible for talking and parsing data to/from the WLED device.
59 * @author Matthew Skinner - Initial contribution
63 public class WLedBridgeHandler extends BaseBridgeHandler {
64 private final Logger logger = LoggerFactory.getLogger(getClass());
65 public final WledDynamicStateDescriptionProvider stateDescriptionProvider;
66 private Map<Integer, WLedSegmentHandler> segmentHandlers = new HashMap<Integer, WLedSegmentHandler>();
67 private WledApiFactory apiFactory;
68 public boolean hasWhite = false;
69 public @Nullable WledApi api;
70 private @Nullable ScheduledFuture<?> pollingFuture = null;
71 public WLedConfiguration config = new WLedConfiguration();
73 public WLedBridgeHandler(Bridge bridge, WledApiFactory apiFactory,
74 WledDynamicStateDescriptionProvider stateDescriptionProvider) {
76 this.apiFactory = apiFactory;
77 this.stateDescriptionProvider = stateDescriptionProvider;
81 * If no thing is setup for specified segmentIndex this will return FALSE.
83 public boolean handlerMissing(int segmentIndex) {
84 return (segmentHandlers.get(segmentIndex) == null);
87 public void savePreset(int position, String presetName) {
88 WledApi localAPI = api;
90 if (localAPI != null) {
91 localAPI.savePreset(position, presetName);
93 } catch (ApiException e) {
94 logger.debug("Error occured when trying to save a preset:{}", e.getMessage());
98 public void removeBridgeChannels(ArrayList<Channel> removeChannels) {
99 ThingBuilder thingBuilder = editThing();
100 thingBuilder.withoutChannels(removeChannels);
101 updateThing(thingBuilder.build());
105 * Updates a channel with a new state for a child of this bridge using the segmentIndex
107 * @param segmentIndex
111 public void update(int segmentIndex, String channelID, State state) {
112 WLedSegmentHandler segmentHandler = segmentHandlers.get(segmentIndex);
113 if (segmentHandler != null) {
114 segmentHandler.update(channelID, state);
119 * Updates the bridges channels with a new state.
124 public void update(String channelID, State state) {
125 updateState(channelID, state);
129 public void childHandlerInitialized(final ThingHandler childHandler, final Thing childThing) {
130 BigDecimal segmentIndex = (BigDecimal) childThing.getConfiguration().get(CONFIG_SEGMENT_INDEX);
131 segmentHandlers.put(segmentIndex.intValue(), (WLedSegmentHandler) childHandler);
135 public void childHandlerDisposed(final ThingHandler childHandler, final Thing childThing) {
136 BigDecimal segmentIndex = (BigDecimal) childThing.getConfiguration().get(CONFIG_SEGMENT_INDEX);
137 segmentHandlers.remove(segmentIndex.intValue());
141 public void handleCommand(ChannelUID channelUID, Command command) {
142 WledApi localApi = api;
143 if (localApi == null) {
147 switch (channelUID.getId()) {
148 case CHANNEL_GLOBAL_BRIGHTNESS:
149 if (command instanceof OnOffType) {
150 localApi.setGlobalOn(OnOffType.ON.equals(command));
151 } else if (command instanceof PercentType percentCommand) {
152 if (PercentType.ZERO.equals(command)) {
153 localApi.setGlobalOn(false);
156 localApi.setGlobalBrightness(percentCommand);
160 localApi.setSleep(OnOffType.ON.equals(command));
162 case CHANNEL_SLEEP_MODE:
163 localApi.setSleepMode(command.toString());
165 case CHANNEL_SLEEP_BRIGHTNESS:
166 if (command instanceof PercentType percentCommand) {
167 localApi.setSleepTargetBrightness(percentCommand);
170 case CHANNEL_SLEEP_DURATION:
171 if (command instanceof QuantityType quantityCommand) {
172 QuantityType<?> minutes = quantityCommand.toUnit(Units.MINUTE);
173 if (minutes != null) {
174 localApi.setSleepDuration(new BigDecimal(minutes.intValue()));
176 } else if (command instanceof DecimalType decimalCommand) {
177 localApi.setSleepDuration(new BigDecimal(decimalCommand.intValue()));
180 case CHANNEL_PLAYLISTS:
181 localApi.setPreset(command.toString());
183 case CHANNEL_SYNC_SEND:
184 localApi.setUdpSend(OnOffType.ON.equals(command));
186 case CHANNEL_SYNC_RECEIVE:
187 localApi.setUdpRecieve(OnOffType.ON.equals(command));
189 case CHANNEL_LIVE_OVERRIDE:
190 localApi.setLiveOverride(command.toString());
192 case CHANNEL_PRESETS:
193 localApi.setPreset(command.toString());
195 case CHANNEL_TRANS_TIME:
196 if (command instanceof QuantityType quantityCommand) {
197 QuantityType<?> seconds = quantityCommand.toUnit(Units.SECOND);
198 if (seconds != null) {
199 localApi.setTransitionTime(new BigDecimal(seconds.multiply(BigDecimal.TEN).intValue()));
201 } else if (command instanceof DecimalType decimalCommand) {
202 localApi.setTransitionTime(new BigDecimal(decimalCommand.intValue()).multiply(BigDecimal.TEN));
205 case CHANNEL_PRESET_DURATION:// ch removed in firmware 0.13.0 and newer
206 if (command instanceof QuantityType quantityCommand) {
207 QuantityType<?> seconds = quantityCommand.toUnit(Units.SECOND);
208 if (seconds != null) {
209 BigDecimal bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000));
210 localApi.sendGetRequest("/win&PT=" + bigTemp.intValue());
212 } else if (command instanceof DecimalType decimalCommand) {
213 BigDecimal bigTemp = new BigDecimal(decimalCommand.intValue()).multiply(new BigDecimal(1000));
214 localApi.sendGetRequest("/win&PT=" + bigTemp.intValue());
217 case CHANNEL_PRESET_CYCLE: // ch removed in firmware 0.13.0 and newer
218 if (command instanceof OnOffType) {
219 localApi.setPresetCycle(OnOffType.ON.equals(command));
223 } catch (ApiException e) {
224 logger.debug("Exception occured when Channel:{}, Command:{}, Error:{}", channelUID.getId(), command,
229 private void pollState() {
230 WledApi localApi = api;
232 if (localApi == null) {
233 api = localApi = apiFactory.getApi(this);
234 localApi.initialize();
237 updateStatus(ThingStatus.ONLINE);
238 } catch (ApiException e) {
239 api = null;// Firmware may be updated so need to check next connect
240 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
245 public void initialize() {
246 config = getConfigAs(WLedConfiguration.class);
247 if (!config.address.contains("://")) {
248 logger.debug("Address was not entered in correct format, it may be the raw IP so adding http:// to start");
249 config.address = "http://" + config.address;
251 pollingFuture = scheduler.scheduleWithFixedDelay(this::pollState, 0, config.pollTime, TimeUnit.SECONDS);
255 public void dispose() {
256 Future<?> future = pollingFuture;
257 if (future != null) {
259 pollingFuture = null;
261 api = null; // re-initialize api after configuration change
265 public Collection<Class<? extends ThingHandlerService>> getServices() {
266 return Set.of(WLedActions.class, WLedSegmentDiscoveryService.class);