2 * Copyright (c) 2010-2022 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.hdpowerview.internal.handler;
15 import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
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;
45 * Handles commands for an HD PowerView Repeater
47 * @author Jacob Laursen - Initial contribution
50 public class HDPowerViewRepeaterHandler extends AbstractHubbedThingHandler {
52 private final Logger logger = LoggerFactory.getLogger(HDPowerViewRepeaterHandler.class);
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";
58 private @Nullable ScheduledFuture<?> refreshStatusFuture = null;
59 private @Nullable ScheduledFuture<?> resetIdentifyStateFuture = null;
60 private int repeaterId;
62 public HDPowerViewRepeaterHandler(Thing thing) {
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");
75 Bridge bridge = getBridge();
77 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
80 if (!(bridge.getHandler() instanceof HDPowerViewHubHandler)) {
81 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
82 "@text/offline.conf-error.invalid-bridge-handler");
85 ThingStatus bridgeStatus = bridge.getStatus();
86 if (bridgeStatus == ThingStatus.ONLINE) {
87 updateStatus(ThingStatus.UNKNOWN);
89 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
95 public void dispose() {
96 logger.debug("Disposing repeater handler for repeater {}", repeaterId);
98 cancelResetIdentifyStateJob();
102 public void handleCommand(ChannelUID channelUID, Command command) {
103 HDPowerViewHubHandler bridge = getBridgeHandler();
104 if (bridge == null) {
105 logger.warn("Missing bridge handler");
108 HDPowerViewWebTargets webTargets = bridge.getWebTargets();
109 if (webTargets == null) {
110 logger.warn("Web targets not initialized");
115 RepeaterData repeaterData;
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));
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));
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,
145 repeaterData = webTargets.setRepeaterColor(repeaterId, color);
146 scheduler.submit(() -> updatePropertyAndStates(repeaterData));
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);
160 logger.warn("Unsupported command: {}. Supported commands are: " + COMMAND_IDENTIFY,
165 case CHANNEL_REPEATER_BLINKING_ENABLED:
166 repeaterData = webTargets.enableRepeaterBlinking(repeaterId, OnOffType.ON == command);
167 scheduler.submit(() -> updatePropertyAndStates(repeaterData));
170 } catch (HubInvalidResponseException e) {
171 Throwable cause = e.getCause();
173 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
175 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
177 } catch (HubMaintenanceException e) {
178 // exceptions are logged in HDPowerViewWebTargets
179 } catch (HubException e) {
180 logger.warn("Unexpected error: {}", e.getMessage());
184 private void cancelResetIdentifyStateJob() {
185 ScheduledFuture<?> scheduledJob = resetIdentifyStateFuture;
186 if (scheduledJob != null) {
187 scheduledJob.cancel(true);
189 resetIdentifyStateFuture = null;
192 private void scheduleRefreshJob() {
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,
200 private void cancelRefreshJob() {
201 ScheduledFuture<?> future = this.refreshStatusFuture;
202 if (future != null) {
203 future.cancel(false);
205 this.refreshStatusFuture = null;
208 private synchronized void poll() {
209 HDPowerViewHubHandler bridge = getBridgeHandler();
210 if (bridge == null) {
211 logger.warn("Missing bridge handler");
214 HDPowerViewWebTargets webTargets = bridge.getWebTargets();
215 if (webTargets == null) {
216 logger.warn("Web targets not initialized");
220 logger.debug("Polling for status information");
222 RepeaterData repeaterData = webTargets.getRepeater(repeaterId);
223 updatePropertyAndStates(repeaterData);
225 } catch (HubInvalidResponseException e) {
226 Throwable cause = e.getCause();
228 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
230 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
232 } catch (HubMaintenanceException e) {
233 // exceptions are logged in HDPowerViewWebTargets
234 } catch (HubException e) {
235 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage());
239 private void updatePropertyAndStates(RepeaterData repeaterData) {
240 updateStatus(ThingStatus.ONLINE);
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());
247 logger.warn("Repeater firmware version missing in response");
250 Color color = repeaterData.color;
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));
257 updateState(CHANNEL_REPEATER_BLINKING_ENABLED, repeaterData.blinkEnabled ? OnOffType.ON : OnOffType.OFF);