[meteofrance] Documentation enhancements and more (#17740)

* Some binding enhancements

Signed-off-by: Gaël L'hopital <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2024-11-20 23:32:33 +01:00 committed by GitHub
parent b4fb5a1f1b
commit 91f16fa16c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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. | | location | Geo coordinates to be considered by the service. |
## Channels ## Channels for `vigilance`
The information that are retrieved is available as these channels: 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 | | 2 | Orange | Be "very vigilant" in the concerned areas |
| 3 | Red | Absolute vigilance required | | 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 ## Provided icon set
This binding has its own IconProvider and makes available the following list of icons 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: meteoalert.things:
```java ```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: 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"} 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 java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.PointType;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; 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, public record Department(String id, String name, double northestLat, double southestLat, double eastestLon,
double westestLon) { double westestLon) {
boolean contains(double latitude, double longitude) {
return northestLat >= latitude && southestLat <= latitude && //
westestLon <= longitude && eastestLon >= longitude;
}
} }
@Activate @Activate
@ -58,7 +62,7 @@ public class DepartmentDbService {
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
departments.addAll(Arrays.asList(gson.fromJson(reader, Department[].class))); 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) { } catch (IOException | JsonSyntaxException | JsonIOException e) {
logger.warn("Unable to load departments list: {}", e.getMessage()); logger.warn("Unable to load departments list: {}", e.getMessage());
} }
@ -67,11 +71,6 @@ public class DepartmentDbService {
public List<Department> getBounding(PointType location) { public List<Department> getBounding(PointType location) {
double latitude = location.getLatitude().doubleValue(); double latitude = location.getLatitude().doubleValue();
double longitude = location.getLongitude().doubleValue(); double longitude = location.getLongitude().doubleValue();
return departments.stream().filter(dep -> dep.northestLat >= latitude && dep.southestLat <= latitude return departments.stream().filter(dep -> dep.contains(latitude, longitude)).toList();
&& 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);
} }
} }

View File

@ -55,8 +55,8 @@ public class MeteoFrance {
Periods periods) { // périodes concernées par la Vigilance Periods periods) { // périodes concernées par la Vigilance
public Optional<TextBlocItem> getBlocItem(Domain searched) { public Optional<TextBlocItem> getBlocItem(Domain searched) {
return textBlocItems != null ? textBlocItems.stream().filter(ti -> ti.validFor(searched)).findFirst() List<TextBlocItem> local = textBlocItems;
: Optional.empty(); return local != null ? local.stream().filter(ti -> ti.validFor(searched)).findFirst() : Optional.empty();
} }
public Optional<Period> getPeriod(Term term) { 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 static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.REQUEST_TIMEOUT_MS;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -159,10 +160,13 @@ public class MeteoFranceBridgeHandler extends BaseBridgeHandler {
} }
public @Nullable RainForecast getRainForecast(PointType location) { 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()); location.getLongitude().doubleValue());
try { try {
logger.debug("Sending rain-forecast request to: {}", url);
String answer = HttpUtil.executeUrl(HttpMethod.GET, url, REQUEST_TIMEOUT_MS); String answer = HttpUtil.executeUrl(HttpMethod.GET, url, REQUEST_TIMEOUT_MS);
logger.debug("Received answer: {}", answer);
return deserializer.deserialize(RainForecast.class, answer); return deserializer.deserialize(RainForecast.class, answer);
} catch (MeteoFranceException e) { } catch (MeteoFranceException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,

View File

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