diff --git a/bundles/org.openhab.binding.openuv/README.md b/bundles/org.openhab.binding.openuv/README.md index 90391dfc8..22aeb8c28 100644 --- a/bundles/org.openhab.binding.openuv/README.md +++ b/bundles/org.openhab.binding.openuv/README.md @@ -62,6 +62,16 @@ This is quite useful with a free OpenUV account (50 req/day included): in this c Thing can be extended with as many SafeExposure channels as needed for each skin type. +## Provided icon set + +This binding has its own IconProvider and makes available the following list of icons + +| Icon Name | Dynamic | Illustration | +|--------------------|---------|--------------| +| oh:openuv:ozone | No | ![](src/main/resources/icon/ozone.svg) | +| oh:openuv:uv-alarm | Yes | ![](src/main/resources/icon/uv-alarm.svg) | +| oh:openuv:uv-index | Yes | ![](src/main/resources/icon/uv-index.svg) | + ## Examples demo.things: diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/AlertLevel.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/AlertLevel.java new file mode 100644 index 000000000..1d32407c6 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/AlertLevel.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2023 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; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link AlertLevel} enum defines alert level in regard of the UV Index + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public enum AlertLevel { + GREEN(DecimalType.ZERO, "3a8b2f"), + YELLOW(new DecimalType(1), "f9a825"), + ORANGE(new DecimalType(2), "ef6c00"), + RED(new DecimalType(3), "b71c1c"), + PURPLE(new DecimalType(4), "6a1b9a"), + UNKNOWN(UnDefType.NULL, "b3b3b3"); + + public final State state; + public final String color; + + AlertLevel(State state, String color) { + this.state = state; + this.color = color; + } + + public static AlertLevel fromUVIndex(double uv) { + if (uv >= 11) { + return PURPLE; + } else if (uv >= 8) { + return RED; + } else if (uv >= 6) { + return ORANGE; + } else if (uv >= 3) { + return YELLOW; + } else if (uv > 0) { + return GREEN; + } + return UNKNOWN; + } +} 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 index ab37e7349..c95dbfed9 100644 --- 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 @@ -29,6 +29,10 @@ public class OpenUVException extends Exception { super(message); } + public OpenUVException(String fitzPatrickIndex, Throwable e) { + super("Unexpected Fitzpatrick index value '%s'".formatted(fitzPatrickIndex), e); + } + private boolean checkMatches(String message) { String currentMessage = getMessage(); return currentMessage != null && currentMessage.startsWith(message); diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVIconProvider.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVIconProvider.java new file mode 100644 index 000000000..4083e6522 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVIconProvider.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2010-2023 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 static org.openhab.binding.openuv.internal.OpenUVBindingConstants.BINDING_ID; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.ui.icon.IconProvider; +import org.openhab.core.ui.icon.IconSet; +import org.openhab.core.ui.icon.IconSet.Format; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link OpenUVIconProvider} is the class providing binding related icons. + * + * @author Gaël L'hopital - Initial contribution + */ +@Component(service = { IconProvider.class }) +@NonNullByDefault +public class OpenUVIconProvider implements IconProvider { + private static final String UV_ALARM = "uv-alarm"; + private static final String UV_INDEX = "uv-index"; + private static final String DEFAULT_LABEL = "OpenUV Icons"; + private static final String DEFAULT_DESCRIPTION = "Icons illustrating UV conditions provided by OpenUV"; + private static final Set ICONS = Set.of("ozone", UV_INDEX, UV_ALARM); + + private final Logger logger = LoggerFactory.getLogger(OpenUVIconProvider.class); + private final BundleContext context; + private final TranslationProvider i18nProvider; + + @Activate + public OpenUVIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) { + this.context = context; + this.i18nProvider = i18nProvider; + } + + @Override + public Set getIconSets() { + return getIconSets(null); + } + + @Override + public Set getIconSets(@Nullable Locale locale) { + String label = getText("label", DEFAULT_LABEL, locale); + String description = getText("decription", DEFAULT_DESCRIPTION, locale); + + return Set.of(new IconSet(BINDING_ID, label, description, Set.of(Format.SVG))); + } + + private String getText(String entry, String defaultValue, @Nullable Locale locale) { + String text = defaultValue; + if (locale != null) { + text = i18nProvider.getText(context.getBundle(), "iconset." + entry, defaultValue, locale); + text = text == null ? defaultValue : text; + } + return text; + } + + @Override + public @Nullable Integer hasIcon(String category, String iconSetId, Format format) { + return ICONS.contains(category) && iconSetId.equals(BINDING_ID) && format == Format.SVG ? 0 : null; + } + + @Override + public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) { + String iconName = category; + if (UV_INDEX.equals(category) && state != null) { + try { + Double numeric = Double.valueOf(state); + iconName = "%s-%d".formatted(category, numeric.intValue()); + } catch (NumberFormatException e) { + logger.debug("Unable to parse {} to a numeric value", state); + } + } + String icon = getResource(iconName); + if (UV_ALARM.equals(category) && state != null) { + try { + Integer ordinal = Integer.valueOf(state); + AlertLevel alertLevel = ordinal < AlertLevel.values().length ? AlertLevel.values()[ordinal] + : AlertLevel.UNKNOWN; + icon = icon.replaceAll(AlertLevel.UNKNOWN.color, alertLevel.color); + } catch (NumberFormatException e) { + logger.debug("Unable to parse {} to a numeric value", state); + } + } + return icon.isEmpty() ? null : new ByteArrayInputStream(icon.getBytes()); + } + + private String getResource(String iconName) { + String result = ""; + + URL iconResource = context.getBundle().getEntry("icon/%s.svg".formatted(iconName)); + try (InputStream stream = iconResource.openStream()) { + result = new String(stream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + logger.warn("Unable to load ressource '{}' : {}", iconResource.getPath(), e.getMessage()); + } + return result; + } +} 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 a39430361..ab46eb4ff 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 @@ -46,11 +46,10 @@ public class OpenUVDiscoveryService extends AbstractDiscoveryService implements @Override public void setThingHandler(ThingHandler handler) { - if (handler instanceof OpenUVBridgeHandler) { - OpenUVBridgeHandler localHandler = (OpenUVBridgeHandler) handler; - bridgeHandler = localHandler; - i18nProvider = localHandler.getI18nProvider(); - localeProvider = localHandler.getLocaleProvider(); + if (handler instanceof OpenUVBridgeHandler bridgeHandler) { + this.bridgeHandler = bridgeHandler; + this.i18nProvider = bridgeHandler.getI18nProvider(); + this.localeProvider = bridgeHandler.getLocaleProvider(); } } 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 f268d8963..af65fe28c 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 @@ -13,6 +13,7 @@ package org.openhab.binding.openuv.internal.handler; import java.io.IOException; +import java.math.BigDecimal; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; @@ -58,7 +59,7 @@ import com.google.gson.JsonSyntaxException; */ @NonNullByDefault public class OpenUVBridgeHandler extends BaseBridgeHandler { - private static final String QUERY_URL = "https://api.openuv.io/api/v1/uv?lat=%s&lng=%s&alt=%s"; + private static final String QUERY_URL = "https://api.openuv.io/api/v1/uv?lat=%.2f&lng=%.2f&alt=%.0f"; private static final int RECONNECT_DELAY_MIN = 5; private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30); @@ -97,6 +98,7 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler { @Override public void dispose() { + header.clear(); freeReconnectJob(); } @@ -104,20 +106,20 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler { public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { initiateConnexion(); - return; + } else { + logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command); } - logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command); } private void initiateConnexion() { // Just checking if the provided api key is a valid one by making a fake call - getUVData("0", "0", "0"); + getUVData(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO); } - public @Nullable OpenUVResult getUVData(String latitude, String longitude, String altitude) { + public @Nullable OpenUVResult getUVData(BigDecimal latitude, BigDecimal longitude, BigDecimal altitude) { String statusMessage = ""; ThingStatusDetail statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; - String url = String.format(QUERY_URL, latitude, longitude, altitude); + String url = QUERY_URL.formatted(latitude, longitude, altitude); String jsonData = ""; try { jsonData = HttpUtil.executeUrl("GET", url, header, null, null, REQUEST_TIMEOUT_MS); @@ -133,28 +135,25 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler { } } catch (JsonSyntaxException e) { if (jsonData.contains("MongoError")) { - statusMessage = String.format("@text/offline.comm-error-faultly-service [ \"%d\" ]", - RECONNECT_DELAY_MIN); + statusMessage = "@text/offline.comm-error-faultly-service [ \"%d\" ]".formatted(RECONNECT_DELAY_MIN); scheduleReconnectJob(RECONNECT_DELAY_MIN); } else { statusDetail = ThingStatusDetail.NONE; - statusMessage = String.format("@text/offline.invalid-json [ \"%s\" ]", url); + statusMessage = "@text/offline.invalid-json [ \"%s\" ]".formatted(url); logger.debug("{} : {}", statusMessage, jsonData); } } catch (IOException e) { - statusMessage = String.format("@text/offline.comm-error-ioexception [ \"%s\",\"%d\" ]", e.getMessage(), + statusMessage = "@text/offline.comm-error-ioexception [ \"%s\",\"%d\" ]".formatted(e.getMessage(), RECONNECT_DELAY_MIN); scheduleReconnectJob(RECONNECT_DELAY_MIN); } catch (OpenUVException e) { if (e.isQuotaError()) { LocalDateTime nextMidnight = LocalDate.now().plusDays(1).atStartOfDay().plusMinutes(2); - statusMessage = String.format("@text/offline.comm-error-quota-exceeded [ \"%s\" ]", - nextMidnight.toString()); + statusMessage = "@text/offline.comm-error-quota-exceeded [ \"%s\" ]".formatted(nextMidnight.toString()); scheduleReconnectJob(Duration.between(LocalDateTime.now(), nextMidnight).toMinutes()); } else if (e.isApiKeyError()) { if (keyVerified) { - statusMessage = String.format("@text/offline.api-key-not-recognized [ \"%d\" ]", - RECONNECT_DELAY_MIN); + statusMessage = "@text/offline.api-key-not-recognized [ \"%d\" ]".formatted(RECONNECT_DELAY_MIN); scheduleReconnectJob(RECONNECT_DELAY_MIN); } else { statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; 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 a0aaac8be..826111e48 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 @@ -17,12 +17,13 @@ import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.util.Map; 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.AlertLevel; +import org.openhab.binding.openuv.internal.OpenUVException; import org.openhab.binding.openuv.internal.config.ReportConfiguration; import org.openhab.binding.openuv.internal.config.SafeExposureConfiguration; import org.openhab.binding.openuv.internal.json.OpenUVResult; @@ -56,16 +57,6 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class OpenUVReportHandler extends BaseThingHandler { - private static final State ALERT_GREEN = DecimalType.ZERO; - private static final State ALERT_YELLOW = new DecimalType(1); - private static final State ALERT_ORANGE = new DecimalType(2); - private static final State ALERT_RED = new DecimalType(3); - private static final State ALERT_PURPLE = new DecimalType(4); - private static final State ALERT_UNDEF = HSBType.fromRGB(179, 179, 179); - - private static final Map ALERT_COLORS = Map.of(ALERT_GREEN, HSBType.fromRGB(85, 139, 47), - ALERT_YELLOW, HSBType.fromRGB(249, 168, 37), ALERT_ORANGE, HSBType.fromRGB(239, 108, 0), ALERT_RED, - HSBType.fromRGB(183, 28, 28), ALERT_PURPLE, HSBType.fromRGB(106, 27, 154)); private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class); @@ -108,9 +99,9 @@ public class OpenUVReportHandler extends BaseThingHandler { ScheduledFuture job = this.uvMaxJob; if ((job == null || job.isCancelled())) { State uvMaxTime = openUVData.getUVMaxTime(); - if (uvMaxTime != UnDefType.NULL) { - ZonedDateTime uvMaxZdt = ((DateTimeType) uvMaxTime).getZonedDateTime(); - long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), uvMaxZdt); + if (uvMaxTime instanceof DateTimeType uvMaxDateTime) { + long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), + uvMaxDateTime.getZonedDateTime()); if (timeDiff > 0) { logger.debug("Scheduling {} in {} minutes", UV_MAX_EVENT, timeDiff); uvMaxJob = scheduler.schedule(() -> { @@ -131,18 +122,17 @@ public class OpenUVReportHandler extends BaseThingHandler { ReportConfiguration config = getConfigAs(ReportConfiguration.class); refreshJob = scheduler.scheduleWithFixedDelay(() -> { if (!suspendUpdates) { - updateChannels(config); + updateChannels(new PointType(config.location)); } }, 0, config.refresh, TimeUnit.MINUTES); } } - private void updateChannels(ReportConfiguration config) { + private void updateChannels(PointType location) { ThingStatusInfo bridgeStatusInfo = bridgeHandler.getThing().getStatusInfo(); if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { - PointType location = new PointType(config.location); - OpenUVResult openUVData = bridgeHandler.getUVData(location.getLatitude().toString(), - location.getLongitude().toString(), location.getAltitude().toString()); + OpenUVResult openUVData = bridgeHandler.getUVData(location.getLatitude().toBigDecimal(), + location.getLongitude().toBigDecimal(), location.getAltitude().toBigDecimal()); if (openUVData != null) { scheduleUVMaxEvent(openUVData); getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID().getId())) @@ -158,28 +148,28 @@ public class OpenUVReportHandler extends BaseThingHandler { @Override public void dispose() { logger.debug("Disposing the OpenUV handler."); - ScheduledFuture refresh = refreshJob; - if (refresh != null && !refresh.isCancelled()) { - refresh.cancel(true); - } + + cancelFuture(refreshJob); refreshJob = null; - ScheduledFuture uxMax = uvMaxJob; - if (uxMax != null && !uxMax.isCancelled()) { - uxMax.cancel(true); - } + cancelFuture(uvMaxJob); uvMaxJob = null; } + private void cancelFuture(@Nullable ScheduledFuture job) { + if (job != null && !job.isCancelled()) { + job.cancel(true); + } + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { scheduler.execute(() -> { ReportConfiguration config = getConfigAs(ReportConfiguration.class); - updateChannels(config); + updateChannels(new PointType(config.location)); }); - } else if (ELEVATION.equals(channelUID.getId()) && command instanceof QuantityType) { - QuantityType qtty = (QuantityType) command; + } else if (ELEVATION.equals(channelUID.getId()) && command instanceof QuantityType qtty) { if (Units.DEGREE_ANGLE.equals(qtty.getUnit())) { suspendUpdates = qtty.doubleValue() < 0; } else { @@ -196,9 +186,9 @@ public class OpenUVReportHandler extends BaseThingHandler { case UV_INDEX: return new DecimalType(openUVData.getUv()); case ALERT_LEVEL: - return asAlertLevel(openUVData.getUv()); + return AlertLevel.fromUVIndex(openUVData.getUv()).state; case UV_COLOR: - return ALERT_COLORS.getOrDefault(asAlertLevel(openUVData.getUv()), ALERT_UNDEF); + return hexToHSB(AlertLevel.fromUVIndex(openUVData.getUv()).color); case UV_MAX: return new DecimalType(openUVData.getUvMax()); case OZONE: @@ -210,26 +200,24 @@ public class OpenUVReportHandler extends BaseThingHandler { case UV_TIME: return openUVData.getUVTime(); } + ChannelTypeUID channelType = channel.getChannelTypeUID(); if (channelType != null && SAFE_EXPOSURE.equals(channelType.getId())) { SafeExposureConfiguration configuration = channel.getConfiguration().as(SafeExposureConfiguration.class); - return openUVData.getSafeExposureTime(configuration.index); + try { + return openUVData.getSafeExposureTime(configuration.index); + } catch (OpenUVException e) { + logger.warn("Error getting safe exposure value : {}", e.getMessage()); + } } + return UnDefType.NULL; } - private State asAlertLevel(double uv) { - if (uv >= 11) { - return ALERT_PURPLE; - } else if (uv >= 8) { - return ALERT_RED; - } else if (uv >= 6) { - return ALERT_ORANGE; - } else if (uv >= 3) { - return ALERT_YELLOW; - } else if (uv > 0) { - return ALERT_GREEN; - } - return UnDefType.NULL; + private State hexToHSB(String hexValue) { + int resultRed = Integer.valueOf(hexValue.substring(0, 2), 16); + int resultGreen = Integer.valueOf(hexValue.substring(2, 4), 16); + int resultBlue = Integer.valueOf(hexValue.substring(4, 6), 16); + return HSBType.fromRGB(resultRed, resultGreen, resultBlue); } } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java index 399e23247..70a6cf6d3 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java @@ -13,18 +13,16 @@ package org.openhab.binding.openuv.internal.json; import java.time.ZonedDateTime; -import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.openuv.internal.OpenUVException; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.gson.annotations.SerializedName; @@ -35,8 +33,6 @@ import com.google.gson.annotations.SerializedName; */ @NonNullByDefault public class OpenUVResult { - private final Logger logger = LoggerFactory.getLogger(OpenUVResult.class); - public enum FitzpatrickType { @SerializedName("st1") I, // Fitzpatrick Skin Type I @@ -58,7 +54,7 @@ public class OpenUVResult { private @Nullable ZonedDateTime uvTime; private @Nullable ZonedDateTime uvMaxTime; private @Nullable ZonedDateTime ozoneTime; - private Map safeExposureTime = new HashMap<>(); + private Map safeExposureTime = Map.of(); public double getUv() { return uv; @@ -88,16 +84,12 @@ public class OpenUVResult { return getValueOrNull(ozoneTime); } - public State getSafeExposureTime(String index) { + public State getSafeExposureTime(String index) throws OpenUVException { try { - FitzpatrickType value = FitzpatrickType.valueOf(index); - Integer duration = safeExposureTime.get(value); - if (duration != null) { - return QuantityType.valueOf(duration, Units.MINUTE); - } + Integer duration = safeExposureTime.get(FitzpatrickType.valueOf(index)); + return duration != null ? QuantityType.valueOf(duration, Units.MINUTE) : UnDefType.NULL; } catch (IllegalArgumentException e) { - logger.warn("Unexpected Fitzpatrick index value '{}' : {}", index, e.getMessage()); + throw new OpenUVException(index, e); } - return UnDefType.NULL; } } diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/i18n/openuv.properties b/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/i18n/openuv.properties index b889bd5f5..8b9ac8ba1 100644 --- a/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/i18n/openuv.properties +++ b/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/i18n/openuv.properties @@ -74,3 +74,8 @@ offline.api-key-not-recognized = Service error while API key is known correct, w # discovery result discovery.openuv.uvreport.local.label = Local UV Report + +# iconprovider + +iconset.label = OpenUV Icons +iconset.description = Icons illustrating UV conditions provided by OpenUV diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/thing/thing-types.xml index 49b43580e..1a0c0bf96 100644 --- a/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.openuv/src/main/resources/OH-INF/thing/thing-types.xml @@ -74,6 +74,7 @@ Number UV Index + oh:openuv:uv-index @@ -81,6 +82,7 @@ Number Max UV Index for the day (at solar noon) + oh:openuv:uv-index @@ -88,6 +90,7 @@ Number:ArealDensity Ozone level from OMI data + oh:openuv:ozone @@ -110,7 +113,7 @@ Number:Time Safe exposure duration for Fitzpatrick Skin Types. - time + Time @@ -147,7 +150,7 @@ Number - alarm + oh:openuv:uv-alarm diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/ozone.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/ozone.svg new file mode 100644 index 000000000..fce47e376 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/ozone.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-alarm.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-alarm.svg new file mode 100644 index 000000000..66394bedc --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-alarm.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-1.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-1.svg new file mode 100644 index 000000000..2767a0776 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-1.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-10.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-10.svg new file mode 100644 index 000000000..de933d523 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-10.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-11.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-11.svg new file mode 100644 index 000000000..368dacdea --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-11.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-2.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-2.svg new file mode 100644 index 000000000..1b1b8c48c --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-2.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-3.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-3.svg new file mode 100644 index 000000000..db47338c8 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-3.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-4.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-4.svg new file mode 100644 index 000000000..186c07cde --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-4.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-5.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-5.svg new file mode 100644 index 000000000..44cd7a6c2 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-5.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-6.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-6.svg new file mode 100644 index 000000000..459828eb9 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-6.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-7.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-7.svg new file mode 100644 index 000000000..f2e2489d6 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-7.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-8.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-8.svg new file mode 100644 index 000000000..bd10d4bc9 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-8.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-9.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-9.svg new file mode 100644 index 000000000..da658b9a4 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index-9.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index.svg b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index.svg new file mode 100755 index 000000000..ed6cb5f43 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/resources/icon/uv-index.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file