]> git.basschouten.com Git - openhab-addons.git/blob
5716b69edd8ec7660c1923e4e51cc42dc10f05d7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.openuv.internal.handler;
14
15 import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.BASE_URL;
16
17 import java.io.IOException;
18 import java.time.Duration;
19 import java.time.LocalDate;
20 import java.time.LocalDateTime;
21 import java.time.ZonedDateTime;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Properties;
25 import java.util.concurrent.TimeUnit;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.openuv.internal.OpenUVBindingConstants;
31 import org.openhab.binding.openuv.internal.json.OpenUVResponse;
32 import org.openhab.binding.openuv.internal.json.OpenUVResult;
33 import org.openhab.core.config.core.Configuration;
34 import org.openhab.core.io.net.http.HttpUtil;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.ThingStatusDetail;
40 import org.openhab.core.thing.ThingUID;
41 import org.openhab.core.thing.binding.BaseBridgeHandler;
42 import org.openhab.core.types.Command;
43 import org.openhab.core.types.RefreshType;
44 import org.osgi.framework.ServiceRegistration;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import com.google.gson.FieldNamingPolicy;
49 import com.google.gson.Gson;
50 import com.google.gson.GsonBuilder;
51 import com.google.gson.JsonDeserializer;
52
53 /**
54  * {@link OpenUVBridgeHandler} is the handler for OpenUV API and connects it
55  * to the webservice.
56  *
57  * @author GaĆ«l L'hopital - Initial contribution
58  *
59  */
60 @NonNullByDefault
61 public class OpenUVBridgeHandler extends BaseBridgeHandler {
62     private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class);
63     private static final String ERROR_QUOTA_EXCEEDED = "Daily API quota exceeded";
64     private static final String ERROR_WRONG_KEY = "User with API Key not found";
65
66     private static final int REQUEST_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30);
67
68     private final Gson gson = new GsonBuilder()
69             .registerTypeAdapter(DecimalType.class,
70                     (JsonDeserializer<DecimalType>) (json, type, jsonDeserializationContext) -> DecimalType
71                             .valueOf(json.getAsJsonPrimitive().getAsString()))
72             .registerTypeAdapter(ZonedDateTime.class,
73                     (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
74                             .parse(json.getAsJsonPrimitive().getAsString()))
75             .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
76
77     private Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
78     private final Properties header = new Properties();
79
80     public OpenUVBridgeHandler(Bridge bridge) {
81         super(bridge);
82     }
83
84     @Override
85     public void initialize() {
86         logger.debug("Initializing OpenUV API bridge handler.");
87         Configuration config = getThing().getConfiguration();
88         String apiKey = (String) config.get(OpenUVBindingConstants.APIKEY);
89         if (StringUtils.trimToNull(apiKey) == null) {
90             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
91                     "Parameter 'apikey' must be configured.");
92         } else {
93             header.put("x-access-token", apiKey);
94             initiateConnexion();
95         }
96     }
97
98     @Override
99     public void handleCommand(ChannelUID channelUID, Command command) {
100         if (command instanceof RefreshType) {
101             initiateConnexion();
102         } else {
103             logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command);
104         }
105     }
106
107     private void initiateConnexion() {
108         // Check if the provided api key is valid for use with the OpenUV service
109         getUVData("0", "0", null);
110     }
111
112     public Map<ThingUID, @Nullable ServiceRegistration<?>> getDiscoveryServiceRegs() {
113         return discoveryServiceRegs;
114     }
115
116     public void setDiscoveryServiceRegs(Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs) {
117         this.discoveryServiceRegs = discoveryServiceRegs;
118     }
119
120     @Override
121     public void handleRemoval() {
122         // removes the old registration service associated to the bridge, if existing
123         ServiceRegistration<?> dis = getDiscoveryServiceRegs().get(getThing().getUID());
124         if (dis != null) {
125             dis.unregister();
126         }
127         super.handleRemoval();
128     }
129
130     public @Nullable OpenUVResult getUVData(String latitude, String longitude, @Nullable String altitude) {
131         StringBuilder urlBuilder = new StringBuilder(BASE_URL).append("?lat=").append(latitude).append("&lng=")
132                 .append(longitude);
133
134         if (altitude != null) {
135             urlBuilder.append("&alt=").append(altitude);
136         }
137         String errorMessage = null;
138         try {
139             String jsonData = HttpUtil.executeUrl("GET", urlBuilder.toString(), header, null, null, REQUEST_TIMEOUT);
140             OpenUVResponse uvResponse = gson.fromJson(jsonData, OpenUVResponse.class);
141             if (uvResponse.getError() == null) {
142                 updateStatus(ThingStatus.ONLINE);
143                 return uvResponse.getResult();
144             } else {
145                 errorMessage = uvResponse.getError();
146             }
147         } catch (IOException e) {
148             errorMessage = e.getMessage();
149         }
150
151         if (errorMessage.startsWith(ERROR_QUOTA_EXCEEDED)) {
152             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage);
153             LocalDate today = LocalDate.now();
154             LocalDate tomorrow = today.plusDays(1);
155             LocalDateTime tomorrowMidnight = tomorrow.atStartOfDay().plusMinutes(2);
156
157             logger.warn("Quota Exceeded, going OFFLINE for today, will retry at : {} ", tomorrowMidnight);
158             scheduler.schedule(this::initiateConnexion,
159                     Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES);
160
161         } else if (errorMessage.startsWith(ERROR_WRONG_KEY)) {
162             logger.error("Error occured during API query : {}", errorMessage);
163             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage);
164         }
165         return null;
166     }
167 }