2 * Copyright (c) 2010-2023 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.plugwiseha.internal.handler;
15 import static org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants.*;
16 import static org.openhab.core.thing.ThingStatus.OFFLINE;
17 import static org.openhab.core.thing.ThingStatus.ONLINE;
18 import static org.openhab.core.thing.ThingStatusDetail.*;
20 import java.util.Collection;
21 import java.util.Collections;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHACommunicationException;
30 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
31 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAInvalidHostException;
32 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHANotAuthorizedException;
33 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHATimeoutException;
34 import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAUnauthorizedException;
35 import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
36 import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAModel;
37 import org.openhab.binding.plugwiseha.internal.api.model.dto.GatewayInfo;
38 import org.openhab.binding.plugwiseha.internal.config.PlugwiseHABridgeThingConfig;
39 import org.openhab.binding.plugwiseha.internal.config.PlugwiseHAThingConfig;
40 import org.openhab.binding.plugwiseha.internal.discovery.PlugwiseHADiscoveryService;
41 import org.openhab.core.thing.Bridge;
42 import org.openhab.core.thing.ChannelUID;
43 import org.openhab.core.thing.Thing;
44 import org.openhab.core.thing.ThingStatus;
45 import org.openhab.core.thing.ThingTypeUID;
46 import org.openhab.core.thing.binding.BaseBridgeHandler;
47 import org.openhab.core.thing.binding.ThingHandler;
48 import org.openhab.core.thing.binding.ThingHandlerService;
49 import org.openhab.core.types.Command;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * The {@link PlugwiseHABridgeHandler} class is responsible for handling
55 * commands and status updates for the Plugwise Home Automation bridge.
56 * Extends @{link BaseBridgeHandler}
58 * @author Bas van Wetten - Initial contribution
59 * @author Leo Siepel - finish initial contribution
64 public class PlugwiseHABridgeHandler extends BaseBridgeHandler {
66 // Private Static error messages
68 private static final String STATUS_DESCRIPTION_COMMUNICATION_ERROR = "Error communicating with the Plugwise Home Automation controller";
69 private static final String STATUS_DESCRIPTION_TIMEOUT = "Communication timeout while communicating with the Plugwise Home Automation controller";
70 private static final String STATUS_DESCRIPTION_CONFIGURATION_ERROR = "Invalid or missing configuration";
71 private static final String STATUS_DESCRIPTION_INVALID_CREDENTIALS = "Invalid username and/or password - please double-check your configuration";
72 private static final String STATUS_DESCRIPTION_INVALID_HOSTNAME = "Invalid hostname - please double-check your configuration";
74 // Private member variables/constants
75 private @Nullable ScheduledFuture<?> refreshJob;
76 private @Nullable volatile PlugwiseHAController controller;
78 private final HttpClient httpClient;
79 private final Logger logger = LoggerFactory.getLogger(PlugwiseHABridgeHandler.class);
83 public PlugwiseHABridgeHandler(Bridge bridge, HttpClient httpClient) {
85 this.httpClient = httpClient;
91 public void initialize() {
92 PlugwiseHABridgeThingConfig bridgeConfig = getConfigAs(PlugwiseHABridgeThingConfig.class);
94 if (this.checkConfig(bridgeConfig)) {
95 logger.debug("Initializing the Plugwise Home Automation bridge handler with config = {}", bridgeConfig);
97 this.controller = new PlugwiseHAController(httpClient, bridgeConfig.getHost(), bridgeConfig.getPort(),
98 bridgeConfig.getUsername(), bridgeConfig.getsmileId(), bridgeConfig.getRefresh());
99 scheduleRefreshJob(bridgeConfig);
100 } catch (PlugwiseHAException e) {
101 updateStatus(OFFLINE, CONFIGURATION_ERROR, e.getMessage());
104 logger.warn("Invalid config for the Plugwise Home Automation bridge handler with config = {}",
110 public Collection<Class<? extends ThingHandlerService>> getServices() {
111 return Collections.singleton(PlugwiseHADiscoveryService.class);
115 public void handleCommand(ChannelUID channelUID, Command command) {
117 "Ignoring command = {} for channel = {} - this channel for the Plugwise Home Automation binding is read-only!",
118 command, channelUID);
122 public void dispose() {
124 if (this.controller != null) {
125 this.controller = null;
129 public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
130 return SUPPORTED_BRIDGE_TYPES_UIDS.contains(thingTypeUID);
135 public @Nullable PlugwiseHAController getController() {
136 return this.controller;
139 // Protected and private methods
142 * Checks the configuration for validity, result is reflected in the status of
145 private boolean checkConfig(PlugwiseHABridgeThingConfig bridgeConfig) {
146 if (!bridgeConfig.isValid()) {
147 updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_CONFIGURATION_ERROR);
154 private void scheduleRefreshJob(PlugwiseHABridgeThingConfig bridgeConfig) {
155 synchronized (this) {
156 if (this.refreshJob == null) {
157 logger.debug("Scheduling refresh job every {}s", bridgeConfig.getRefresh());
158 this.refreshJob = scheduler.scheduleWithFixedDelay(this::run, 0, bridgeConfig.getRefresh(),
166 this.logger.trace("Executing refresh job");
169 if (super.thing.getStatus() == ThingStatus.INITIALIZING) {
170 setBridgeProperties();
173 } catch (PlugwiseHAInvalidHostException e) {
174 updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_HOSTNAME);
175 } catch (PlugwiseHAUnauthorizedException | PlugwiseHANotAuthorizedException e) {
176 updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_CREDENTIALS);
177 } catch (PlugwiseHACommunicationException e) {
178 this.logger.trace("Bridge encountered an error {}", e.getMessage(), e);
179 updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
180 } catch (PlugwiseHATimeoutException e) {
181 this.logger.trace("Bridge encountered an error {}", e.getMessage(), e);
182 updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_TIMEOUT);
183 } catch (PlugwiseHAException e) {
184 this.logger.trace("Bridge encountered an error {}", e.getMessage(), e);
185 updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
186 } catch (RuntimeException e) {
187 this.logger.trace("Bridge encountered an error {}", e.getMessage(), e);
188 updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
192 @SuppressWarnings("unchecked")
193 private void refresh() throws PlugwiseHAException {
194 if (this.getController() != null) {
195 logger.debug("Refreshing the Plugwise Home Automation Controller {}", getThing().getUID());
197 PlugwiseHAController controller = this.getController();
198 if (controller != null) {
199 controller.refresh();
200 updateStatus(ONLINE);
203 getThing().getThings().forEach((thing) -> {
204 ThingHandler thingHandler = thing.getHandler();
205 if (thingHandler instanceof PlugwiseHABaseHandler) {
206 ((PlugwiseHABaseHandler<PlugwiseHAModel, PlugwiseHAThingConfig>) thingHandler).refresh();
212 @SuppressWarnings("null")
213 private void cancelRefreshJob() {
214 synchronized (this) {
215 if (this.refreshJob != null) {
216 logger.debug("Cancelling refresh job");
217 this.refreshJob.cancel(true);
218 this.refreshJob = null;
223 protected void setBridgeProperties() {
224 logger.debug("Setting bridge properties");
226 PlugwiseHAController controller = this.getController();
227 GatewayInfo localGatewayInfo = null;
228 if (controller != null) {
229 localGatewayInfo = controller.getGatewayInfo();
232 if (localGatewayInfo != null) {
233 Map<String, String> properties = editProperties();
234 if (localGatewayInfo.getFirmwareVersion() != null) {
235 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, localGatewayInfo.getFirmwareVersion());
237 if (localGatewayInfo.getHardwareVersion() != null) {
238 properties.put(Thing.PROPERTY_HARDWARE_VERSION, localGatewayInfo.getHardwareVersion());
240 if (localGatewayInfo.getMacAddress() != null) {
241 properties.put(Thing.PROPERTY_MAC_ADDRESS, localGatewayInfo.getMacAddress());
243 if (localGatewayInfo.getVendorName() != null) {
244 properties.put(Thing.PROPERTY_VENDOR, localGatewayInfo.getVendorName());
246 if (localGatewayInfo.getVendorModel() != null) {
247 properties.put(Thing.PROPERTY_MODEL_ID, localGatewayInfo.getVendorModel());
250 updateProperties(properties);
252 } catch (PlugwiseHAException e) {
253 updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);