diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java index 4a1e887fe0f..9383e870979 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java @@ -28,13 +28,9 @@ import org.openhab.core.thing.ThingTypeUID; */ @NonNullByDefault public class OpenUVBindingConstants { - public static final String BASE_URL = "https://api.openuv.io/api/v1/uv"; public static final String BINDING_ID = "openuv"; public static final String LOCAL = "local"; - public static final String LOCATION = "location"; - public static final String APIKEY = "apikey"; - // List of Bridge Type UIDs public static final ThingTypeUID APIBRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "openuvapi"); diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVException.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVException.java new file mode 100644 index 00000000000..e51aeb42c27 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVException.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Will be thrown for cloud errors + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class OpenUVException extends Exception { + private static final long serialVersionUID = -1411477662081482350L; + private static final String ERROR_QUOTA_EXCEEDED = "Daily API quota exceeded"; + private static final String ERROR_WRONG_KEY = "User with API Key not found"; + + public OpenUVException(String message) { + super(message); + } + + public boolean isApiKeyError() { + return this.getMessage().startsWith(ERROR_WRONG_KEY); + } + + public boolean isQuotaError() { + return this.getMessage().startsWith(ERROR_QUOTA_EXCEEDED); + } +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVHandlerFactory.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVHandlerFactory.java index c498e14d123..3e66d08db83 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVHandlerFactory.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVHandlerFactory.java @@ -14,15 +14,15 @@ package org.openhab.binding.openuv.internal; import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*; -import java.util.Hashtable; +import java.time.ZonedDateTime; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.openuv.internal.discovery.OpenUVDiscoveryService; import org.openhab.binding.openuv.internal.handler.OpenUVBridgeHandler; import org.openhab.binding.openuv.internal.handler.OpenUVReportHandler; -import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.i18n.LocationProvider; +import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -33,6 +33,11 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializer; + /** * The {@link OpenUVHandlerFactory} is responsible for creating things and thing * handlers. @@ -44,10 +49,21 @@ import org.osgi.service.component.annotations.Reference; public class OpenUVHandlerFactory extends BaseThingHandlerFactory { private final LocationProvider locationProvider; + private final Gson gson; @Activate - public OpenUVHandlerFactory(@Reference LocationProvider locationProvider) { + public OpenUVHandlerFactory(@Reference TimeZoneProvider timeZoneProvider, + @Reference LocationProvider locationProvider) { this.locationProvider = locationProvider; + this.gson = new GsonBuilder() + .registerTypeAdapter(DecimalType.class, + (JsonDeserializer) (json, type, jsonDeserializationContext) -> DecimalType + .valueOf(json.getAsJsonPrimitive().getAsString())) + .registerTypeAdapter(ZonedDateTime.class, + (JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime + .parse(json.getAsJsonPrimitive().getAsString()) + .withZoneSameInstant(timeZoneProvider.getTimeZone())) + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); } @Override @@ -59,20 +75,8 @@ public class OpenUVHandlerFactory extends BaseThingHandlerFactory { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - if (APIBRIDGE_THING_TYPE.equals(thingTypeUID)) { - OpenUVBridgeHandler handler = new OpenUVBridgeHandler((Bridge) thing); - registerOpenUVDiscoveryService(handler); - return handler; - } else if (LOCATION_REPORT_THING_TYPE.equals(thingTypeUID)) { - return new OpenUVReportHandler(thing); - } - - return null; - } - - private void registerOpenUVDiscoveryService(OpenUVBridgeHandler bridgeHandler) { - OpenUVDiscoveryService discoveryService = new OpenUVDiscoveryService(bridgeHandler, locationProvider); - bridgeHandler.getDiscoveryServiceRegs().put(bridgeHandler.getThing().getUID(), - bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>())); + return APIBRIDGE_THING_TYPE.equals(thingTypeUID) + ? new OpenUVBridgeHandler((Bridge) thing, locationProvider, gson) + : LOCATION_REPORT_THING_TYPE.equals(thingTypeUID) ? new OpenUVReportHandler(thing) : null; } } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java deleted file mode 100644 index 00d933b3839..00000000000 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openuv.internal; - -/** - * The {@link ReportConfiguration} is the class used to match the - * thing configuration. - * - * @author Gaël L"hopital - Initial contribution - */ -public class ReportConfiguration { - String[] elements = null; - - private String location; - public Integer refresh; - - public String getLatitude() { - return getElement(0); - } - - public String getLongitude() { - return getElement(1); - } - - public String getAltitude() { - return getElement(2); - } - - private String getElement(int index) { - if (elements == null) { - elements = location.split(","); - } - if (index < elements.length) { - return elements[index].trim(); - } else { - return null; - } - } -} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/BridgeConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/BridgeConfiguration.java new file mode 100644 index 00000000000..745603bfcc5 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/BridgeConfiguration.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link BridgeConfiguration} is the class used to match the + * bridge configuration. + * + * @author Gaël L"hopital - Initial contribution + */ +@NonNullByDefault +public class BridgeConfiguration { + public String apikey = ""; +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/ReportConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/ReportConfiguration.java new file mode 100644 index 00000000000..56ac8728786 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/ReportConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ReportConfiguration} is the class used to match the + * thing configuration. + * + * @author Gaël L"hopital - Initial contribution + */ +@NonNullByDefault +public class ReportConfiguration { + public static final String LOCATION = "location"; + + public int refresh = 10; + public String location = ""; +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/SafeExposureConfiguration.java similarity index 83% rename from bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java rename to bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/SafeExposureConfiguration.java index 5a4514c058c..a78eb3fcfaf 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/config/SafeExposureConfiguration.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.openuv.internal; +package org.openhab.binding.openuv.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; /** * The {@link SafeExposureConfiguration} is the class used to match the @@ -18,6 +20,7 @@ package org.openhab.binding.openuv.internal; * * @author Gaël L"hopital - Initial contribution */ +@NonNullByDefault public class SafeExposureConfiguration { public int index = -1; } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java index 4494c4e5212..25e595e2186 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java @@ -14,21 +14,16 @@ package org.openhab.binding.openuv.internal.discovery; import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.openuv.internal.config.ReportConfiguration; import org.openhab.binding.openuv.internal.handler.OpenUVBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; -import org.openhab.core.i18n.LocationProvider; import org.openhab.core.library.types.PointType; import org.openhab.core.thing.ThingUID; -import org.osgi.service.component.annotations.Modified; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,83 +33,54 @@ import org.slf4j.LoggerFactory; * @author Gaël L'hopital - Initial Contribution */ @NonNullByDefault -public class OpenUVDiscoveryService extends AbstractDiscoveryService { +public class OpenUVDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { private final Logger logger = LoggerFactory.getLogger(OpenUVDiscoveryService.class); - private static final int DISCOVER_TIMEOUT_SECONDS = 10; - private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60; + private static final int DISCOVER_TIMEOUT_SECONDS = 2; - private final LocationProvider locationProvider; - private final OpenUVBridgeHandler bridgeHandler; - private @Nullable ScheduledFuture discoveryJob; - private @Nullable PointType previousLocation; + private @Nullable OpenUVBridgeHandler bridgeHandler; /** * Creates a OpenUVDiscoveryService with enabled autostart. */ - public OpenUVDiscoveryService(OpenUVBridgeHandler bridgeHandler, LocationProvider locationProvider) { - super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, true); - this.locationProvider = locationProvider; - this.bridgeHandler = bridgeHandler; + public OpenUVDiscoveryService() { + super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS); } @Override - protected void activate(@Nullable Map configProperties) { - super.activate(configProperties); + public void setThingHandler(ThingHandler handler) { + if (handler instanceof OpenUVBridgeHandler) { + this.bridgeHandler = (OpenUVBridgeHandler) handler; + } } @Override - @Modified - protected void modified(@Nullable Map configProperties) { - super.modified(configProperties); + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void deactivate() { + super.deactivate(); } @Override protected void startScan() { logger.debug("Starting OpenUV discovery scan"); - PointType location = locationProvider.getLocation(); - if (location == null) { - logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results"); - return; - } - createResults(location); - } - - @Override - protected void startBackgroundDiscovery() { - if (discoveryJob == null) { - discoveryJob = scheduler.scheduleWithFixedDelay(() -> { - PointType currentLocation = locationProvider.getLocation(); - if (currentLocation != null && !Objects.equals(currentLocation, previousLocation)) { - logger.debug("Location has been changed from {} to {}: Creating new discovery results", - previousLocation, currentLocation); - createResults(currentLocation); - previousLocation = currentLocation; - } - }, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS); - logger.debug("Scheduled OpenUV-changed job every {} seconds", LOCATION_CHANGED_CHECK_INTERVAL); - } - } - - public void createResults(PointType location) { - ThingUID bridgeUID = bridgeHandler.getThing().getUID(); - ThingUID localOpenUVThing = new ThingUID(LOCATION_REPORT_THING_TYPE, bridgeUID, LOCAL); - Map properties = new HashMap<>(); - properties.put(LOCATION, location.toString()); - thingDiscovered(DiscoveryResultBuilder.create(localOpenUVThing).withLabel("Local UV Information") - .withProperties(properties).withRepresentationProperty(location.toString()).withBridge(bridgeUID) - .build()); - } - - @SuppressWarnings("null") - @Override - protected void stopBackgroundDiscovery() { - logger.debug("Stopping OpenUV background discovery"); - if (discoveryJob != null && !discoveryJob.isCancelled()) { - if (discoveryJob.cancel(true)) { - discoveryJob = null; - logger.debug("Stopped OpenUV background discovery"); + OpenUVBridgeHandler bridge = bridgeHandler; + if (bridge != null) { + PointType location = bridge.getLocation(); + if (location != null) { + ThingUID bridgeUID = bridge.getThing().getUID(); + thingDiscovered(DiscoveryResultBuilder + .create(new ThingUID(LOCATION_REPORT_THING_TYPE, bridgeUID, LOCAL)).withLabel("Local UV Report") + .withProperty(ReportConfiguration.LOCATION, location.toString()) + .withRepresentationProperty(ReportConfiguration.LOCATION).withBridge(bridgeUID).build()); + } else { + logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results"); } + } else { + logger.debug("OpenUV Bridge Handler is not set -> Will not provide any discovery results"); } } } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java index 5716b69edd8..b3fb8a1614c 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java @@ -12,43 +12,38 @@ */ package org.openhab.binding.openuv.internal.handler; -import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.BASE_URL; - import java.io.IOException; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; +import java.util.Collection; +import java.util.Collections; import java.util.Properties; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.openuv.internal.OpenUVBindingConstants; +import org.openhab.binding.openuv.internal.OpenUVException; +import org.openhab.binding.openuv.internal.config.BridgeConfiguration; +import org.openhab.binding.openuv.internal.discovery.OpenUVDiscoveryService; import org.openhab.binding.openuv.internal.json.OpenUVResponse; import org.openhab.binding.openuv.internal.json.OpenUVResult; -import org.openhab.core.config.core.Configuration; +import org.openhab.core.i18n.LocationProvider; import org.openhab.core.io.net.http.HttpUtil; -import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PointType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; -import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; -import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializer; /** * {@link OpenUVBridgeHandler} is the handler for OpenUV API and connects it @@ -60,41 +55,45 @@ import com.google.gson.JsonDeserializer; @NonNullByDefault public class OpenUVBridgeHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class); - private static final String ERROR_QUOTA_EXCEEDED = "Daily API quota exceeded"; - private static final String ERROR_WRONG_KEY = "User with API Key not found"; - private static final int REQUEST_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30); + private static final String QUERY_URL = "https://api.openuv.io/api/v1/uv?lat=%s&lng=%s&alt=%s"; - private final Gson gson = new GsonBuilder() - .registerTypeAdapter(DecimalType.class, - (JsonDeserializer) (json, type, jsonDeserializationContext) -> DecimalType - .valueOf(json.getAsJsonPrimitive().getAsString())) - .registerTypeAdapter(ZonedDateTime.class, - (JsonDeserializer) (json, type, jsonDeserializationContext) -> ZonedDateTime - .parse(json.getAsJsonPrimitive().getAsString())) - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30); - private Map> discoveryServiceRegs = new HashMap<>(); private final Properties header = new Properties(); + private final Gson gson; - public OpenUVBridgeHandler(Bridge bridge) { + private final LocationProvider locationProvider; + private @Nullable ScheduledFuture reconnectJob; + + public OpenUVBridgeHandler(Bridge bridge, LocationProvider locationProvider, Gson gson) { super(bridge); + this.gson = gson; + this.locationProvider = locationProvider; } @Override public void initialize() { logger.debug("Initializing OpenUV API bridge handler."); - Configuration config = getThing().getConfiguration(); - String apiKey = (String) config.get(OpenUVBindingConstants.APIKEY); - if (StringUtils.trimToNull(apiKey) == null) { + BridgeConfiguration config = getConfigAs(BridgeConfiguration.class); + if (config.apikey.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Parameter 'apikey' must be configured."); } else { - header.put("x-access-token", apiKey); + header.put("x-access-token", config.apikey); initiateConnexion(); } } + @Override + public void dispose() { + ScheduledFuture job = this.reconnectJob; + if (job != null && !job.isCancelled()) { + job.cancel(true); + } + reconnectJob = null; + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { @@ -106,62 +105,49 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler { private void initiateConnexion() { // Check if the provided api key is valid for use with the OpenUV service - getUVData("0", "0", null); + getUVData("0", "0", "0"); } - public Map> getDiscoveryServiceRegs() { - return discoveryServiceRegs; - } - - public void setDiscoveryServiceRegs(Map> discoveryServiceRegs) { - this.discoveryServiceRegs = discoveryServiceRegs; - } - - @Override - public void handleRemoval() { - // removes the old registration service associated to the bridge, if existing - ServiceRegistration dis = getDiscoveryServiceRegs().get(getThing().getUID()); - if (dis != null) { - dis.unregister(); - } - super.handleRemoval(); - } - - public @Nullable OpenUVResult getUVData(String latitude, String longitude, @Nullable String altitude) { - StringBuilder urlBuilder = new StringBuilder(BASE_URL).append("?lat=").append(latitude).append("&lng=") - .append(longitude); - - if (altitude != null) { - urlBuilder.append("&alt=").append(altitude); - } - String errorMessage = null; + public @Nullable OpenUVResult getUVData(String latitude, String longitude, String altitude) { try { - String jsonData = HttpUtil.executeUrl("GET", urlBuilder.toString(), header, null, null, REQUEST_TIMEOUT); + String jsonData = HttpUtil.executeUrl("GET", String.format(QUERY_URL, latitude, longitude, altitude), + header, null, null, REQUEST_TIMEOUT_MS); OpenUVResponse uvResponse = gson.fromJson(jsonData, OpenUVResponse.class); if (uvResponse.getError() == null) { updateStatus(ThingStatus.ONLINE); return uvResponse.getResult(); } else { - errorMessage = uvResponse.getError(); + throw new OpenUVException(uvResponse.getError()); } } catch (IOException e) { - errorMessage = e.getMessage(); - } + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } catch (OpenUVException e) { + if (e.isQuotaError()) { + LocalDate today = LocalDate.now(); + LocalDate tomorrow = today.plusDays(1); + LocalDateTime tomorrowMidnight = tomorrow.atStartOfDay().plusMinutes(2); - if (errorMessage.startsWith(ERROR_QUOTA_EXCEEDED)) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage); - LocalDate today = LocalDate.now(); - LocalDate tomorrow = today.plusDays(1); - LocalDateTime tomorrowMidnight = tomorrow.atStartOfDay().plusMinutes(2); + String message = "Quota Exceeded, going OFFLINE for today, will retry at : " + + tomorrowMidnight.toString(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message); - logger.warn("Quota Exceeded, going OFFLINE for today, will retry at : {} ", tomorrowMidnight); - scheduler.schedule(this::initiateConnexion, - Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES); - - } else if (errorMessage.startsWith(ERROR_WRONG_KEY)) { - logger.error("Error occured during API query : {}", errorMessage); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage); + reconnectJob = scheduler.schedule(this::initiateConnexion, + Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES); + } else if (e.isApiKeyError()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage()); + } } return null; } + + @Override + public Collection> getServices() { + return Collections.singleton(OpenUVDiscoveryService.class); + } + + public @Nullable PointType getLocation() { + return locationProvider.getLocation(); + } } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java index 74ee8acb72f..582e3ca67d7 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java @@ -23,11 +23,13 @@ import java.util.concurrent.TimeUnit; import javax.measure.quantity.Angle; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.openuv.internal.ReportConfiguration; -import org.openhab.binding.openuv.internal.SafeExposureConfiguration; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.openuv.internal.config.ReportConfiguration; +import org.openhab.binding.openuv.internal.config.SafeExposureConfiguration; import org.openhab.binding.openuv.internal.json.OpenUVResult; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.SmartHomeUnits; import org.openhab.core.thing.Bridge; @@ -54,13 +56,11 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class OpenUVReportHandler extends BaseThingHandler { - private static final int DEFAULT_REFRESH_PERIOD = 30; - private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class); private @NonNullByDefault({}) OpenUVBridgeHandler bridgeHandler; - private @NonNullByDefault({}) ScheduledFuture refreshJob; - private @NonNullByDefault({}) ScheduledFuture uvMaxJob; + private @Nullable ScheduledFuture refreshJob; + private @Nullable ScheduledFuture uvMaxJob; private boolean suspendUpdates = false; public OpenUVReportHandler(Thing thing) { @@ -73,7 +73,7 @@ public class OpenUVReportHandler extends BaseThingHandler { ReportConfiguration config = getConfigAs(ReportConfiguration.class); - if (config.refresh != null && config.refresh < 3) { + if (config.refresh < 3) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Parameter 'refresh' must be higher than 3 minutes to stay in free API plan"); } else { @@ -94,7 +94,8 @@ public class OpenUVReportHandler extends BaseThingHandler { * @param openUVData */ private void scheduleUVMaxEvent(OpenUVResult openUVData) { - if ((uvMaxJob == null || uvMaxJob.isCancelled())) { + ScheduledFuture job = this.uvMaxJob; + if ((job == null || job.isCancelled())) { State uvMaxTime = openUVData.getUVMaxTime(); if (uvMaxTime != UnDefType.NULL) { ZonedDateTime uvMaxZdt = ((DateTimeType) uvMaxTime).getZonedDateTime(); @@ -114,22 +115,23 @@ public class OpenUVReportHandler extends BaseThingHandler { * Start the job refreshing the data */ private void startAutomaticRefresh() { - if (refreshJob == null || refreshJob.isCancelled()) { + ScheduledFuture job = this.refreshJob; + if (job == null || job.isCancelled()) { ReportConfiguration config = getConfigAs(ReportConfiguration.class); - int delay = (config.refresh != null) ? config.refresh.intValue() : DEFAULT_REFRESH_PERIOD; refreshJob = scheduler.scheduleWithFixedDelay(() -> { if (!suspendUpdates) { updateChannels(config); } - }, 0, delay, TimeUnit.MINUTES); + }, 0, config.refresh, TimeUnit.MINUTES); } } private void updateChannels(ReportConfiguration config) { ThingStatusInfo bridgeStatusInfo = bridgeHandler.getThing().getStatusInfo(); if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { - OpenUVResult openUVData = bridgeHandler.getUVData(config.getLatitude(), config.getLongitude(), - config.getAltitude()); + PointType location = new PointType(config.location); + OpenUVResult openUVData = bridgeHandler.getUVData(location.getLatitude().toString(), + location.getLongitude().toString(), location.getAltitude().toString()); if (openUVData != null) { scheduleUVMaxEvent(openUVData); getThing().getChannels().forEach(channel -> { @@ -146,16 +148,17 @@ public class OpenUVReportHandler extends BaseThingHandler { @Override public void dispose() { logger.debug("Disposing the OpenUV handler."); - - if (refreshJob != null && !refreshJob.isCancelled()) { - refreshJob.cancel(true); - refreshJob = null; + ScheduledFuture refresh = this.refreshJob; + if (refresh != null && !refresh.isCancelled()) { + refresh.cancel(true); } + refreshJob = null; - if (uvMaxJob != null && !uvMaxJob.isCancelled()) { - uvMaxJob.cancel(true); - uvMaxJob = null; + ScheduledFuture uxMax = this.uvMaxJob; + if (uxMax != null && !uxMax.isCancelled()) { + uxMax.cancel(true); } + uvMaxJob = null; } @SuppressWarnings("unchecked")