]> git.basschouten.com Git - openhab-addons.git/blob
a3a0cf35fee7871c1f007f37462eef85172ab8fa
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.hdpowerview.internal.handler;
14
15 import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*;
16
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
23 import org.openhab.binding.hdpowerview.internal.api.Color;
24 import org.openhab.binding.hdpowerview.internal.api.Firmware;
25 import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
26 import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration;
27 import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
28 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
29 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
30 import org.openhab.core.library.types.HSBType;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.PercentType;
33 import org.openhab.core.library.types.StringType;
34 import org.openhab.core.thing.Bridge;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.UnDefType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Handles commands for an HD PowerView Repeater
46  *
47  * @author Jacob Laursen - Initial contribution
48  */
49 @NonNullByDefault
50 public class HDPowerViewRepeaterHandler extends AbstractHubbedThingHandler {
51
52     private final Logger logger = LoggerFactory.getLogger(HDPowerViewRepeaterHandler.class);
53
54     private static final int REFRESH_INTERVAL_MINUTES = 5;
55     private static final int IDENTITY_PERIOD_SECONDS = 3;
56     private static final String COMMAND_IDENTIFY = "IDENTIFY";
57
58     private @Nullable ScheduledFuture<?> refreshStatusFuture = null;
59     private @Nullable ScheduledFuture<?> resetIdentifyStateFuture = null;
60     private int repeaterId;
61
62     public HDPowerViewRepeaterHandler(Thing thing) {
63         super(thing);
64     }
65
66     @Override
67     public void initialize() {
68         repeaterId = getConfigAs(HDPowerViewRepeaterConfiguration.class).id;
69         logger.debug("Initializing repeater handler for repeater {}", repeaterId);
70         if (repeaterId <= 0) {
71             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
72                     "@text/offline.conf-error.invalid-id");
73             return;
74         }
75         Bridge bridge = getBridge();
76         if (bridge == null) {
77             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
78             return;
79         }
80         if (!(bridge.getHandler() instanceof HDPowerViewHubHandler)) {
81             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
82                     "@text/offline.conf-error.invalid-bridge-handler");
83             return;
84         }
85
86         updateStatus(ThingStatus.UNKNOWN);
87
88         scheduleRefreshJob();
89     }
90
91     @Override
92     public void dispose() {
93         logger.debug("Disposing repeater handler for repeater {}", repeaterId);
94         cancelRefreshJob();
95         cancelResetIdentifyStateJob();
96     }
97
98     @Override
99     public void handleCommand(ChannelUID channelUID, Command command) {
100         HDPowerViewHubHandler bridge = getBridgeHandler();
101         if (bridge == null) {
102             logger.warn("Missing bridge handler");
103             return;
104         }
105         HDPowerViewWebTargets webTargets = bridge.getWebTargets();
106         if (webTargets == null) {
107             logger.warn("Web targets not initialized");
108             return;
109         }
110
111         try {
112             RepeaterData repeaterData;
113
114             switch (channelUID.getId()) {
115                 case CHANNEL_REPEATER_COLOR:
116                     if (command instanceof HSBType) {
117                         Color currentColor = webTargets.getRepeater(repeaterId).color;
118                         if (currentColor != null) {
119                             HSBType hsbCommand = (HSBType) command;
120                             var color = new Color(currentColor.brightness, hsbCommand);
121                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
122                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
123                         }
124                     } else if (command instanceof OnOffType) {
125                         Color currentColor = webTargets.getRepeater(repeaterId).color;
126                         if (currentColor != null) {
127                             var color = command == OnOffType.ON
128                                     ? new Color(currentColor.brightness, java.awt.Color.WHITE)
129                                     : new Color(currentColor.brightness, java.awt.Color.BLACK);
130                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
131                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
132                         }
133                     }
134                     break;
135                 case CHANNEL_REPEATER_BRIGHTNESS:
136                     if (command instanceof PercentType) {
137                         Color currentColor = webTargets.getRepeater(repeaterId).color;
138                         if (currentColor != null) {
139                             PercentType brightness = (PercentType) command;
140                             var color = new Color(brightness.intValue(), currentColor.red, currentColor.green,
141                                     currentColor.blue);
142                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
143                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
144                         }
145                     }
146                     break;
147                 case CHANNEL_REPEATER_IDENTIFY:
148                     if (command instanceof StringType) {
149                         if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) {
150                             repeaterData = webTargets.identifyRepeater(repeaterId);
151                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
152                             cancelResetIdentifyStateJob();
153                             resetIdentifyStateFuture = scheduler.schedule(() -> {
154                                 updateState(CHANNEL_REPEATER_IDENTIFY, UnDefType.UNDEF);
155                             }, IDENTITY_PERIOD_SECONDS, TimeUnit.SECONDS);
156                         } else {
157                             logger.warn("Unsupported command: {}. Supported commands are: " + COMMAND_IDENTIFY,
158                                     command);
159                         }
160                     }
161                     break;
162                 case CHANNEL_REPEATER_BLINKING_ENABLED:
163                     repeaterData = webTargets.enableRepeaterBlinking(repeaterId, OnOffType.ON == command);
164                     scheduler.submit(() -> updatePropertyAndStates(repeaterData));
165                     break;
166             }
167         } catch (HubInvalidResponseException e) {
168             Throwable cause = e.getCause();
169             if (cause == null) {
170                 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
171             } else {
172                 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
173             }
174         } catch (HubMaintenanceException e) {
175             // exceptions are logged in HDPowerViewWebTargets
176         } catch (HubException e) {
177             logger.warn("Unexpected error: {}", e.getMessage());
178         }
179     }
180
181     private void cancelResetIdentifyStateJob() {
182         ScheduledFuture<?> scheduledJob = resetIdentifyStateFuture;
183         if (scheduledJob != null) {
184             scheduledJob.cancel(true);
185         }
186         resetIdentifyStateFuture = null;
187     }
188
189     private void scheduleRefreshJob() {
190         cancelRefreshJob();
191         logger.debug("Scheduling poll for repeater {} now, then every {} minutes", repeaterId,
192                 REFRESH_INTERVAL_MINUTES);
193         this.refreshStatusFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, REFRESH_INTERVAL_MINUTES,
194                 TimeUnit.MINUTES);
195     }
196
197     private void cancelRefreshJob() {
198         ScheduledFuture<?> future = this.refreshStatusFuture;
199         if (future != null) {
200             future.cancel(false);
201         }
202         this.refreshStatusFuture = null;
203     }
204
205     private synchronized void poll() {
206         HDPowerViewHubHandler bridge = getBridgeHandler();
207         if (bridge == null) {
208             logger.warn("Missing bridge handler");
209             return;
210         }
211         HDPowerViewWebTargets webTargets = bridge.getWebTargets();
212         if (webTargets == null) {
213             logger.warn("Web targets not initialized");
214             return;
215         }
216         try {
217             logger.debug("Polling for status information");
218
219             RepeaterData repeaterData = webTargets.getRepeater(repeaterId);
220             updatePropertyAndStates(repeaterData);
221
222         } catch (HubInvalidResponseException e) {
223             Throwable cause = e.getCause();
224             if (cause == null) {
225                 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
226             } else {
227                 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
228             }
229         } catch (HubMaintenanceException e) {
230             // exceptions are logged in HDPowerViewWebTargets
231         } catch (HubException e) {
232             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage());
233         }
234     }
235
236     private void updatePropertyAndStates(RepeaterData repeaterData) {
237         updateStatus(ThingStatus.ONLINE);
238
239         Firmware firmware = repeaterData.firmware;
240         if (firmware != null) {
241             logger.debug("Repeater firmware version received: {}", firmware.toString());
242             updateProperty(Thing.PROPERTY_FIRMWARE_VERSION, firmware.toString());
243         } else {
244             logger.warn("Repeater firmware version missing in response");
245         }
246
247         Color color = repeaterData.color;
248         if (color != null) {
249             logger.debug("Repeater color data received: {}", color.toString());
250             updateState(CHANNEL_REPEATER_COLOR, HSBType.fromRGB(color.red, color.green, color.red));
251             updateState(CHANNEL_REPEATER_BRIGHTNESS, new PercentType(color.brightness));
252         }
253
254         updateState(CHANNEL_REPEATER_BLINKING_ENABLED, repeaterData.blinkEnabled ? OnOffType.ON : OnOffType.OFF);
255     }
256 }