2 * Copyright (c) 2010-2021 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.nuki.internal.handler;
15 import java.util.List;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
19 import org.eclipse.jetty.client.HttpClient;
20 import org.eclipse.jetty.http.HttpStatus;
21 import org.openhab.binding.nuki.internal.NukiBindingConstants;
22 import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackAddResponse;
23 import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackListResponse;
24 import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackRemoveResponse;
25 import org.openhab.binding.nuki.internal.dataexchange.BridgeInfoResponse;
26 import org.openhab.binding.nuki.internal.dataexchange.NukiHttpClient;
27 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListCallbackDto;
28 import org.openhab.core.config.core.Configuration;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseBridgeHandler;
34 import org.openhab.core.types.Command;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * The {@link NukiBridgeHandler} is responsible for handling commands, which are
40 * sent to one of the channels.
42 * @author Markus Katter - Initial contribution
44 public class NukiBridgeHandler extends BaseBridgeHandler {
46 private final Logger logger = LoggerFactory.getLogger(NukiBridgeHandler.class);
47 private static final int JOB_INTERVAL = 600;
49 private HttpClient httpClient;
50 private NukiHttpClient nukiHttpClient;
51 private String callbackUrl;
52 private ScheduledFuture<?> checkBridgeOnlineJob;
53 private String bridgeIp;
54 private boolean manageCallbacks;
55 private boolean initializable;
57 public NukiBridgeHandler(Bridge bridge, HttpClient httpClient, String callbackUrl) {
59 logger.debug("Instantiating NukiBridgeHandler({}, {}, {})", bridge, httpClient, callbackUrl);
60 this.httpClient = httpClient;
61 this.callbackUrl = callbackUrl;
62 this.initializable = getConfig().get(NukiBindingConstants.CONFIG_IP) != null
63 && getConfig().get(NukiBindingConstants.CONFIG_API_TOKEN) != null;
66 public NukiHttpClient getNukiHttpClient() {
67 if (nukiHttpClient == null) {
68 nukiHttpClient = new NukiHttpClient(httpClient, getConfig());
70 return nukiHttpClient;
73 public boolean isInitializable() {
78 public void initialize() {
79 logger.debug("initialize() for Bridge[{}].", getThing().getUID());
80 Configuration config = getConfig();
81 bridgeIp = (String) config.get(NukiBindingConstants.CONFIG_IP);
82 manageCallbacks = (Boolean) config.get(NukiBindingConstants.CONFIG_MANAGECB);
83 if (bridgeIp == null) {
84 logger.debug("NukiBridgeHandler[{}] is not initializable, IP setting is unset in the configuration!",
86 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP setting is unset");
87 } else if (config.get(NukiBindingConstants.CONFIG_API_TOKEN) == null) {
88 logger.debug("NukiBridgeHandler[{}] is not initializable, apiToken setting is unset in the configuration!",
90 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "apiToken setting is unset");
92 scheduler.execute(this::initializeHandler);
93 checkBridgeOnlineJob = scheduler.scheduleWithFixedDelay(this::checkBridgeOnline, JOB_INTERVAL, JOB_INTERVAL,
99 public void handleCommand(ChannelUID channelUID, Command command) {
100 logger.debug("handleCommand({}, {}) for Bridge[{}] not implemented!", channelUID, command, bridgeIp);
104 public void dispose() {
105 logger.debug("dispose() for Bridge[{}].", getThing().getUID());
106 nukiHttpClient = null;
107 if (checkBridgeOnlineJob != null && !checkBridgeOnlineJob.isCancelled()) {
108 checkBridgeOnlineJob.cancel(true);
110 checkBridgeOnlineJob = null;
113 private synchronized void initializeHandler() {
114 logger.debug("initializeHandler() for Bridge[{}].", bridgeIp);
115 BridgeInfoResponse bridgeInfoResponse = getNukiHttpClient().getBridgeInfo();
116 if (bridgeInfoResponse.getStatus() == HttpStatus.OK_200) {
117 if (manageCallbacks) {
118 manageNukiBridgeCallbacks();
120 logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge online.", bridgeIp,
121 bridgeInfoResponse.getStatus());
122 updateStatus(ThingStatus.ONLINE);
124 logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge offline!", bridgeIp,
125 bridgeInfoResponse.getStatus());
126 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, bridgeInfoResponse.getMessage());
130 private void checkBridgeOnline() {
131 logger.debug("checkBridgeOnline():bridgeIp[{}] status[{}]", bridgeIp, getThing().getStatus());
132 if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
133 logger.debug("Requesting BridgeInfo to ensure Bridge[{}] is online.", bridgeIp);
134 BridgeInfoResponse bridgeInfoResponse = getNukiHttpClient().getBridgeInfo();
135 int status = bridgeInfoResponse.getStatus();
136 if (status == HttpStatus.OK_200) {
137 logger.debug("Bridge[{}] responded with status[{}]. Bridge is online.", bridgeIp, status);
138 } else if (status == HttpStatus.SERVICE_UNAVAILABLE_503) {
140 "Bridge[{}] responded with status[{}]. REST service seems to be busy but Bridge is online.",
143 logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge offline!", bridgeIp, status);
144 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
145 bridgeInfoResponse.getMessage());
152 private void manageNukiBridgeCallbacks() {
153 logger.debug("manageNukiBridgeCallbacks() for Bridge[{}].", bridgeIp);
154 BridgeCallbackListResponse bridgeCallbackListResponse = getNukiHttpClient().getBridgeCallbackList();
155 List<BridgeApiCallbackListCallbackDto> callbacks = bridgeCallbackListResponse.getCallbacks();
156 boolean callbackExists = false;
157 int callbackCount = callbacks == null ? 0 : callbacks.size();
158 if (callbacks != null) {
159 for (BridgeApiCallbackListCallbackDto callback : callbacks) {
160 if (callback.getUrl().equals(callbackUrl)) {
161 logger.debug("callbackUrl[{}] already existing on Bridge[{}].", callbackUrl, bridgeIp);
162 callbackExists = true;
165 if (callback.getUrl().contains(NukiBindingConstants.CALLBACK_ENDPOINT)) {
166 logger.debug("Partial callbackUrl[{}] found on Bridge[{}] - Removing it!", callbackUrl, bridgeIp);
167 BridgeCallbackRemoveResponse bridgeCallbackRemoveResponse = getNukiHttpClient()
168 .getBridgeCallbackRemove(callback.getId());
169 if (bridgeCallbackRemoveResponse.getStatus() == HttpStatus.OK_200) {
170 logger.debug("Successfully removed callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
176 if (!callbackExists) {
177 if (callbackCount == 3) {
178 logger.debug("Already 3 callback URLs existing on Bridge[{}] - Removing ID 0!", bridgeIp);
179 BridgeCallbackRemoveResponse bridgeCallbackRemoveResponse = getNukiHttpClient()
180 .getBridgeCallbackRemove(0);
181 if (bridgeCallbackRemoveResponse.getStatus() == HttpStatus.OK_200) {
182 logger.debug("Successfully removed callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
186 logger.debug("Adding callbackUrl[{}] to Bridge[{}]!", callbackUrl, bridgeIp);
187 BridgeCallbackAddResponse bridgeCallbackAddResponse = getNukiHttpClient().getBridgeCallbackAdd(callbackUrl);
188 if (bridgeCallbackAddResponse.getStatus() == HttpStatus.OK_200) {
189 logger.debug("Successfully added callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
190 callbackExists = true;