]> git.basschouten.com Git - openhab-addons.git/blob
a19df70e64a3873ec784b4d6695f3d3e99c17f4d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.wled.internal.handlers;
14
15 import static org.openhab.binding.wled.internal.WLedBindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.Future;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
26
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;
55
56 /**
57  * The {@link WLedBridgeHandler} is responsible for talking and parsing data to/from the WLED device.
58  *
59  * @author Matthew Skinner - Initial contribution
60  */
61
62 @NonNullByDefault
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();
72
73     public WLedBridgeHandler(Bridge bridge, WledApiFactory apiFactory,
74             WledDynamicStateDescriptionProvider stateDescriptionProvider) {
75         super(bridge);
76         this.apiFactory = apiFactory;
77         this.stateDescriptionProvider = stateDescriptionProvider;
78     }
79
80     /**
81      * If no thing is setup for specified segmentIndex this will return FALSE.
82      */
83     public boolean handlerMissing(int segmentIndex) {
84         return (segmentHandlers.get(segmentIndex) == null);
85     }
86
87     public void savePreset(int position, String presetName) {
88         WledApi localAPI = api;
89         try {
90             if (localAPI != null) {
91                 localAPI.savePreset(position, presetName);
92             }
93         } catch (ApiException e) {
94             logger.debug("Error occured when trying to save a preset:{}", e.getMessage());
95         }
96     }
97
98     public void removeBridgeChannels(ArrayList<Channel> removeChannels) {
99         ThingBuilder thingBuilder = editThing();
100         thingBuilder.withoutChannels(removeChannels);
101         updateThing(thingBuilder.build());
102     }
103
104     /**
105      * Updates a channel with a new state for a child of this bridge using the segmentIndex
106      *
107      * @param segmentIndex
108      * @param channelID
109      * @param state
110      */
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);
115         }
116     }
117
118     /**
119      * Updates the bridges channels with a new state.
120      *
121      * @param channelID
122      * @param state
123      */
124     public void update(String channelID, State state) {
125         updateState(channelID, state);
126     }
127
128     @Override
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);
132     }
133
134     @Override
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());
138     }
139
140     @Override
141     public void handleCommand(ChannelUID channelUID, Command command) {
142         WledApi localApi = api;
143         if (localApi == null) {
144             return;
145         }
146         try {
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) {
152                         if (PercentType.ZERO.equals(command)) {
153                             localApi.setGlobalOn(false);
154                             return;
155                         }
156                         localApi.setGlobalBrightness((PercentType) command);
157                     }
158                     break;
159                 case CHANNEL_SLEEP:
160                     localApi.setSleep(OnOffType.ON.equals(command));
161                     break;
162                 case CHANNEL_SLEEP_MODE:
163                     localApi.setSleepMode(command.toString());
164                     break;
165                 case CHANNEL_SLEEP_BRIGHTNESS:
166                     if (command instanceof PercentType) {
167                         localApi.setSleepTargetBrightness((PercentType) command);
168                     }
169                     break;
170                 case CHANNEL_SLEEP_DURATION:
171                     if (command instanceof QuantityType) {
172                         QuantityType<?> minutes = ((QuantityType<?>) command).toUnit(Units.MINUTE);
173                         if (minutes != null) {
174                             localApi.setSleepDuration(new BigDecimal(minutes.intValue()));
175                         }
176                     } else if (command instanceof DecimalType) {
177                         localApi.setSleepDuration(new BigDecimal(((DecimalType) command).intValue()));
178                     }
179                     break;
180                 case CHANNEL_PLAYLISTS:
181                     localApi.setPreset(command.toString());
182                     break;
183                 case CHANNEL_SYNC_SEND:
184                     localApi.setUdpSend(OnOffType.ON.equals(command));
185                     break;
186                 case CHANNEL_SYNC_RECEIVE:
187                     localApi.setUdpRecieve(OnOffType.ON.equals(command));
188                     break;
189                 case CHANNEL_LIVE_OVERRIDE:
190                     localApi.setLiveOverride(command.toString());
191                     break;
192                 case CHANNEL_PRESETS:
193                     localApi.setPreset(command.toString());
194                     break;
195                 case CHANNEL_TRANS_TIME:
196                     if (command instanceof QuantityType) {
197                         QuantityType<?> seconds = ((QuantityType<?>) command).toUnit(Units.SECOND);
198                         if (seconds != null) {
199                             localApi.setTransitionTime(new BigDecimal(seconds.multiply(BigDecimal.TEN).intValue()));
200                         }
201                     } else if (command instanceof DecimalType) {
202                         localApi.setTransitionTime(
203                                 new BigDecimal(((DecimalType) command).intValue()).multiply(BigDecimal.TEN));
204                     }
205                     break;
206                 case CHANNEL_PRESET_DURATION:// ch removed in firmware 0.13.0 and newer
207                     if (command instanceof QuantityType) {
208                         QuantityType<?> seconds = ((QuantityType<?>) command).toUnit(Units.SECOND);
209                         if (seconds != null) {
210                             BigDecimal bigTemp = new BigDecimal(seconds.intValue()).multiply(new BigDecimal(1000));
211                             localApi.sendGetRequest("/win&PT=" + bigTemp.intValue());
212                         }
213                     } else if (command instanceof DecimalType) {
214                         BigDecimal bigTemp = new BigDecimal(((DecimalType) command).intValue())
215                                 .multiply(new BigDecimal(1000));
216                         localApi.sendGetRequest("/win&PT=" + bigTemp.intValue());
217                     }
218                     break;
219                 case CHANNEL_PRESET_CYCLE: // ch removed in firmware 0.13.0 and newer
220                     if (command instanceof OnOffType) {
221                         localApi.setPresetCycle(OnOffType.ON.equals(command));
222                     }
223                     break;
224             }
225         } catch (ApiException e) {
226             logger.debug("Exception occured when Channel:{}, Command:{}, Error:{}", channelUID.getId(), command,
227                     e.getMessage());
228         }
229     }
230
231     private void pollState() {
232         WledApi localApi = api;
233         try {
234             if (localApi == null) {
235                 api = localApi = apiFactory.getApi(this);
236                 localApi.initialize();
237             }
238             localApi.update();
239             updateStatus(ThingStatus.ONLINE);
240         } catch (ApiException e) {
241             api = null;// Firmware may be updated so need to check next connect
242             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
243         }
244     }
245
246     @Override
247     public void initialize() {
248         config = getConfigAs(WLedConfiguration.class);
249         if (!config.address.contains("://")) {
250             logger.debug("Address was not entered in correct format, it may be the raw IP so adding http:// to start");
251             config.address = "http://" + config.address;
252         }
253         pollingFuture = scheduler.scheduleWithFixedDelay(this::pollState, 0, config.pollTime, TimeUnit.SECONDS);
254     }
255
256     @Override
257     public void dispose() {
258         Future<?> future = pollingFuture;
259         if (future != null) {
260             future.cancel(true);
261             pollingFuture = null;
262         }
263         api = null; // re-initialize api after configuration change
264     }
265
266     @Override
267     public Collection<Class<? extends ThingHandlerService>> getServices() {
268         return Set.of(WLedActions.class, WLedSegmentDiscoveryService.class);
269     }
270 }