]> git.basschouten.com Git - openhab-addons.git/blob
1274ff1faa597c9bfbd1b1982919c4c9b02d6796
[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         ThingStatus bridgeStatus = bridge.getStatus();
86         if (bridgeStatus == ThingStatus.ONLINE) {
87             updateStatus(ThingStatus.UNKNOWN);
88         } else {
89             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
90         }
91         scheduleRefreshJob();
92     }
93
94     @Override
95     public void dispose() {
96         logger.debug("Disposing repeater handler for repeater {}", repeaterId);
97         cancelRefreshJob();
98         cancelResetIdentifyStateJob();
99     }
100
101     @Override
102     public void handleCommand(ChannelUID channelUID, Command command) {
103         HDPowerViewHubHandler bridge = getBridgeHandler();
104         if (bridge == null) {
105             logger.warn("Missing bridge handler");
106             return;
107         }
108         HDPowerViewWebTargets webTargets = bridge.getWebTargets();
109         if (webTargets == null) {
110             logger.warn("Web targets not initialized");
111             return;
112         }
113
114         try {
115             RepeaterData repeaterData;
116
117             switch (channelUID.getId()) {
118                 case CHANNEL_REPEATER_COLOR:
119                     if (command instanceof HSBType) {
120                         Color currentColor = webTargets.getRepeater(repeaterId).color;
121                         if (currentColor != null) {
122                             HSBType hsbCommand = (HSBType) command;
123                             var color = new Color(currentColor.brightness, hsbCommand);
124                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
125                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
126                         }
127                     } else if (command instanceof OnOffType) {
128                         Color currentColor = webTargets.getRepeater(repeaterId).color;
129                         if (currentColor != null) {
130                             var color = command == OnOffType.ON
131                                     ? new Color(currentColor.brightness, java.awt.Color.WHITE)
132                                     : new Color(currentColor.brightness, java.awt.Color.BLACK);
133                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
134                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
135                         }
136                     }
137                     break;
138                 case CHANNEL_REPEATER_BRIGHTNESS:
139                     if (command instanceof PercentType) {
140                         Color currentColor = webTargets.getRepeater(repeaterId).color;
141                         if (currentColor != null) {
142                             PercentType brightness = (PercentType) command;
143                             var color = new Color(brightness.intValue(), currentColor.red, currentColor.green,
144                                     currentColor.blue);
145                             repeaterData = webTargets.setRepeaterColor(repeaterId, color);
146                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
147                         }
148                     }
149                     break;
150                 case CHANNEL_REPEATER_IDENTIFY:
151                     if (command instanceof StringType) {
152                         if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) {
153                             repeaterData = webTargets.identifyRepeater(repeaterId);
154                             scheduler.submit(() -> updatePropertyAndStates(repeaterData));
155                             cancelResetIdentifyStateJob();
156                             resetIdentifyStateFuture = scheduler.schedule(() -> {
157                                 updateState(CHANNEL_REPEATER_IDENTIFY, UnDefType.UNDEF);
158                             }, IDENTITY_PERIOD_SECONDS, TimeUnit.SECONDS);
159                         } else {
160                             logger.warn("Unsupported command: {}. Supported commands are: " + COMMAND_IDENTIFY,
161                                     command);
162                         }
163                     }
164                     break;
165                 case CHANNEL_REPEATER_BLINKING_ENABLED:
166                     repeaterData = webTargets.enableRepeaterBlinking(repeaterId, OnOffType.ON == command);
167                     scheduler.submit(() -> updatePropertyAndStates(repeaterData));
168                     break;
169             }
170         } catch (HubInvalidResponseException e) {
171             Throwable cause = e.getCause();
172             if (cause == null) {
173                 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
174             } else {
175                 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
176             }
177         } catch (HubMaintenanceException e) {
178             // exceptions are logged in HDPowerViewWebTargets
179         } catch (HubException e) {
180             logger.warn("Unexpected error: {}", e.getMessage());
181         }
182     }
183
184     private void cancelResetIdentifyStateJob() {
185         ScheduledFuture<?> scheduledJob = resetIdentifyStateFuture;
186         if (scheduledJob != null) {
187             scheduledJob.cancel(true);
188         }
189         resetIdentifyStateFuture = null;
190     }
191
192     private void scheduleRefreshJob() {
193         cancelRefreshJob();
194         logger.debug("Scheduling poll for repeater {} now, then every {} minutes", repeaterId,
195                 REFRESH_INTERVAL_MINUTES);
196         this.refreshStatusFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, REFRESH_INTERVAL_MINUTES,
197                 TimeUnit.MINUTES);
198     }
199
200     private void cancelRefreshJob() {
201         ScheduledFuture<?> future = this.refreshStatusFuture;
202         if (future != null) {
203             future.cancel(false);
204         }
205         this.refreshStatusFuture = null;
206     }
207
208     private synchronized void poll() {
209         HDPowerViewHubHandler bridge = getBridgeHandler();
210         if (bridge == null) {
211             logger.warn("Missing bridge handler");
212             return;
213         }
214         HDPowerViewWebTargets webTargets = bridge.getWebTargets();
215         if (webTargets == null) {
216             logger.warn("Web targets not initialized");
217             return;
218         }
219         try {
220             logger.debug("Polling for status information");
221
222             RepeaterData repeaterData = webTargets.getRepeater(repeaterId);
223             updatePropertyAndStates(repeaterData);
224
225         } catch (HubInvalidResponseException e) {
226             Throwable cause = e.getCause();
227             if (cause == null) {
228                 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
229             } else {
230                 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
231             }
232         } catch (HubMaintenanceException e) {
233             // exceptions are logged in HDPowerViewWebTargets
234         } catch (HubException e) {
235             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage());
236         }
237     }
238
239     private void updatePropertyAndStates(RepeaterData repeaterData) {
240         updateStatus(ThingStatus.ONLINE);
241
242         Firmware firmware = repeaterData.firmware;
243         if (firmware != null) {
244             logger.debug("Repeater firmware version received: {}", firmware.toString());
245             updateProperty(Thing.PROPERTY_FIRMWARE_VERSION, firmware.toString());
246         } else {
247             logger.warn("Repeater firmware version missing in response");
248         }
249
250         Color color = repeaterData.color;
251         if (color != null) {
252             logger.debug("Repeater color data received: {}", color.toString());
253             updateState(CHANNEL_REPEATER_COLOR, HSBType.fromRGB(color.red, color.green, color.red));
254             updateState(CHANNEL_REPEATER_BRIGHTNESS, new PercentType(color.brightness));
255         }
256
257         updateState(CHANNEL_REPEATER_BLINKING_ENABLED, repeaterData.blinkEnabled ? OnOffType.ON : OnOffType.OFF);
258     }
259 }