[meteofrance] Documentation enhancements and more (#17740)

* Some binding enhancements

Signed-off-by: Gaël L'hopital <gael@lhopital.org>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Gaël L'hopital 2024-11-20 23:32:33 +01:00 committed by Ciprian Pascu
parent 35aa4bcdc0
commit a5d4dc230b
5 changed files with 67 additions and 20 deletions

View File

@ -41,7 +41,7 @@ The `rain-forecast` thing has this configuration parameters:
|-----------|--------------------------------------------------------------|
| location | Geo coordinates to be considered by the service. |
## Channels
## Channels for `vigilance`
The information that are retrieved is available as these channels:
@ -78,6 +78,24 @@ The information that are retrieved is available as these channels:
| 2 | Orange | Be "very vigilant" in the concerned areas |
| 3 | Red | Absolute vigilance required |
## Channels for `rain-forecast`
The information that are retrieved is available as these channels:
| Channel ID | Item Type | Description |
|--------------|-----------|--------------------------|
| update-time | DateTime | Observation Timestamp |
| intensity | Number | Rain intensity level (*) |
(*) Rain intensity values and associated descriptions:
| Code | Description |
|------|---------------|
| 0 | Dry Weather |
| 1 | Light Rain |
| 2 | Moderate Rain |
| 3 | Heavy Rain |
## Provided icon set
This binding has its own IconProvider and makes available the following list of icons
@ -101,7 +119,10 @@ This binding has its own IconProvider and makes available the following list of
meteoalert.things:
```java
Thing meteofrance:department:yvelines @ "MyCity" [department="78", refresh=12]
Bridge meteofrance:api:local "Portail Météo-France" [ apikey="ey......FIjG1MIC9lmG5t6HygPAPg=="] {
vigilance yvelines "Vigilance Météo" [ department="78" ]
rain-forecast yvelines [ location="48.764207,2.05948" ]
}
```
meteoalert.items:
@ -129,4 +150,17 @@ Image MA_icon_avalanche "Avalanche" <oh:meteofranc
DateTime MA_ObservationTS "Timestamp [%1$tH:%1$tM]" <time> (gMeteoAlert) {channel="meteofrance:department:yvelines:observation-time"}
Number Intensite_Pluie "Intensité Pluie" <oh:meteofrance:intensity> (gMeteoAlert) {channel="meteofrance:rain-forecast:yvelines:intensity" }
```
jdbc.persist:
```java
Items {
* : strategy = everyChange
Intensite_Pluie : strategy = forecast
}
```

View File

@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.PointType;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@ -49,6 +48,11 @@ public class DepartmentDbService {
public record Department(String id, String name, double northestLat, double southestLat, double eastestLon,
double westestLon) {
boolean contains(double latitude, double longitude) {
return northestLat >= latitude && southestLat <= latitude && //
westestLon <= longitude && eastestLon >= longitude;
}
}
@Activate
@ -58,7 +62,7 @@ public class DepartmentDbService {
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
departments.addAll(Arrays.asList(gson.fromJson(reader, Department[].class)));
logger.debug("Successfully loaded {} departments", departments.size());
logger.debug("Successfully loaded {} French departments", departments.size());
} catch (IOException | JsonSyntaxException | JsonIOException e) {
logger.warn("Unable to load departments list: {}", e.getMessage());
}
@ -67,11 +71,6 @@ public class DepartmentDbService {
public List<Department> getBounding(PointType location) {
double latitude = location.getLatitude().doubleValue();
double longitude = location.getLongitude().doubleValue();
return departments.stream().filter(dep -> dep.northestLat >= latitude && dep.southestLat <= latitude
&& dep.westestLon <= longitude && dep.eastestLon >= longitude).toList();
}
public @Nullable Department getDept(String deptId) {
return departments.stream().filter(dep -> dep.id.equalsIgnoreCase(deptId)).findFirst().orElse(null);
return departments.stream().filter(dep -> dep.contains(latitude, longitude)).toList();
}
}

View File

@ -55,8 +55,8 @@ public class MeteoFrance {
Periods periods) { // périodes concernées par la Vigilance
public Optional<TextBlocItem> getBlocItem(Domain searched) {
return textBlocItems != null ? textBlocItems.stream().filter(ti -> ti.validFor(searched)).findFirst()
: Optional.empty();
List<TextBlocItem> local = textBlocItems;
return local != null ? local.stream().filter(ti -> ti.validFor(searched)).findFirst() : Optional.empty();
}
public Optional<Period> getPeriod(Term term) {

View File

@ -15,6 +15,7 @@ package org.openhab.binding.meteofrance.internal.handler;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.REQUEST_TIMEOUT_MS;
import java.io.IOException;
import java.util.Locale;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
@ -159,10 +160,13 @@ public class MeteoFranceBridgeHandler extends BaseBridgeHandler {
}
public @Nullable RainForecast getRainForecast(PointType location) {
String url = RAIN_FORECAST_BASE_URL.formatted(location.getLatitude().doubleValue(),
String url = String.format(Locale.US, RAIN_FORECAST_BASE_URL, location.getLatitude().doubleValue(),
location.getLongitude().doubleValue());
try {
logger.debug("Sending rain-forecast request to: {}", url);
String answer = HttpUtil.executeUrl(HttpMethod.GET, url, REQUEST_TIMEOUT_MS);
logger.debug("Received answer: {}", answer);
return deserializer.deserialize(RainForecast.class, answer);
} catch (MeteoFranceException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,

View File

@ -116,9 +116,9 @@ public class RainForecastHandler extends BaseThingHandler implements MeteoFrance
location.ifPresent(loc -> {
RainForecast forecast = handler.getRainForecast(loc);
if (forecast != null) {
updateStatus(ThingStatus.ONLINE);
setProperties(forecast.properties);
updateDate(UPDATE_TIME, forecast.updateTime);
updateStatus(ThingStatus.ONLINE);
}
});
}, () -> logger.warn("No viable bridge"));
@ -142,19 +142,29 @@ public class RainForecastHandler extends BaseThingHandler implements MeteoFrance
private void setForecast(List<Forecast> forecast) {
TimeSeries timeSeries = new TimeSeries(REPLACE);
ZonedDateTime now = ZonedDateTime.now();
forecast.forEach(prevision -> {
State currentState = null;
long untilNextRun = 0;
for (Forecast prevision : forecast) {
State state = prevision.rainIntensity() != RainIntensity.UNKNOWN
? new DecimalType(prevision.rainIntensity().ordinal())
: UnDefType.UNDEF;
if (currentState == null) {
currentState = state;
if (prevision.time().isAfter(now)) {
untilNextRun = now.until(prevision.time(), ChronoUnit.SECONDS);
}
}
timeSeries.add(prevision.time().toInstant(), state);
});
}
updateState(intensityChannelUID, currentState == null ? UnDefType.UNDEF : currentState);
sendTimeSeries(intensityChannelUID, timeSeries);
Forecast current = forecast.get(0);
long until = ZonedDateTime.now().until(current.time(), ChronoUnit.SECONDS);
logger.debug("Refresh rain intensity forecast in : {}s", until);
refreshJob = Optional.of(scheduler.schedule(this::updateAndPublish, until, TimeUnit.SECONDS));
untilNextRun = untilNextRun != 0 ? untilNextRun : 300;
logger.debug("Refresh rain intensity forecast in: {}s", untilNextRun);
refreshJob = Optional.of(scheduler.schedule(this::updateAndPublish, untilNextRun, TimeUnit.SECONDS));
}
private void updateDate(String channelId, @Nullable ZonedDateTime zonedDateTime) {