]> git.basschouten.com Git - openhab-addons.git/blob
4ddfc455692b2483d1463d75dcf793969ff9b16e
[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.weatherunderground.internal.handler;
14
15 import java.io.IOException;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.weatherunderground.internal.WeatherUndergroundBindingConstants;
22 import org.openhab.binding.weatherunderground.internal.json.WeatherUndergroundJsonData;
23 import org.openhab.core.config.core.Configuration;
24 import org.openhab.core.io.net.http.HttpUtil;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.ThingStatus;
28 import org.openhab.core.thing.ThingStatusDetail;
29 import org.openhab.core.thing.binding.BaseBridgeHandler;
30 import org.openhab.core.types.Command;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.google.gson.Gson;
35 import com.google.gson.JsonSyntaxException;
36
37 /**
38  * The {@link WeatherUndergroundBridgeHandler} is responsible for handling the
39  * bridge things created to use the Weather Underground Service. This way, the
40  * API key may be entered only once.
41  *
42  * @author Theo Giovanna - Initial Contribution
43  * @author Laurent Garnier - refactor bridge/thing handling
44  */
45 @NonNullByDefault
46 public class WeatherUndergroundBridgeHandler extends BaseBridgeHandler {
47
48     private final Logger logger = LoggerFactory.getLogger(WeatherUndergroundBridgeHandler.class);
49     private final Gson gson;
50     private static final String URL = "http://api.wunderground.com/api/%APIKEY%/";
51     public static final int FETCH_TIMEOUT_MS = 30000;
52
53     @Nullable
54     private ScheduledFuture<?> controlApiKeyJob;
55
56     private String apikey = "";
57
58     public WeatherUndergroundBridgeHandler(Bridge bridge) {
59         super(bridge);
60         gson = new Gson();
61     }
62
63     @Override
64     public void initialize() {
65         logger.debug("Initializing weatherunderground bridge handler.");
66         Configuration config = getThing().getConfiguration();
67
68         // Check if an api key has been provided during the bridge creation
69         Object configApiKey = config.get(WeatherUndergroundBindingConstants.APIKEY);
70         if (!(configApiKey instanceof String) || ((String) configApiKey).trim().isEmpty()) {
71             logger.debug("Setting thing '{}' to OFFLINE: Parameter 'apikey' must be configured.", getThing().getUID());
72             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
73                     "@text/offline.conf-error-missing-apikey");
74         } else {
75             apikey = ((String) configApiKey).trim();
76             updateStatus(ThingStatus.UNKNOWN);
77             startControlApiKeyJob();
78         }
79     }
80
81     /**
82      * Start the job controlling the API key
83      */
84     private void startControlApiKeyJob() {
85         if (controlApiKeyJob == null || controlApiKeyJob.isCancelled()) {
86             Runnable runnable = new Runnable() {
87                 @Override
88                 public void run() {
89                     WeatherUndergroundJsonData result = null;
90                     String errorDetail = null;
91                     String error = null;
92                     String statusDescr = null;
93                     boolean resultOk = false;
94
95                     // Check if the provided api key is valid for use with the weatherunderground service
96                     try {
97                         String urlStr = URL.replace("%APIKEY%", getApikey());
98                         // Run the HTTP request and get the JSON response from Weather Underground
99                         String response = null;
100                         try {
101                             response = HttpUtil.executeUrl("GET", urlStr, FETCH_TIMEOUT_MS);
102                             logger.debug("apiResponse = {}", response);
103                         } catch (IllegalArgumentException e) {
104                             // Catch Illegal character in path at index XX: http://api.wunderground.com/...
105                             error = "Error creating URI";
106                             errorDetail = e.getMessage();
107                             statusDescr = "@text/offline.uri-error";
108                         }
109                         // Map the JSON response to an object
110                         result = gson.fromJson(response, WeatherUndergroundJsonData.class);
111                         if (result.getResponse() == null) {
112                             error = "Error in Weather Underground response";
113                             errorDetail = "missing response sub-object";
114                             statusDescr = "@text/offline.comm-error-response";
115                         } else if (result.getResponse().getErrorDescription() != null) {
116                             if ("keynotfound".equals(result.getResponse().getErrorType())) {
117                                 error = "API key has to be fixed";
118                                 errorDetail = result.getResponse().getErrorDescription();
119                                 statusDescr = "@text/offline.comm-error-invalid-api-key";
120                             } else if ("invalidquery".equals(result.getResponse().getErrorType())) {
121                                 // The API key provided is valid
122                                 resultOk = true;
123                             } else {
124                                 error = "Error in Weather Underground response";
125                                 errorDetail = result.getResponse().getErrorDescription();
126                                 statusDescr = "@text/offline.comm-error-response";
127                             }
128                         } else {
129                             resultOk = true;
130                         }
131                     } catch (IOException e) {
132                         error = "Error running Weather Underground request";
133                         errorDetail = e.getMessage();
134                         statusDescr = "@text/offline.comm-error-running-request";
135                     } catch (JsonSyntaxException e) {
136                         error = "Error parsing Weather Underground response";
137                         errorDetail = e.getMessage();
138                         statusDescr = "@text/offline.comm-error-parsing-response";
139                     }
140
141                     // Update the thing status
142                     if (resultOk) {
143                         updateStatus(ThingStatus.ONLINE);
144                     } else {
145                         logger.debug("Setting thing '{}' to OFFLINE: Error '{}': {}", getThing().getUID(), error,
146                                 errorDetail);
147                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, statusDescr);
148                     }
149                 }
150             };
151             controlApiKeyJob = scheduler.schedule(runnable, 1, TimeUnit.SECONDS);
152         }
153     }
154
155     @Override
156     public void dispose() {
157         logger.debug("Disposing weatherunderground bridge handler.");
158
159         if (controlApiKeyJob != null && !controlApiKeyJob.isCancelled()) {
160             controlApiKeyJob.cancel(true);
161             controlApiKeyJob = null;
162         }
163     }
164
165     @Override
166     public void handleCommand(ChannelUID channelUID, Command command) {
167         // not needed
168     }
169
170     public String getApikey() {
171         return apikey;
172     }
173 }