]> git.basschouten.com Git - openhab-addons.git/blob
108e5b9744c9983408cf0c5facce9f5894076e68
[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.plugwiseha.internal.handler;
14
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.*;
19
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Map;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
25
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;
52
53 /**
54  * The {@link PlugwiseHABridgeHandler} class is responsible for handling
55  * commands and status updates for the Plugwise Home Automation bridge.
56  * Extends @{link BaseBridgeHandler}
57  *
58  * @author Bas van Wetten - Initial contribution
59  * @author Leo Siepel - finish initial contribution
60  * 
61  */
62
63 @NonNullByDefault
64 public class PlugwiseHABridgeHandler extends BaseBridgeHandler {
65
66     // Private Static error messages
67
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";
73
74     // Private member variables/constants
75     private @Nullable ScheduledFuture<?> refreshJob;
76     private @Nullable volatile PlugwiseHAController controller;
77
78     private final HttpClient httpClient;
79     private final Logger logger = LoggerFactory.getLogger(PlugwiseHABridgeHandler.class);
80
81     // Constructor
82
83     public PlugwiseHABridgeHandler(Bridge bridge, HttpClient httpClient) {
84         super(bridge);
85         this.httpClient = httpClient;
86     }
87
88     // Public methods
89
90     @Override
91     public void initialize() {
92         PlugwiseHABridgeThingConfig bridgeConfig = getConfigAs(PlugwiseHABridgeThingConfig.class);
93
94         if (this.checkConfig(bridgeConfig)) {
95             logger.debug("Initializing the Plugwise Home Automation bridge handler with config = {}", bridgeConfig);
96             try {
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());
102             }
103         } else {
104             logger.warn("Invalid config for the Plugwise Home Automation bridge handler with config = {}",
105                     bridgeConfig);
106         }
107     }
108
109     @Override
110     public Collection<Class<? extends ThingHandlerService>> getServices() {
111         return Collections.singleton(PlugwiseHADiscoveryService.class);
112     }
113
114     @Override
115     public void handleCommand(ChannelUID channelUID, Command command) {
116         this.logger.warn(
117                 "Ignoring command = {} for channel = {} - this channel for the Plugwise Home Automation binding is read-only!",
118                 command, channelUID);
119     }
120
121     @Override
122     public void dispose() {
123         cancelRefreshJob();
124         if (this.controller != null) {
125             this.controller = null;
126         }
127     }
128
129     public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
130         return SUPPORTED_BRIDGE_TYPES_UIDS.contains(thingTypeUID);
131     }
132
133     // Getters & setters
134
135     public @Nullable PlugwiseHAController getController() {
136         return this.controller;
137     }
138
139     // Protected and private methods
140
141     /**
142      * Checks the configuration for validity, result is reflected in the status of
143      * the Thing
144      */
145     private boolean checkConfig(PlugwiseHABridgeThingConfig bridgeConfig) {
146         if (!bridgeConfig.isValid()) {
147             updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_CONFIGURATION_ERROR);
148             return false;
149         } else {
150             return true;
151         }
152     }
153
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(),
159                         TimeUnit.SECONDS);
160             }
161         }
162     }
163
164     private void run() {
165         try {
166             this.logger.trace("Executing refresh job");
167             refresh();
168
169             if (super.thing.getStatus() == ThingStatus.INITIALIZING) {
170                 setBridgeProperties();
171             }
172
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());
189         }
190     }
191
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());
196
197             PlugwiseHAController controller = this.getController();
198             if (controller != null) {
199                 controller.refresh();
200                 updateStatus(ONLINE);
201             }
202
203             getThing().getThings().forEach((thing) -> {
204                 ThingHandler thingHandler = thing.getHandler();
205                 if (thingHandler instanceof PlugwiseHABaseHandler) {
206                     ((PlugwiseHABaseHandler<PlugwiseHAModel, PlugwiseHAThingConfig>) thingHandler).refresh();
207                 }
208             });
209         }
210     }
211
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;
219             }
220         }
221     }
222
223     protected void setBridgeProperties() {
224         logger.debug("Setting bridge properties");
225         try {
226             PlugwiseHAController controller = this.getController();
227             GatewayInfo localGatewayInfo = null;
228             if (controller != null) {
229                 localGatewayInfo = controller.getGatewayInfo();
230             }
231
232             if (localGatewayInfo != null) {
233                 Map<String, String> properties = editProperties();
234                 if (localGatewayInfo.getFirmwareVersion() != null) {
235                     properties.put(Thing.PROPERTY_FIRMWARE_VERSION, localGatewayInfo.getFirmwareVersion());
236                 }
237                 if (localGatewayInfo.getHardwareVersion() != null) {
238                     properties.put(Thing.PROPERTY_HARDWARE_VERSION, localGatewayInfo.getHardwareVersion());
239                 }
240                 if (localGatewayInfo.getMacAddress() != null) {
241                     properties.put(Thing.PROPERTY_MAC_ADDRESS, localGatewayInfo.getMacAddress());
242                 }
243                 if (localGatewayInfo.getVendorName() != null) {
244                     properties.put(Thing.PROPERTY_VENDOR, localGatewayInfo.getVendorName());
245                 }
246                 if (localGatewayInfo.getVendorModel() != null) {
247                     properties.put(Thing.PROPERTY_MODEL_ID, localGatewayInfo.getVendorModel());
248                 }
249
250                 updateProperties(properties);
251             }
252         } catch (PlugwiseHAException e) {
253             updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
254         }
255     }
256 }