[OpenUV] Issue when UV index < 1 (#9198)

* [OpenUV] Correcting incorrect behaviour when UV < 1
and some code enhancements
* Correcting SAT findings
* Initiating bundle localization in French

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2020-12-04 02:31:18 +01:00 committed by GitHub
parent b27ddbe9fc
commit 48dcb27a61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 119 additions and 143 deletions

View File

@ -52,7 +52,8 @@ The OpenUV Report thing that is retrieved has these channels:
| Channel ID | Item Type | Description |
|--------------|---------------------|-------------------------------------------------|
| UVIndex | Number | UV Index |
| UVColor | Color | Color associated to given UV Index. |
| Alert | Number | Alert level associated to given UV Index |
| UVColor | Color | Color associated to given alert level. |
| UVMax | Number | Max UV Index for the day (at solar noon) |
| UVMaxTime | DateTime | Max UV Index datetime (solar noon) |
| Ozone | Number:ArealDensity | Ozone level in du (Dobson Units) from OMI data |

View File

@ -22,5 +22,5 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/
@NonNullByDefault
public class SafeExposureConfiguration {
public int index = -1;
public String index = "II";
}

View File

@ -54,16 +54,14 @@ import com.google.gson.Gson;
*/
@NonNullByDefault
public class OpenUVBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class);
private static final String QUERY_URL = "https://api.openuv.io/api/v1/uv?lat=%s&lng=%s&alt=%s";
private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);
private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class);
private final Properties header = new Properties();
private final Gson gson;
private final LocationProvider locationProvider;
private @Nullable ScheduledFuture<?> reconnectJob;
public OpenUVBridgeHandler(Bridge bridge, LocationProvider locationProvider, Gson gson) {
@ -79,10 +77,10 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler {
if (config.apikey.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Parameter 'apikey' must be configured.");
} else {
header.put("x-access-token", config.apikey);
initiateConnexion();
return;
}
header.put("x-access-token", config.apikey);
initiateConnexion();
}
@Override
@ -98,13 +96,13 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler {
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
initiateConnexion();
} else {
logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command);
return;
}
logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command);
}
private void initiateConnexion() {
// Check if the provided api key is valid for use with the OpenUV service
// Just checking if the provided api key is a valid one by making a fake call
getUVData("0", "0", "0");
}
@ -113,11 +111,13 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler {
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 {
throw new OpenUVException(uvResponse.getError());
if (uvResponse != null) {
String error = uvResponse.getError();
if (error == null) {
updateStatus(ThingStatus.ONLINE);
return uvResponse.getResult();
}
throw new OpenUVException(error);
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
@ -133,10 +133,10 @@ public class OpenUVBridgeHandler extends BaseBridgeHandler {
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());
updateStatus(ThingStatus.OFFLINE,
e.isApiKeyError() ? ThingStatusDetail.CONFIGURATION_ERROR : ThingStatusDetail.NONE,
e.getMessage());
}
}
return null;

View File

@ -21,8 +21,6 @@ import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.measure.quantity.Angle;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openuv.internal.config.ReportConfiguration;
@ -174,7 +172,6 @@ public class OpenUVReportHandler extends BaseThingHandler {
uvMaxJob = null;
}
@SuppressWarnings("unchecked")
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
@ -184,8 +181,8 @@ public class OpenUVReportHandler extends BaseThingHandler {
});
} else if (ELEVATION.equals(channelUID.getId()) && command instanceof QuantityType) {
QuantityType<?> qtty = (QuantityType<?>) command;
if ("°".equals(qtty.getUnit().toString())) {
suspendUpdates = ((QuantityType<Angle>) qtty).doubleValue() < 0;
if (qtty.getUnit() == SmartHomeUnits.DEGREE_ANGLE) {
suspendUpdates = qtty.doubleValue() < 0;
} else {
logger.info("The OpenUV Report handles Sun Elevation of Number:Angle type, {} does not fit.", command);
}
@ -208,7 +205,7 @@ public class OpenUVReportHandler extends BaseThingHandler {
if (channelTypeUID != null) {
switch (channelTypeUID.getId()) {
case UV_INDEX:
updateState(channelUID, asDecimalType(openUVData.getUv()));
updateState(channelUID, new DecimalType(openUVData.getUv()));
break;
case ALERT_LEVEL:
updateState(channelUID, asAlertLevel(openUVData.getUv()));
@ -218,7 +215,7 @@ public class OpenUVReportHandler extends BaseThingHandler {
ALERT_COLORS.getOrDefault(asAlertLevel(openUVData.getUv()), ALERT_UNDEF));
break;
case UV_MAX:
updateState(channelUID, asDecimalType(openUVData.getUvMax()));
updateState(channelUID, new DecimalType(openUVData.getUvMax()));
break;
case OZONE:
updateState(channelUID, new QuantityType<>(openUVData.getOzone(), SmartHomeUnits.DOBSON_UNIT));
@ -235,24 +232,14 @@ public class OpenUVReportHandler extends BaseThingHandler {
case SAFE_EXPOSURE:
SafeExposureConfiguration configuration = channel.getConfiguration()
.as(SafeExposureConfiguration.class);
if (configuration.index != -1) {
updateState(channelUID,
openUVData.getSafeExposureTime().getSafeExposure(configuration.index));
}
updateState(channelUID, openUVData.getSafeExposureTime(configuration.index));
break;
}
}
}
}
private State asDecimalType(int uv) {
if (uv >= 1) {
return new DecimalType(uv);
}
return UnDefType.NULL;
}
private State asAlertLevel(int uv) {
private State asAlertLevel(double uv) {
if (uv >= 11) {
return ALERT_PURPLE;
} else if (uv >= 8) {
@ -261,7 +248,7 @@ public class OpenUVReportHandler extends BaseThingHandler {
return ALERT_ORANGE;
} else if (uv >= 3) {
return ALERT_YELLOW;
} else if (uv >= 1) {
} else if (uv > 0) {
return ALERT_GREEN;
}
return UnDefType.NULL;

View File

@ -12,21 +12,25 @@
*/
package org.openhab.binding.openuv.internal.json;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link OpenUVResponse} is the Java class used to map the JSON
* response to the OpenUV request.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class OpenUVResponse {
private String error;
private OpenUVResult result;
private @Nullable String error;
private @Nullable OpenUVResult result;
public OpenUVResult getResult() {
public @Nullable OpenUVResult getResult() {
return result;
}
public String getError() {
public @Nullable String getError() {
return error;
}
}

View File

@ -12,14 +12,21 @@
*/
package org.openhab.binding.openuv.internal.json;
import java.time.LocalDateTime;
import java.time.ZoneId;
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.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
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;
/**
* The {@link OpenUVResult} is responsible for storing
@ -29,21 +36,37 @@ import org.openhab.core.types.UnDefType;
*/
@NonNullByDefault
public class OpenUVResult {
private final ZonedDateTime DEFAULT_ZDT = ZonedDateTime.of(LocalDateTime.MIN, ZoneId.systemDefault());
private double uv;
private ZonedDateTime uvTime = DEFAULT_ZDT;
private double uvMax;
private ZonedDateTime uvMaxTime = DEFAULT_ZDT;
private double ozone;
private ZonedDateTime ozoneTime = DEFAULT_ZDT;
private SafeExposureTime safeExposureTime = new SafeExposureTime();
private final Logger logger = LoggerFactory.getLogger(OpenUVResult.class);
public int getUv() {
return (int) uv;
public enum FitzpatrickType {
@SerializedName("st1")
I, // Fitzpatrick Skin Type I
@SerializedName("st2")
II, // Fitzpatrick Skin Type II
@SerializedName("st3")
III, // Fitzpatrick Skin Type III
@SerializedName("st4")
IV, // Fitzpatrick Skin Type IV
@SerializedName("st5")
V, // Fitzpatrick Skin Type V
@SerializedName("st6")
VI;// Fitzpatrick Skin Type VI
}
public int getUvMax() {
return (int) uvMax;
private double uv;
private @Nullable ZonedDateTime uvTime;
private double uvMax;
private @Nullable ZonedDateTime uvMaxTime;
private double ozone;
private @Nullable ZonedDateTime ozoneTime;
private Map<FitzpatrickType, @Nullable Integer> safeExposureTime = new HashMap<>();
public double getUv() {
return uv;
}
public double getUvMax() {
return uvMax;
}
public double getOzone() {
@ -51,21 +74,30 @@ public class OpenUVResult {
}
public State getUVTime() {
return uvTime != DEFAULT_ZDT ? new DateTimeType(uvTime.withZoneSameInstant(ZoneId.systemDefault()))
: UnDefType.NULL;
ZonedDateTime value = uvTime;
return value != null ? new DateTimeType(value) : UnDefType.NULL;
}
public State getUVMaxTime() {
return uvMaxTime != DEFAULT_ZDT ? new DateTimeType(uvMaxTime.withZoneSameInstant(ZoneId.systemDefault()))
: UnDefType.NULL;
ZonedDateTime value = uvMaxTime;
return value != null ? new DateTimeType(value) : UnDefType.NULL;
}
public State getOzoneTime() {
return ozoneTime != DEFAULT_ZDT ? new DateTimeType(ozoneTime.withZoneSameInstant(ZoneId.systemDefault()))
: UnDefType.NULL;
ZonedDateTime value = ozoneTime;
return value != null ? new DateTimeType(value) : UnDefType.NULL;
}
public SafeExposureTime getSafeExposureTime() {
return safeExposureTime;
public State getSafeExposureTime(String index) {
try {
FitzpatrickType value = FitzpatrickType.valueOf(index);
Integer duration = safeExposureTime.get(value);
if (duration != null) {
return new QuantityType<>(duration, SmartHomeUnits.MINUTE);
}
} catch (IllegalArgumentException e) {
logger.warn("Unexpected Fitzpatrick index value '{}' : {}", index, e.getMessage());
}
return UnDefType.NULL;
}
}

View File

@ -1,62 +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.json;
import java.math.BigInteger;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* Wrapper type around values reported by OpenUV safe exposure time.
*
* @author Gaël L'hopital - Initial contribution
*/
public class SafeExposureTime {
public @Nullable BigInteger st1;
public @Nullable BigInteger st2;
public @Nullable BigInteger st3;
public @Nullable BigInteger st4;
public @Nullable BigInteger st5;
public @Nullable BigInteger st6;
public State getSafeExposure(int index) {
BigInteger result;
switch (index) {
case 1:
result = st1;
break;
case 2:
result = st2;
break;
case 3:
result = st3;
break;
case 4:
result = st4;
break;
case 5:
result = st5;
break;
case 6:
result = st6;
break;
default:
result = null;
}
return (result != null) ? new QuantityType<>(result, SmartHomeUnits.MINUTE) : UnDefType.NULL;
}
}

View File

@ -0,0 +1,10 @@
# binding
binding.openuv.name = Extension OpenUV
binding.openuv.description = Service de prévision globale de l'indice UV en temps réel.
# thing types
thing-type.openuv.openuvapi.label = Bridge OpenUV
thing-type.openuv.openuvapi.description = Passerelle vers le service du projet OpenUV. Pour recevoir des données vous devez créer votre compte à l'adresse https://www.openuv.io/auth/google et obtenir votre clef API.
thing-type.openuv.uvreport.label = Rapport UV
thing-type.openuv.uvreport.description = Fournit diverses information pour un emplacement donnée.

View File

@ -67,14 +67,14 @@
<item-type>Number</item-type>
<label>UV Index</label>
<description>UV Index</description>
<state readOnly="true" pattern="%d/16" min="0" max="16"/>
<state readOnly="true" pattern="%.0f/16" min="0" max="16"/>
</channel-type>
<channel-type id="UVMax" advanced="true">
<item-type>Number</item-type>
<label>UV Max</label>
<description>Max UV Index for the day (at solar noon)</description>
<state readOnly="true" pattern="%d/16" min="0" max="16"/>
<state readOnly="true" pattern="%.0f/16" min="0" max="16"/>
</channel-type>
<channel-type id="Ozone">
@ -102,37 +102,39 @@
<channel-type id="UVTime" advanced="true">
<item-type>DateTime</item-type>
<label>UV Time</label>
<description>UV Index timestamp.</description>
<label>Report Timestamp</label>
<description>UV Report timestamp.</description>
<category>time</category>
<state readOnly="true" pattern="%1$tF %1$tR"/>
</channel-type>
<channel-type id="UVColor" advanced="true">
<item-type>Color</item-type>
<label>UV Alert Color</label>
<label>Alert Color</label>
<description>Color associated to given UV Index alert level.</description>
<category>rgb</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="SafeExposure" advanced="false">
<item-type>Number:Time</item-type>
<label>Safe Exposure</label>
<description>Safe exposure time for Fitzpatrick Skin Types</description>
<description>Safe exposure duration for Fitzpatrick Skin Types.</description>
<category>time</category>
<state readOnly="true" pattern="%d %unit%"/>
<config-description>
<parameter name="index" type="integer">
<parameter name="index" type="text">
<label>Skin Type</label>
<description>Fitzpatrick Skin Type.</description>
<options>
<option value="1">I White</option>
<option value="2">II White</option>
<option value="3">III Light brown</option>
<option value="4">IV Moderate brown</option>
<option value="5">V Dark brown</option>
<option value="6">VI Black</option>
<option value="I">Pale</option>
<option value="II">White</option>
<option value="III">Light brown</option>
<option value="IV">Moderate brown</option>
<option value="V">Dark brown</option>
<option value="VI">Black</option>
</options>
<default>2</default>
<default>II</default>
</parameter>
</config-description>
</channel-type>
@ -140,7 +142,8 @@
<channel-type id="elevation">
<item-type>Number:Angle</item-type>
<label>Elevation</label>
<description>The elevation of the sun</description>
<description>The elevation of the sun (should FOLLOW appropriate item).</description>
<category>niveau</category>
<state pattern="%.2f %unit%"/>
</channel-type>
@ -154,6 +157,7 @@
<channel-type id="Alert">
<item-type>Number</item-type>
<label>UV Alert</label>
<category>alarm</category>
<state readOnly="true">
<options>
<option value="0">Low</option>