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