## Discovery
-This binding does not handle auto-discovery.
+You can discover stations based upon the system location.
+Select Vigicrues binding and click scan in order to discover new stations.
+The first scan will proceed with stations located in a radius of 10 km.
+This radius will increase by 10 km at each new scan.
## Binding Configuration
## Channels
+Once created, at first initialization, the thing will discover its capabilities (available data) using the webservices apis.
+Channels will be presented depending upon actual available data.
+
The VigiCrues information that retrieved are made available with these channels:
-| Channel ID | Item Type | Description |
-|------------------|---------------------------|-------------------------------|
-| observation-time | DateTime | Date and time of measurement |
-| flow | Number:VolumetricFlowRate | Volume of water per time unit |
-| height | Number:Length | Water height of the river |
+| Channel ID | Item Type | Description |
+|------------------|---------------------------|------------------------------------------------------------|
+| observation-time | DateTime | Date and time of measurement |
+| flow | Number:VolumetricFlowRate | Volume of water per time unit |
+| height | Number:Length | Water height of the river |
+| relative-height | Number:Dimensionless | Current water level toward lowest historical flood |
+| relative-flow | Number:Dimensionless | Current water flow tower lowest historical flood |
+| alert (*) | Number | Flooding alert level of the portion related to the station |
+| alert-icon | Image | Pictogram associated to the alert level |
+| short-comment | String | Description of the alert level |
+| comment | String | Detailed informations regarding the ongoing event |
+
+(*) Each alert level is described by a color :
+
+| Code | Color | Description |
+|------|--------|-------------------------------------------|
+| 0 | Green | No particular vigilance |
+| 1 | Yellow | Be attentive to the flooding situation |
+| 2 | Orange | Be "very vigilant" in the concerned areas |
+| 3 | Red | Absolute vigilance required |
## Full Example
--- /dev/null
+/**
+ * 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.vigicrues.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link StationConfiguration} is the class used to match the
+ * thing configuration.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class StationConfiguration {
+ public static String ID = "id";
+
+ public String id = "";
+ public int refresh = 30;
+}
public class VigiCruesBindingConstants {
public static final String BINDING_ID = "vigicrues";
- public static final String OPENDATASOFT_URL = "https://public.opendatasoft.com/api/records/1.0/search/";
// List of all Thing Type UIDs
- public static final ThingTypeUID THING_TYPE_VIGI_CRUES = new ThingTypeUID(BINDING_ID, "station");
+ public static final ThingTypeUID THING_TYPE_STATION = new ThingTypeUID(BINDING_ID, "station");
// List of all Channel id's
public static final String OBSERVATION_TIME = "observation-time";
public static final String HEIGHT = "height";
public static final String FLOW = "flow";
-
+ public static final String ALERT = "alert";
public static final String COMMENT = "comment";
-
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_VIGI_CRUES);
+ public static final String RELATIVE_PREFIX = "relative";
+ public static final String RELATIVE_HEIGHT = RELATIVE_PREFIX + "-" + HEIGHT;
+ public static final String RELATIVE_FLOW = RELATIVE_PREFIX + "-" + FLOW;
+ public static final String SHORT_COMMENT = "short-" + COMMENT;
+
+ // List of properties Labels
+ public static final String TRONCON = "Tronçon";
+ public static final String DISTANCE = "Distance";
+ public static final String RIVER = "Cours";
+ public static final String LOCATION = "Location";
+ public static final String FLOOD = "Crue";
+
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_STATION);
}
+++ /dev/null
-/**
- * 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.vigicrues.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link VigiCruesConfiguration} is the class used to match the
- * thing configuration.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class VigiCruesConfiguration {
- public String id = "";
- public int refresh = 30;
-}
*/
package org.openhab.binding.vigicrues.internal;
-import static org.openhab.binding.vigicrues.internal.VigiCruesBindingConstants.*;
-
-import java.time.ZonedDateTime;
+import static org.openhab.binding.vigicrues.internal.VigiCruesBindingConstants.SUPPORTED_THING_TYPES_UIDS;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.vigicrues.internal.api.ApiHandler;
import org.openhab.binding.vigicrues.internal.handler.VigiCruesHandler;
-import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializer;
-
/**
* The {@link VigiCruesHandlerFactory} is responsible for creating things and thing
* handlers.
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.vigicrues")
@NonNullByDefault
public class VigiCruesHandlerFactory extends BaseThingHandlerFactory {
- private final Gson gson;
- // Needed for converting UTC time to local time
- private final TimeZoneProvider timeZoneProvider;
+ private final LocationProvider locationProvider;
+ private final ApiHandler apiHandler;
@Activate
- public VigiCruesHandlerFactory(@Reference TimeZoneProvider timeZoneProvider) {
- this.timeZoneProvider = timeZoneProvider;
- this.gson = new GsonBuilder()
- .registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer<ZonedDateTime>) (json, type,
- jsonDeserializationContext) -> ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString()))
- .create();
+ public VigiCruesHandlerFactory(@Reference ApiHandler apiHandler, @Reference LocationProvider locationProvider) {
+ this.locationProvider = locationProvider;
+ this.apiHandler = apiHandler;
}
@Override
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
-
- if (thingTypeUID.equals(THING_TYPE_VIGI_CRUES)) {
- return new VigiCruesHandler(thing, timeZoneProvider, gson);
- }
-
- return null;
+ return supportsThingType(thingTypeUID) ? new VigiCruesHandler(thing, locationProvider, apiHandler) : null;
}
}
--- /dev/null
+/**
+ * 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.vigicrues.internal.api;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.Locale;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.vigicrues.internal.dto.hubeau.HubEauResponse;
+import org.openhab.binding.vigicrues.internal.dto.opendatasoft.OpenDatasoftResponse;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.CdStationHydro;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.InfoVigiCru;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.TerEntVigiCru;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.TronEntVigiCru;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.core.library.types.PointType;
+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.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link ApiHandler} is the responsible to call a given
+ * url and transform the answer in the appropriate dto class
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+
+@Component(service = ApiHandler.class)
+@NonNullByDefault
+public class ApiHandler {
+ private static final int TIMEOUT_MS = 30000;
+ private final Gson gson;
+
+ @Activate
+ public ApiHandler(@Reference TimeZoneProvider timeZoneProvider) {
+ this.gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class,
+ (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
+ .parse(json.getAsJsonPrimitive().getAsString())
+ .withZoneSameInstant(timeZoneProvider.getTimeZone()))
+ .create();
+ }
+
+ private <T> T execute(String url, Class<T> responseType) throws VigiCruesException {
+ String jsonResponse = "";
+ try {
+ jsonResponse = HttpUtil.executeUrl("GET", url, TIMEOUT_MS);
+ return gson.fromJson(jsonResponse, responseType);
+ } catch (IOException e) {
+ throw new VigiCruesException(e);
+ } catch (JsonSyntaxException e) {
+ throw new VigiCruesException(e);
+ }
+ }
+
+ public InfoVigiCru getTronconStatus(String tronconId) throws VigiCruesException {
+ final String BASE_URL = "https://www.vigicrues.gouv.fr/services/1/InfoVigiCru.jsonld/?TypEntVigiCru=8&CdEntVigiCru=%s";
+ return execute(String.format(BASE_URL, tronconId), InfoVigiCru.class);
+ }
+
+ public TronEntVigiCru getTroncon(String stationId) throws VigiCruesException {
+ final String BASE_URL = "https://www.vigicrues.gouv.fr/services/1/TronEntVigiCru.jsonld/?TypEntVigiCru=8&CdEntVigiCru=%s";
+ return execute(String.format(BASE_URL, stationId), TronEntVigiCru.class);
+ }
+
+ public TerEntVigiCru getTerritoire(String stationId) throws VigiCruesException {
+ final String BASE_URL = "https://www.vigicrues.gouv.fr/services/1/TerEntVigiCru.jsonld/?TypEntVigiCru=5&CdEntVigiCru=%s";
+ return execute(String.format(BASE_URL, stationId), TerEntVigiCru.class);
+ }
+
+ public CdStationHydro getStationDetails(String stationId) throws VigiCruesException {
+ final String BASE_URL = "https://www.vigicrues.gouv.fr/services/station.json/index.php?CdStationHydro=%s";
+ return execute(String.format(BASE_URL, stationId), CdStationHydro.class);
+ }
+
+ public OpenDatasoftResponse getMeasures(String stationId) throws VigiCruesException {
+ final String BASE_URL = "https://public.opendatasoft.com/api/records/1.0/search/?dataset=vigicrues&sort=timestamp&q=%s";
+ return execute(String.format(BASE_URL, stationId), OpenDatasoftResponse.class);
+ }
+
+ public HubEauResponse discoverStations(PointType location, int range) throws VigiCruesException {
+ final String BASE_URL = "https://hubeau.eaufrance.fr/api/v1/hydrometrie/referentiel/stations?format=json&size=2000";
+
+ return execute(
+ BASE_URL + String.format(Locale.US, "&latitude=%.2f&longitude=%.2f&distance=%d",
+ location.getLatitude().floatValue(), location.getLongitude().floatValue(), range),
+ HubEauResponse.class);
+ }
+
+ public HubEauResponse discoverStations(String stationId) throws VigiCruesException {
+ final String BASE_URL = "https://hubeau.eaufrance.fr/api/v1/hydrometrie/referentiel/stations?format=json&size=2000";
+ return execute(BASE_URL + String.format("&code_station=%s", stationId), HubEauResponse.class);
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.api;
+
+import java.net.URL;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.net.http.TlsCertificateProvider;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Provides a TrustManager for the VigiCrues SSL certificate
+ *
+ * @author Gaël L'hopital - Initial Contribution
+ */
+@Component
+@NonNullByDefault
+public class VigiCruesCertificateProvider implements TlsCertificateProvider {
+
+ @Override
+ public String getHostName() {
+ return "www.vigicrues.gouv.fr";
+ }
+
+ @Override
+ public URL getCertificate() {
+ URL resource = Thread.currentThread().getContextClassLoader().getResource("vigicrues.cer");
+ if (resource != null) {
+ return resource;
+ } else {
+ throw new IllegalStateException("Certifcate resource not found or not accessible");
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Exception for errors when using the VigiCrues API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class VigiCruesException extends Exception {
+ private static final long serialVersionUID = -7781683052187130152L;
+
+ public VigiCruesException(Throwable e) {
+ super(null, e);
+ }
+
+ public VigiCruesException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public VigiCruesException(String msg) {
+ super(msg, null);
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.discovery;
+
+import static org.openhab.binding.vigicrues.internal.VigiCruesBindingConstants.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.vigicrues.internal.StationConfiguration;
+import org.openhab.binding.vigicrues.internal.api.ApiHandler;
+import org.openhab.binding.vigicrues.internal.api.VigiCruesException;
+import org.openhab.binding.vigicrues.internal.dto.hubeau.HubEauResponse;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+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.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 VigiCruesDiscoveryService} searches for available
+ * hydro stations discoverable through API
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@Component(service = DiscoveryService.class, configurationPid = "discovery.vigicrues")
+@NonNullByDefault
+public class VigiCruesDiscoveryService extends AbstractDiscoveryService {
+ private static final int SEARCH_TIME = 5;
+
+ private final Logger logger = LoggerFactory.getLogger(VigiCruesDiscoveryService.class);
+ private final LocationProvider locationProvider;
+ private final ApiHandler apiHandler;
+
+ private int searchRange = 10;
+
+ @Activate
+ public VigiCruesDiscoveryService(@Reference ApiHandler apiHandler, @Reference LocationProvider locationProvider) {
+ super(SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME, false);
+ this.apiHandler = apiHandler;
+ this.locationProvider = locationProvider;
+ }
+
+ @Override
+ public void startScan() {
+ PointType location = locationProvider.getLocation();
+ if (location != null) {
+ try {
+ HubEauResponse response = apiHandler.discoverStations(location, searchRange);
+ if (response.count > 0) {
+ response.stations.stream().filter(station -> station.enService).forEach(station -> {
+ thingDiscovered(DiscoveryResultBuilder
+ .create(new ThingUID(THING_TYPE_STATION, station.codeStation))
+ .withLabel(station.libelleStation).withRepresentationProperty(StationConfiguration.ID)
+ .withProperty(StationConfiguration.ID, station.codeStation).build());
+ });
+ } else {
+ logger.info("No station exists in a neighbourhood of {} km", searchRange);
+ }
+ } catch (VigiCruesException e) {
+ logger.warn("Error discovering nearby hydro stations : {}", e.getMessage());
+ }
+ searchRange += 10;
+ }
+ stopScan();
+ }
+}
--- /dev/null
+package org.openhab.binding.vigicrues.internal.dto.hubeau;
+
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+public class HubEauResponse {
+ public class StationData {
+ @SerializedName("en_service")
+ public boolean enService;
+
+ @SerializedName("code_station")
+ public String codeStation;
+
+ @SerializedName("libelle_station")
+ public String libelleStation;
+
+ @SerializedName("longitude_station")
+ public double longitudeStation;
+
+ @SerializedName("latitude_station")
+ public double latitudeStation;
+
+ @SerializedName("libelle_cours_eau")
+ public String libelleCoursEau;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("code_site")
+ * public String codeSite;
+ *
+ * @SerializedName("libelle_site")
+ * public String libelleSite;
+ *
+ * @SerializedName("type_station")
+ * public String typeStation;
+ *
+ * @SerializedName("coordonnee_x_station")
+ * public int coordonneeXStation;
+ *
+ * @SerializedName("coordonnee_y_station")
+ * public int coordonneeYStation;
+ *
+ * @SerializedName("code_projection")
+ * public int codeProjection;
+ *
+ * @SerializedName("influence_locale_station")
+ * public int influenceLocaleStation;
+ *
+ * @SerializedName("commentaire_station")
+ * public String commentaireStation;
+ *
+ * @SerializedName("altitude_ref_alti_station")
+ * public double altitudeRefAltiStation;
+ *
+ * @SerializedName("code_systeme_alti_site")
+ * public int codeSystemeAltiSite;
+ *
+ * @SerializedName("code_commune_station")
+ * public String codeCommuneStation;
+ *
+ * @SerializedName("libelle_commune")
+ * public String libelleCommune;
+ *
+ * @SerializedName("code_departement")
+ * public String codeDepartement;
+ *
+ * @SerializedName("code_region")
+ * public String codeRegion;
+ *
+ * @SerializedName("libelle_region")
+ * public String libelleRegion;
+ *
+ * @SerializedName("code_cours_eau")
+ * public String codeCoursEau;
+ *
+ * @SerializedName("uri_cours_eau")
+ * public String uriCoursEau;
+ *
+ * @SerializedName("descriptif_station")
+ * public String descriptifStation;
+ *
+ * @SerializedName("date_maj_station")
+ * public String dateMajStation;
+ *
+ * @SerializedName("date_ouverture_station")
+ * public String dateOuvertureStation;
+ *
+ * @SerializedName("date_fermeture_station")
+ * public String dateFermetureStation;
+ *
+ * @SerializedName("commentaire_influence_locale_station")
+ * public String commentaireInfluenceLocaleStation;
+ *
+ * @SerializedName("code_regime_station")
+ * public int codeRegimeStation;
+ *
+ * @SerializedName("qualification_donnees_station")
+ * public int qualificationDonneesStation;
+ *
+ * @SerializedName("code_finalite_station")
+ * public int codeFinaliteStation;
+ *
+ * @SerializedName("type_contexte_loi_stat_station")
+ * public int typeContexteLoiStatStation;
+ *
+ * @SerializedName("type_loi_station")
+ * public int typeLoiStation;
+ *
+ * @SerializedName("code_sandre_reseau_station")
+ * public List<String> codeSandreReseauStation;
+ *
+ * @SerializedName("date_debut_ref_alti_station")
+ * public String dateDebutRefAltiStation;
+ *
+ * @SerializedName("date_activation_ref_alti_station")
+ * public String dateActivationRefAltiStation;
+ *
+ * @SerializedName("date_maj_ref_alti_station")
+ * public String dateMajRefAltiStation;
+ *
+ * @SerializedName("libelle_departement")
+ * public String libelleDepartement;
+ * public Geometry geometry;
+ */
+ }
+
+ public int count;
+ @SerializedName("data")
+ public List<StationData> stations;
+
+ /*
+ * Currently unused, maybe interesting in the future
+ * public String first;
+ * public String last;
+ * public String prev;
+ * public String next;
+ *
+ * @SerializedName("api_version")
+ * public String apiVersion;
+ *
+ * public class Crs {
+ * public String type;
+ * public Properties properties;
+ * }
+ *
+ * public class Properties {
+ * public String name;
+ * }
+ *
+ * public class Geometry {
+ * public String type;
+ * public Crs crs;
+ * public List<Double> coordinates;
+ * }
+ */
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.opendatasoft;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link OpenDatasoftResponse} is the Java class used to map the JSON
+ * response to an opendatasoft endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class OpenDatasoftResponse {
+ @SerializedName("nhits")
+ private int nHits;
+ private @Nullable Parameters parameters;
+ private List<Record> records = new ArrayList<>();
+
+ public int getNHits() {
+ return nHits;
+ }
+
+ public Optional<Parameters> getParameters() {
+ Parameters parameters = this.parameters;
+ if (parameters != null) {
+ return Optional.of(parameters);
+ }
+ return Optional.empty();
+ }
+
+ public Optional<VigiCruesFields> getFirstRecord() {
+ return records.stream().findFirst().flatMap(Record::getFields);
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.opendatasoft;
+
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link Parameters} is the Java class used to map the JSON
+ * response to the webservice request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Parameters {
+ private String[] dataset = {};
+ private String timezone = "";
+ private int rows;
+ private String format = "";
+ private @Nullable Refine refine;
+ private String[] facet = {};
+
+ public Optional<String> getDataset() {
+ return Arrays.stream(dataset).findFirst();
+ }
+
+ public ZoneId getTimezone() {
+ return ZoneId.of(timezone);
+ }
+
+ public int getRows() {
+ return rows;
+ }
+
+ public String getFormat() {
+ return format;
+ }
+
+ public Optional<Refine> getRefine() {
+ Refine refine = this.refine;
+ if (refine != null) {
+ return Optional.of(refine);
+ }
+ return Optional.empty();
+ }
+
+ public String[] getFacets() {
+ return facet;
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.opendatasoft;
+
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Record} is the Java class used to map the JSON
+ * response to the webservice request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Record {
+ @SerializedName("datasetid")
+ private String datasetId = "";
+ @SerializedName("recordid")
+ private String recordId = "";
+ @SerializedName("record_timestamp")
+ private String recordTimestamp = "";
+ private @Nullable VigiCruesFields fields;
+
+ public String getDatasetId() {
+ return datasetId;
+ }
+
+ public String getRecordId() {
+ return recordId;
+ }
+
+ public String getRecordTimestamp() {
+ return recordTimestamp;
+ }
+
+ public Optional<VigiCruesFields> getFields() {
+ VigiCruesFields fields = this.fields;
+ if (fields != null) {
+ return Optional.of(fields);
+ }
+ return Optional.empty();
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.opendatasoft;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Refine} is the Java class used to map the JSON
+ * response to the webservice request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class Refine {
+ @SerializedName("nom_dept")
+ private String departmentName = "";
+
+ public String getDepartmentName() {
+ return departmentName;
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.opendatasoft;
+
+import java.time.ZonedDateTime;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link VigiCruesFields} is the Java class used to map the JSON
+ * response to the webservice request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class VigiCruesFields {
+ @SerializedName("debit")
+ private @Nullable Double flow;
+ @SerializedName("hauteur")
+ private @Nullable Double height;
+ private @Nullable ZonedDateTime timestamp;
+
+ public Optional<ZonedDateTime> getTimestamp() {
+ ZonedDateTime timestamp = this.timestamp;
+ if (timestamp != null) {
+ return Optional.of(timestamp);
+ }
+ return Optional.empty();
+ }
+
+ public Optional<Double> getFlow() {
+ Double flow = this.flow;
+ if (flow != null) {
+ return Optional.of(flow);
+ }
+ return Optional.empty();
+ }
+
+ public Optional<Double> getHeight() {
+ Double height = this.height;
+ if (height != null) {
+ return Optional.of(height);
+ }
+ return Optional.empty();
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.vigicrues;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.openhab.binding.vigicrues.internal.VigiCruesBindingConstants;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link CdStationHydro} is the Java class used to map the JSON
+ * response to an vigicrue api endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class CdStationHydro {
+
+ public class PereBoitEntVigiCru {
+ @SerializedName("CdEntVigiCru")
+ public String cdEntVigiCru;
+ }
+
+ public class CruesHistorique {
+ @SerializedName("LbUsuel")
+ public String name;
+ @SerializedName("ValHauteur")
+ public double height;
+ @SerializedName("ValDebit")
+ public double flow;
+
+ public Map<String, String> getDescription() {
+ Map<String, String> result = new HashMap<>();
+ if (height != 0) {
+ result.put(String.format("%s %s (%s)", VigiCruesBindingConstants.FLOOD,
+ VigiCruesBindingConstants.HEIGHT, name), String.format(Locale.US, "%.2f m", height));
+ }
+ if (flow != 0) {
+ result.put(String.format("%s %s (%s)", VigiCruesBindingConstants.FLOOD, VigiCruesBindingConstants.FLOW,
+ name), String.format(Locale.US, "%.2f m³/s", flow));
+ }
+ return result;
+ }
+ }
+
+ public class VigilanceCrues {
+ @SerializedName("PereBoitEntVigiCru")
+ public PereBoitEntVigiCru pereBoitEntVigiCru;
+ @SerializedName("CruesHistoriques")
+ public List<CruesHistorique> cruesHistoriques;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("StationPrevision")
+ * public String stationPrevision;
+ *
+ * @SerializedName("Photo")
+ * public String photo;
+ *
+ * @SerializedName("ZoomInitial")
+ * public String zoomInitial;
+ */
+ }
+
+ @SerializedName("VigilanceCrues")
+ public VigilanceCrues vigilanceCrues;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("VersionFlux")
+ * public String versionFlux;
+ *
+ * @SerializedName("CdStationHydro")
+ * public String cdStationHydro;
+ *
+ * @SerializedName("LbStationHydro")
+ * public String lbStationHydro;
+ *
+ * @SerializedName("LbCoursEau")
+ * public String lbCoursEau;
+ *
+ * @SerializedName("CdStationHydroAncienRef")
+ * public String cdStationHydroAncienRef;
+ *
+ * @SerializedName("CdCommune")
+ * public String cdCommune;
+ *
+ * @SerializedName("error_msg")
+ * public String errorMsg;
+ */
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.vigicrues;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link InfoVigiCru} is the Java class used to map the JSON
+ * response to an vigicrue api endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class InfoVigiCru {
+ public class VicInfoVigiCru {
+ @SerializedName("vic:NivInfoVigiCru")
+ public int vicNivInfoVigiCru;
+ @SerializedName("vic:SituActuInfoVigiCru")
+ public String vicSituActuInfoVigiCru;
+ @SerializedName("vic:QualifInfoVigiCru")
+ public String vicQualifInfoVigiCru;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("vic:RefInfoVigiCru")
+ * private String vicRefInfoVigiCru;
+ *
+ * @SerializedName("vic:TypInfoVigiCru")
+ * private int vicTypInfoVigiCru;
+ *
+ * @SerializedName("vic:DtHrInfoVigiCru")
+ * private String vicDtHrInfoVigiCru;
+ *
+ * @SerializedName("vic:DtHrSuivInfoVigiCru")
+ * private String vicDtHrSuivInfoVigiCru;
+ *
+ * @SerializedName("vic:EstNivCalInfoVigiCru")
+ * private Boolean vicEstNivCalInfoVigiCru;
+ *
+ * @SerializedName("vic:StInfoVigiCru")
+ * private int vicStInfoVigiCru;
+ */
+ }
+
+ @SerializedName("vic:InfoVigiCru")
+ public VicInfoVigiCru vicInfoVigiCru;
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.vigicrues;
+
+import java.util.List;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link TerEntVigiCru} is the Java class used to map the JSON
+ * response to an vigicrue api endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class TerEntVigiCru {
+
+ public class VicTerEntVigiCru {
+ @SerializedName("vic:aNMoinsUn")
+ public List<VicANMoinsUn> vicANMoinsUn;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("@id")
+ * public String id;
+ *
+ * @SerializedName("vic:CdEntVigiCru")
+ * public String vicCdEntVigiCru;
+ *
+ * @SerializedName("vic:TypEntVigiCru")
+ * public String vicTypEntVigiCru;
+ *
+ * @SerializedName("vic:LbEntVigiCru")
+ * public String vicLbEntVigiCru;
+ *
+ * @SerializedName("vic:DtHrCreatEntVigiCru")
+ * public String vicDtHrCreatEntVigiCru;
+ *
+ * @SerializedName("vic:DtHrMajEntVigiCru")
+ * public String vicDtHrMajEntVigiCru;
+ *
+ * @SerializedName("vic:StEntVigiCru")
+ * public String vicStEntVigiCru;
+ * public int count_aNMoinsUn;
+ *
+ * @SerializedName("LinkInfoCru")
+ * public String linkInfoCru;
+ */
+ }
+
+ @SerializedName("vic:TerEntVigiCru")
+ public VicTerEntVigiCru vicTerEntVigiCru;
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.vigicrues;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link TronEntVigiCru} is the Java class used to map the JSON
+ * response to an vigicrue api endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class TronEntVigiCru {
+ public class VicStaEntVigiCru {
+ @SerializedName("vic:CdEntVigiCru")
+ public String vicCdEntVigiCru;
+ }
+
+ public class VicTronEntVigiCru {
+ @SerializedName("vic:aNMoinsUn")
+ public List<VicStaEntVigiCru> stations;
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("@id")
+ * public String id;
+ *
+ * @SerializedName("vic:CdEntVigiCru")
+ * public String vicCdEntVigiCru;
+ *
+ * @SerializedName("vic:TypEntVigiCru")
+ * public String vicTypEntVigiCru;
+ *
+ * @SerializedName("vic:LbEntVigiCru")
+ * public String vicLbEntVigiCru;
+ *
+ * @SerializedName("vic:DtHrCreatEntVigiCru")
+ * public String vicDtHrCreatEntVigiCru;
+ *
+ * @SerializedName("vic:DtHrMajEntVigiCru")
+ * public String vicDtHrMajEntVigiCru;
+ *
+ * @SerializedName("vic:StEntVigiCru")
+ * public String vicStEntVigiCru;
+ *
+ * @SerializedName("count_aNPlusUn")
+ * public Integer countANPlusUn;
+ *
+ * @SerializedName("count_aNMoinsUn")
+ * public Integer countANMoinsUn;
+ *
+ * @SerializedName("LinkInfoCru")
+ * public String linkInfoCru;
+ */
+ }
+
+ @SerializedName("vic:TronEntVigiCru")
+ private VicTronEntVigiCru tronTerEntVigiCru;
+
+ public Stream<VicStaEntVigiCru> getStations() {
+ if (tronTerEntVigiCru != null && tronTerEntVigiCru.stations != null) {
+ return tronTerEntVigiCru.stations.stream();
+ } else {
+ return Stream.empty();
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.vigicrues.internal.dto.vigicrues;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link VicANMoinsUn} is the Java class used to map the JSON
+ * response to an vigicrue api endpoint request.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class VicANMoinsUn {
+ @SerializedName("vic.CdEntVigiCru")
+ public String vicCdEntVigiCru;
+
+ /*
+ * Currently unused, maybe interesting in the future
+ *
+ * @SerializedName("@id")
+ * private String id;
+ *
+ * @SerializedName("vic.TypEntVigiCru")
+ * private String vicTypEntVigiCru;
+ *
+ * @SerializedName("vic.LbEntVigiCru")
+ * private String vicLbEntVigiCru;
+ *
+ * @SerializedName("LinkEntity")
+ * private String linkEntity;
+ *
+ * @SerializedName("LinkInfoCru")
+ * private String linkInfoCru;
+ */
+}
package org.openhab.binding.vigicrues.internal.handler;
import static org.openhab.binding.vigicrues.internal.VigiCruesBindingConstants.*;
-import static org.openhab.core.library.unit.SmartHomeUnits.CUBICMETRE_PER_SECOND;
-import static tec.uom.se.unit.Units.METRE;
import java.io.IOException;
-import java.net.MalformedURLException;
+import java.io.InputStream;
import java.time.ZonedDateTime;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.vigicrues.internal.VigiCruesConfiguration;
-import org.openhab.binding.vigicrues.internal.json.OpenDatasoftResponse;
-import org.openhab.binding.vigicrues.internal.json.Record;
-import org.openhab.core.i18n.TimeZoneProvider;
-import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.binding.vigicrues.internal.StationConfiguration;
+import org.openhab.binding.vigicrues.internal.api.ApiHandler;
+import org.openhab.binding.vigicrues.internal.api.VigiCruesException;
+import org.openhab.binding.vigicrues.internal.dto.hubeau.HubEauResponse;
+import org.openhab.binding.vigicrues.internal.dto.opendatasoft.OpenDatasoftResponse;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.CdStationHydro;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.InfoVigiCru;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.TerEntVigiCru;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.TronEntVigiCru;
+import org.openhab.binding.vigicrues.internal.dto.vigicrues.VicANMoinsUn;
+import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.RawType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.SmartHomeUnits;
+import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.gson.Gson;
-
/**
* The {@link VigiCruesHandler} is responsible for querying the API and
* updating channels
*/
@NonNullByDefault
public class VigiCruesHandler extends BaseThingHandler {
- private static final String URL = OPENDATASOFT_URL + "?dataset=vigicrues&sort=timestamp&q=";
- private static final int TIMEOUT_MS = 30000;
private final Logger logger = LoggerFactory.getLogger(VigiCruesHandler.class);
+ private final LocationProvider locationProvider;
- // Time zone provider representing time zone configured in openHAB configuration
- private final TimeZoneProvider timeZoneProvider;
- private final Gson gson;
private @Nullable ScheduledFuture<?> refreshJob;
- private @Nullable String queryUrl;
+ private final ApiHandler apiHandler;
+
+ private List<QuantityType<?>> referenceHeights = new ArrayList<>();
+ private List<QuantityType<?>> referenceFlows = new ArrayList<>();
+ private @Nullable String portion;
- public VigiCruesHandler(Thing thing, TimeZoneProvider timeZoneProvider, Gson gson) {
+ public VigiCruesHandler(Thing thing, LocationProvider locationProvider, ApiHandler apiHandler) {
super(thing);
- this.timeZoneProvider = timeZoneProvider;
- this.gson = gson;
+ this.apiHandler = apiHandler;
+ this.locationProvider = locationProvider;
}
@Override
public void initialize() {
logger.debug("Initializing VigiCrues handler.");
- VigiCruesConfiguration config = getConfigAs(VigiCruesConfiguration.class);
- logger.debug("config station = {}", config.id);
+ StationConfiguration config = getConfigAs(StationConfiguration.class);
logger.debug("config refresh = {} min", config.refresh);
updateStatus(ThingStatus.UNKNOWN);
- queryUrl = URL + config.id;
+
+ if (thing.getProperties().isEmpty()) {
+ Map<String, String> properties = discoverAttributes(config);
+ updateProperties(properties);
+ }
+ getReferences();
refreshJob = scheduler.scheduleWithFixedDelay(this::updateAndPublish, 0, config.refresh, TimeUnit.MINUTES);
}
+ private void getReferences() {
+ List<QuantityType<?>> heights = new ArrayList<>();
+ List<QuantityType<?>> flows = new ArrayList<>();
+ thing.getProperties().keySet().stream().filter(key -> key.startsWith(FLOOD)).forEach(key -> {
+ String value = thing.getProperties().get(key);
+ if (key.contains(FLOW)) {
+ flows.add(new QuantityType<>(value));
+ } else {
+ heights.add(new QuantityType<>(value));
+ }
+ });
+ referenceHeights = heights.stream().distinct().sorted().collect(Collectors.toList());
+ referenceFlows = flows.stream().distinct().sorted().collect(Collectors.toList());
+ portion = thing.getProperties().get(TRONCON);
+ }
+
+ private Map<String, String> discoverAttributes(StationConfiguration config) {
+ Map<String, String> properties = new HashMap<>();
+
+ ThingBuilder thingBuilder = editThing();
+ List<Channel> channels = new ArrayList<>(getThing().getChannels());
+
+ try {
+ HubEauResponse stationDetails = apiHandler.discoverStations(config.id);
+ stationDetails.stations.stream().findFirst().ifPresent(station -> {
+ PointType stationLocation = new PointType(
+ String.format(Locale.US, "%f,%f", station.latitudeStation, station.longitudeStation));
+ properties.put(LOCATION, stationLocation.toString());
+ PointType serverLocation = locationProvider.getLocation();
+ if (serverLocation != null) {
+ DecimalType distance = serverLocation.distanceFrom(stationLocation);
+ properties.put(DISTANCE, new QuantityType<>(distance, SIUnits.METRE).toString());
+ }
+ properties.put(RIVER, station.libelleCoursEau);
+ });
+ } catch (VigiCruesException e) {
+ logger.info("Unable to retrieve station location details {} : {}", config.id, e.getMessage());
+ }
+
+ try {
+ CdStationHydro refineStation = apiHandler.getStationDetails(config.id);
+ if (refineStation.vigilanceCrues.cruesHistoriques == null) {
+ throw new VigiCruesException("No historical data available");
+ }
+ refineStation.vigilanceCrues.cruesHistoriques.stream()
+ .forEach(crue -> properties.putAll(crue.getDescription()));
+ String codeTerritoire = refineStation.vigilanceCrues.pereBoitEntVigiCru.cdEntVigiCru;
+ TerEntVigiCru territoire = apiHandler.getTerritoire(codeTerritoire);
+ for (VicANMoinsUn troncon : territoire.vicTerEntVigiCru.vicANMoinsUn) {
+ TronEntVigiCru detail = apiHandler.getTroncon(troncon.vicCdEntVigiCru);
+ if (detail.getStations().anyMatch(s -> config.id.equalsIgnoreCase(s.vicCdEntVigiCru))) {
+ properties.put(TRONCON, troncon.vicCdEntVigiCru);
+ break;
+ }
+ }
+ } catch (VigiCruesException e) {
+ logger.info("Historical flooding data are not available {} : {}", config.id, e.getMessage());
+ channels.removeIf(channel -> (channel.getUID().getId().contains(RELATIVE_PREFIX)));
+ }
+
+ if (!properties.containsKey(TRONCON)) {
+ channels.removeIf(channel -> (channel.getUID().getId().contains(ALERT)));
+ channels.removeIf(channel -> (channel.getUID().getId().contains(COMMENT)));
+ }
+
+ try {
+ OpenDatasoftResponse measures = apiHandler.getMeasures(config.id);
+ measures.getFirstRecord().ifPresent(field -> {
+ if (field.getHeight().isEmpty()) {
+ channels.removeIf(channel -> (channel.getUID().getId().contains(HEIGHT)));
+ }
+ if (field.getFlow().isEmpty()) {
+ channels.removeIf(channel -> (channel.getUID().getId().contains(FLOW)));
+ }
+ });
+ } catch (VigiCruesException e) {
+ logger.warn("Unable to read measurements data {} : {}", config.id, e.getMessage());
+ }
+
+ thingBuilder.withChannels(channels);
+ updateThing(thingBuilder.build());
+
+ return properties;
+ }
+
@Override
public void dispose() {
logger.debug("Disposing the VigiCrues handler.");
}
private void updateAndPublish() {
+ StationConfiguration config = getConfigAs(StationConfiguration.class);
try {
- if (queryUrl != null) {
- String response = HttpUtil.executeUrl("GET", queryUrl, TIMEOUT_MS);
- updateStatus(ThingStatus.ONLINE);
- OpenDatasoftResponse apiResponse = gson.fromJson(response, OpenDatasoftResponse.class);
- Arrays.stream(apiResponse.getRecords()).findFirst().flatMap(Record::getFields).ifPresent(field -> {
- field.getHeight().ifPresent(height -> updateQuantity(HEIGHT, height, METRE));
- field.getFlow().ifPresent(flow -> updateQuantity(FLOW, flow, CUBICMETRE_PER_SECOND));
- field.getTimestamp().ifPresent(date -> updateDate(OBSERVATION_TIME, date));
+ OpenDatasoftResponse measures = apiHandler.getMeasures(config.id);
+ measures.getFirstRecord().ifPresent(field -> {
+ field.getHeight().ifPresent(height -> {
+ updateQuantity(HEIGHT, height, SIUnits.METRE);
+ updateRelativeMeasure(RELATIVE_HEIGHT, referenceHeights, height);
});
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED,
- "queryUrl should never be null, but it is !");
+ field.getFlow().ifPresent(flow -> {
+ updateQuantity(FLOW, flow, SmartHomeUnits.CUBICMETRE_PER_SECOND);
+ updateRelativeMeasure(RELATIVE_FLOW, referenceFlows, flow);
+ });
+ field.getTimestamp().ifPresent(date -> updateDate(OBSERVATION_TIME, date));
+ });
+ String currentPortion = this.portion;
+ if (currentPortion != null) {
+ InfoVigiCru status = apiHandler.getTronconStatus(currentPortion);
+ updateAlert(ALERT, status.vicInfoVigiCru.vicNivInfoVigiCru - 1);
+ updateString(SHORT_COMMENT, status.vicInfoVigiCru.vicSituActuInfoVigiCru);
+ updateString(COMMENT, status.vicInfoVigiCru.vicQualifInfoVigiCru);
}
- } catch (MalformedURLException e) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- String.format("Querying '%s' raised : %s", queryUrl, e.getMessage()));
- } catch (IOException e) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- String.format("Error opening connection to VigiCrues webservice : {}", e.getMessage()));
+ updateStatus(ThingStatus.ONLINE);
+ } catch (VigiCruesException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ private void updateString(String channelId, String value) {
+ if (isLinked(channelId)) {
+ updateState(channelId, new StringType(value));
+ }
+ }
+
+ private void updateRelativeMeasure(String channelId, List<QuantityType<?>> reference, double value) {
+ if (reference.size() > 0) {
+ double percent = value / reference.get(0).doubleValue() * 100;
+ updateQuantity(channelId, percent, SmartHomeUnits.PERCENT);
}
}
public void updateDate(String channelId, ZonedDateTime zonedDateTime) {
if (isLinked(channelId)) {
- ZonedDateTime localDateTime = zonedDateTime.withZoneSameInstant(timeZoneProvider.getTimeZone());
- updateState(channelId, new DateTimeType(localDateTime));
+ updateState(channelId, new DateTimeType(zonedDateTime));
+ }
+ }
+
+ public void updateAlert(String channelId, int value) {
+ String channelIcon = channelId + "-icon";
+ if (isLinked(channelId)) {
+ updateState(channelId, new DecimalType(value));
+ }
+ if (isLinked(channelIcon)) {
+ byte[] resource = getResource(String.format("picto/crue-%d.svg", value));
+ updateState(channelIcon, resource != null ? new RawType(resource, "image/svg+xml") : UnDefType.UNDEF);
+ }
+ }
+
+ public byte @Nullable [] getResource(String iconPath) {
+ try (InputStream stream = VigiCruesHandler.class.getClassLoader().getResourceAsStream(iconPath)) {
+ return stream.readAllBytes();
+ } catch (IOException e) {
+ logger.warn("Unable to load ressource '{}' : {}", iconPath, e.getMessage());
}
+ return null;
}
}
+++ /dev/null
-/**
- * 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.vigicrues.internal.json;
-
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link OpenDatasoftResponse} is the Java class used to map the JSON
- * response to an opendatasoft endpoint request.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class OpenDatasoftResponse {
- @SerializedName("nhits")
- private int nHits;
- private @Nullable Parameters parameters;
- private Record[] records = {};
-
- public int getNHits() {
- return nHits;
- }
-
- public Optional<Parameters> getParameters() {
- Parameters parameters = this.parameters;
- if (parameters != null) {
- return Optional.of(parameters);
- }
- return Optional.empty();
- }
-
- public Record[] getRecords() {
- return records;
- }
-}
+++ /dev/null
-/**
- * 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.vigicrues.internal.json;
-
-import java.time.ZoneId;
-import java.util.Arrays;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * The {@link Parameters} is the Java class used to map the JSON
- * response to the webservice request.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class Parameters {
- private String[] dataset = {};
- private String timezone = "";
- private int rows;
- private String format = "";
- private @Nullable Refine refine;
- private String[] facet = {};
-
- public Optional<String> getDataset() {
- return Arrays.stream(dataset).findFirst();
- }
-
- public ZoneId getTimezone() {
- return ZoneId.of(timezone);
- }
-
- public int getRows() {
- return rows;
- }
-
- public String getFormat() {
- return format;
- }
-
- public Optional<Refine> getRefine() {
- Refine refine = this.refine;
- if (refine != null) {
- return Optional.of(refine);
- }
- return Optional.empty();
- }
-
- public String[] getFacets() {
- return facet;
- }
-}
+++ /dev/null
-/**
- * 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.vigicrues.internal.json;
-
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link Record} is the Java class used to map the JSON
- * response to the webservice request.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class Record {
- @SerializedName("datasetid")
- private String datasetId = "";
- @SerializedName("recordid")
- private String recordId = "";
- @SerializedName("record_timestamp")
- private String recordTimestamp = "";
- private @Nullable VigiCruesFields fields;
-
- public String getDatasetId() {
- return datasetId;
- }
-
- public String getRecordId() {
- return recordId;
- }
-
- public String getRecordTimestamp() {
- return recordTimestamp;
- }
-
- public Optional<VigiCruesFields> getFields() {
- VigiCruesFields fields = this.fields;
- if (fields != null) {
- return Optional.of(fields);
- }
- return Optional.empty();
- }
-}
+++ /dev/null
-/**
- * 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.vigicrues.internal.json;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link Refine} is the Java class used to map the JSON
- * response to the webservice request.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class Refine {
- @SerializedName("nom_dept")
- private String departmentName = "";
-
- public String getDepartmentName() {
- return departmentName;
- }
-}
+++ /dev/null
-/**
- * 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.vigicrues.internal.json;
-
-import java.time.ZonedDateTime;
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link VigiCruesFields} is the Java class used to map the JSON
- * response to the webservice request.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@NonNullByDefault
-public class VigiCruesFields {
- @SerializedName("debit")
- private @Nullable Double flow;
- @SerializedName("hauteur")
- private @Nullable Double height;
- private @Nullable ZonedDateTime timestamp;
-
- public Optional<ZonedDateTime> getTimestamp() {
- ZonedDateTime timestamp = this.timestamp;
- if (timestamp != null) {
- return Optional.of(timestamp);
- }
- return Optional.empty();
- }
-
- public Optional<Double> getFlow() {
- Double flow = this.flow;
- if (flow != null) {
- return Optional.of(flow);
- }
- return Optional.empty();
- }
-
- public Optional<Double> getHeight() {
- Double height = this.height;
- if (height != null) {
- return Optional.of(height);
- }
- return Optional.empty();
- }
-}
<channels>
<channel id="height" typeId="height"/>
+ <channel id="relative-height" typeId="gauge">
+ <label>Relative Height</label>
+ <description>Current height toward historical floods.</description>
+ </channel>
<channel id="flow" typeId="flow"/>
+ <channel id="relative-flow" typeId="gauge">
+ <label>Relative Flow</label>
+ <description>Current flow toward historic floods.</description>
+ </channel>
+ <channel id="alert" typeId="alert-level"/>
+ <channel id="alert-icon" typeId="alert-icon"/>
+ <channel id="short-comment" typeId="comment">
+ <label>Info Qualification</label>
+ </channel>
+ <channel id="comment" typeId="comment">
+ <label>Situation</label>
+ </channel>
<channel id="observation-time" typeId="observation-time"/>
</channels>
+ <representation-property>id</representation-property>
+
<config-description>
<parameter name="id" type="text" required="true">
<label>Identifiant</label>
<channel-type id="flow">
<item-type>Number:VolumetricFlowRate</item-type>
<label>Current Flow</label>
+ <category>flow</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<item-type>DateTime</item-type>
<label>Observation Time</label>
<description>Observation date and time</description>
+ <category>time</category>
<state readOnly="true"/>
</channel-type>
<description>Water level in the river</description>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
+
+ <channel-type id="gauge">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Relative Measure</label>
+ <state readOnly="true" pattern="%.2f %unit%"/>
+ </channel-type>
+
+ <channel-type id="alert-level">
+ <item-type>Number</item-type>
+ <label>Alerte</label>
+ <state readOnly="true">
+ <options>
+ <option value="0">Vert</option>
+ <option value="1">Jaune</option>
+ <option value="2">Orange</option>
+ <option value="3">Rouge</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="comment">
+ <item-type>String</item-type>
+ <label>Commentaire</label>
+ <state readOnly="true" pattern="%s"/>
+ </channel-type>
+
+ <channel-type id="alert-icon">
+ <item-type>Image</item-type>
+ <label>Pictogramme</label>
+ <description>Pictogramme associé au niveau d'alerte.</description>
+ <state readOnly="true"/>
+ </channel-type>
</thing:thing-descriptions>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 78.271 84.645"><g id="indice-vert"><path d="M47.594 24.86c0 .233-.197.944-.59 2.123a480.704 480.704 0 0 1-2.656 7.733c-.432 1.22-.689 2.027-.77 2.421a89.557 89.557 0 0 1-1.414 4.134 133.507 133.507 0 0 0-1.533 4.367 82.549 82.549 0 0 1-1.42 4.311 678.77 678.77 0 0 0-1.533 4.307c-.316.869-.65 1.828-1.004 2.891a54.563 54.563 0 0 1-1.004 2.775 481.208 481.208 0 0 1-2.125 5.963 97.792 97.792 0 0 0-2.006 6.08c-.475 1.42-.943 2.5-1.418 3.246-.473.75-1.004 1.277-1.594 1.596-.592.313-1.281.49-2.064.531-.791.039-1.656.057-2.602.057-1.258 0-2.301-.039-3.125-.117-.828-.08-1.535-.371-2.127-.885-.59-.512-1.141-1.279-1.654-2.303-.51-1.021-1.043-2.479-1.596-4.369a710.439 710.439 0 0 0-7.375-21.898A1676.56 1676.56 0 0 1 .369 25.92a1.32 1.32 0 0 1-.119-.589v-.471c0-.787.193-1.379.592-1.773a2.828 2.828 0 0 1 1.416-.77 8.273 8.273 0 0 1 1.713-.175H7.69a4.96 4.96 0 0 1 2.008.415c.627.274 1.197.747 1.709 1.415.514.668.924 1.636 1.24 2.892.158.554.414 1.379.768 2.479.356 1.105.648 2.087.885 2.952a114.139 114.139 0 0 1 1.773 5.611 410.58 410.58 0 0 0 1.887 6.312c.315.871.631 1.873.947 3.014.31 1.141.666 2.264 1.06 3.365.078.234.217.725.412 1.477.197.746.473 1.514.826 2.301.356.785.75 1.494 1.182 2.121.432.633.967.947 1.596.947.551 0 1.059-.314 1.533-.947.473-.627.885-1.314 1.24-2.064.353-.744.629-1.455.826-2.123.195-.666.297-1.041.297-1.123.234-.863.51-1.828.824-2.891.313-1.063.592-2.025.826-2.893a71.07 71.07 0 0 0 .828-2.361c.313-.946.59-1.85.824-2.716.633-1.811 1.262-3.7 1.893-5.666a170.943 170.943 0 0 0 1.889-6.378c.391-1.493.783-2.654 1.178-3.483.395-.825.865-1.433 1.416-1.829.553-.393 1.223-.628 2.008-.706a29.256 29.256 0 0 1 2.832-.119h1.479c.59 0 1.16.077 1.711.235a3.584 3.584 0 0 1 1.418.768c.391.355.589.926.589 1.713z" fill="#28d761" stroke="#504d49" stroke-width=".5"/><path d="M78.271 77.531c0 3.926-9.189 7.113-20.523 7.113-11.336 0-20.523-3.188-20.523-7.113 0-3.928 9.187-7.109 20.523-7.109 11.334 0 20.523 3.182 20.523 7.109z" fill="#7c9fbb"/><ellipse cx="62.657" cy="78.139" rx="9.595" ry="2.639" fill="#63839b"/><path d="M62.656 16.214c0 .048-.053.154-.15.313a.903.903 0 0 1-.459.354c-.545.246-1.17.382-1.885.407-.715.025-1.445.036-2.189.036-.547 0-1.121-.007-1.732-.018a8.751 8.751 0 0 1-1.732-.203 1.903 1.903 0 0 1-.865-.425c-.238-.212-.355-.366-.355-.464V1.072c0-.05.049-.148.152-.293.102-.15.254-.276.457-.372.543-.248 1.189-.375 1.934-.388C56.578.004 57.293 0 57.973 0c.473 0 1.033.004 1.68.019.645.013 1.273.079 1.883.203.342.072.613.208.816.407.207.198.305.345.305.443v15.142zM62.656 36.682c0 .051-.053.157-.15.316a.89.89 0 0 1-.459.35c-.545.249-1.17.385-1.885.41-.715.022-1.445.035-2.189.035-.547 0-1.121-.005-1.732-.017a8.679 8.679 0 0 1-1.732-.202 1.869 1.869 0 0 1-.865-.428c-.238-.21-.355-.364-.355-.464v-15.14c0-.049.049-.147.152-.295a1.16 1.16 0 0 1 .457-.371c.543-.246 1.189-.375 1.934-.389a102.378 102.378 0 0 1 3.823 0c.645.014 1.273.08 1.883.205.342.073.613.209.816.407.207.195.305.346.305.443v15.14zM62.656 57.152c0 .047-.053.154-.15.314a.905.905 0 0 1-.459.352c-.545.246-1.17.383-1.885.408a74.944 74.944 0 0 1-3.921.017 8.603 8.603 0 0 1-1.732-.205 1.89 1.89 0 0 1-.865-.422c-.238-.213-.355-.367-.355-.465V42.013c0-.05.049-.15.152-.296.102-.15.254-.273.457-.373.543-.247 1.189-.376 1.934-.39.748-.012 1.463-.018 2.143-.018.473 0 1.033.006 1.68.018.645.014 1.273.082 1.883.205.342.072.613.208.816.407.207.198.305.345.305.446v15.14z" fill="#504d49"/><path d="M62.656 77.389c0 .051-.053.156-.15.314a.897.897 0 0 1-.459.357c-.545.244-1.17.379-1.885.404a67.95 67.95 0 0 1-3.921.019 8.751 8.751 0 0 1-1.732-.203 1.878 1.878 0 0 1-.865-.428c-.238-.211-.355-.363-.355-.465V62.252c0-.049.049-.148.152-.297.102-.15.254-.273.457-.371.543-.246 1.189-.375 1.934-.389.748-.012 1.463-.018 2.143-.018.473 0 1.033.006 1.68.018.645.014 1.273.08 1.883.205.342.072.613.209.816.404.207.199.305.348.305.447v15.138z" fill="#28d761" stroke="#504d49" stroke-width=".5"/></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 78.271 84.645"><g id="indice-jaune"><path d="M47.594 24.86c0 .233-.197.944-.59 2.123a480.704 480.704 0 0 1-2.656 7.733c-.432 1.22-.689 2.027-.77 2.421a89.557 89.557 0 0 1-1.414 4.134 133.507 133.507 0 0 0-1.533 4.367 81.616 81.616 0 0 1-1.42 4.311 831.628 831.628 0 0 0-1.533 4.307c-.316.869-.649 1.828-1.004 2.891s-.692 1.992-1.004 2.775a481.208 481.208 0 0 1-2.125 5.963 98.462 98.462 0 0 0-2.006 6.08c-.475 1.42-.943 2.5-1.418 3.246-.473.75-1.004 1.277-1.594 1.596-.592.313-1.281.49-2.064.531-.791.039-1.656.057-2.602.057-1.258 0-2.301-.039-3.125-.117-.828-.08-1.535-.371-2.127-.885-.59-.512-1.141-1.279-1.653-2.303-.511-1.021-1.044-2.479-1.596-4.369a707.767 707.767 0 0 0-7.376-21.898A1691.504 1691.504 0 0 1 .369 25.92a1.32 1.32 0 0 1-.119-.589v-.471c0-.787.194-1.379.592-1.773a2.828 2.828 0 0 1 1.416-.77 8.273 8.273 0 0 1 1.713-.175H7.69a4.96 4.96 0 0 1 2.008.415c.627.274 1.197.747 1.71 1.415.513.668.923 1.636 1.239 2.892.158.554.414 1.379.769 2.479a72.92 72.92 0 0 1 .884 2.952 114.043 114.043 0 0 1 1.774 5.611c.63 2.164 1.258 4.271 1.888 6.312.313.871.63 1.873.946 3.014a45.059 45.059 0 0 0 1.062 3.365c.077.234.216.725.412 1.477.196.746.472 1.514.826 2.301.354.785.75 1.494 1.182 2.121.431.633.966.947 1.596.947.551 0 1.059-.314 1.533-.947.472-.627.884-1.314 1.239-2.064.354-.744.629-1.455.826-2.123.196-.666.297-1.041.297-1.123.234-.863.51-1.828.824-2.891.313-1.063.592-2.025.826-2.893.237-.629.513-1.416.829-2.361.311-.946.589-1.85.824-2.716a242.12 242.12 0 0 0 1.893-5.666 172.94 172.94 0 0 0 1.888-6.378c.391-1.493.783-2.654 1.179-3.483.394-.825.865-1.433 1.416-1.829.552-.393 1.222-.628 2.008-.706a29.22 29.22 0 0 1 2.831-.119h1.479c.59 0 1.16.077 1.711.235a3.577 3.577 0 0 1 1.418.768c.389.355.587.926.587 1.713z" fill="#ff0" stroke="#504d49" stroke-width=".5"/><path d="M78.271 77.531c0 3.926-9.189 7.113-20.523 7.113-11.336 0-20.523-3.188-20.523-7.113 0-3.928 9.188-7.109 20.523-7.109 11.334 0 20.523 3.182 20.523 7.109z" fill="#7c9fbb"/><ellipse cx="62.657" cy="78.139" rx="9.595" ry="2.639" fill="#63839b"/><path d="M62.656 16.214c0 .048-.053.154-.15.313a.903.903 0 0 1-.459.354c-.545.246-1.17.382-1.885.407-.715.025-1.445.036-2.19.036-.547 0-1.121-.007-1.732-.018a8.751 8.751 0 0 1-1.732-.203 1.903 1.903 0 0 1-.865-.425c-.238-.212-.355-.366-.355-.464V1.072c0-.05.049-.148.152-.293.102-.15.254-.276.457-.372.543-.248 1.189-.375 1.934-.388C56.578.004 57.293 0 57.973 0c.473 0 1.033.004 1.68.019.645.013 1.273.079 1.883.203.342.072.613.208.816.407.207.198.305.345.305.443v15.142zM62.656 36.682c0 .051-.053.157-.15.316a.89.89 0 0 1-.459.35c-.545.249-1.17.385-1.885.41-.715.022-1.445.035-2.19.035-.547 0-1.121-.005-1.732-.017a8.679 8.679 0 0 1-1.732-.202 1.869 1.869 0 0 1-.865-.428c-.238-.21-.355-.364-.355-.464v-15.14c0-.049.049-.147.152-.295a1.16 1.16 0 0 1 .457-.371c.543-.246 1.189-.375 1.934-.389a102.378 102.378 0 0 1 3.822 0c.645.014 1.273.08 1.883.205.342.073.613.209.816.407.207.195.305.346.305.443v15.14z" fill="#504d49"/><path d="M62.656 57.152c0 .047-.053.154-.15.314a.905.905 0 0 1-.459.352c-.545.246-1.17.383-1.885.408a74.944 74.944 0 0 1-3.922.017 8.603 8.603 0 0 1-1.732-.205 1.89 1.89 0 0 1-.865-.422c-.238-.213-.355-.367-.355-.465V42.013c0-.05.049-.15.152-.296.102-.15.254-.273.457-.373.543-.247 1.189-.376 1.934-.39.748-.012 1.463-.018 2.142-.018.473 0 1.033.006 1.68.018.645.014 1.273.082 1.883.205.342.072.613.208.816.407.207.198.305.345.305.446v15.14z" fill="#ff0" stroke="#504d49" stroke-width=".5"/><path d="M62.656 77.389c0 .051-.053.156-.15.314a.897.897 0 0 1-.459.357c-.545.244-1.17.379-1.885.404a67.96 67.96 0 0 1-3.922.019 8.751 8.751 0 0 1-1.732-.203 1.878 1.878 0 0 1-.865-.428c-.238-.211-.355-.363-.355-.465V62.252c0-.049.049-.148.152-.297.102-.15.254-.273.457-.371.543-.246 1.189-.375 1.934-.389.748-.012 1.463-.018 2.142-.018.473 0 1.033.006 1.68.018.645.014 1.273.08 1.883.205.342.072.613.209.816.404.207.199.305.348.305.447v15.138z" fill="#504d49"/></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 78.272 84.645"><g id="indice-orange"><path d="M47.594 24.86c0 .233-.197.944-.59 2.123a480.704 480.704 0 0 1-2.656 7.733c-.432 1.22-.69 2.027-.77 2.421a88.378 88.378 0 0 1-1.414 4.134 133.507 133.507 0 0 0-1.533 4.367 82.222 82.222 0 0 1-1.42 4.31 747.465 747.465 0 0 0-1.533 4.307c-.317.869-.65 1.828-1.004 2.891a51.805 51.805 0 0 1-1.004 2.775 465.383 465.383 0 0 1-2.125 5.963 97.53 97.53 0 0 0-2.005 6.08c-.475 1.42-.944 2.5-1.418 3.246-.472.75-1.003 1.277-1.593 1.596-.592.313-1.281.49-2.065.531-.791.039-1.656.057-2.602.057-1.258 0-2.3-.039-3.124-.117-.829-.08-1.536-.371-2.127-.885-.589-.512-1.141-1.279-1.653-2.303-.51-1.021-1.045-2.479-1.596-4.369a706.87 706.87 0 0 0-7.376-21.898A1642.262 1642.262 0 0 1 .37 25.92a1.306 1.306 0 0 1-.12-.589v-.471c0-.787.194-1.379.592-1.773a2.828 2.828 0 0 1 1.416-.77 8.266 8.266 0 0 1 1.713-.175h3.718c.707 0 1.377.139 2.008.414.627.274 1.198.747 1.71 1.415.513.668.923 1.636 1.239 2.892.158.554.415 1.379.769 2.479.354 1.104.647 2.087.884 2.952a113.068 113.068 0 0 1 1.773 5.611 413.78 413.78 0 0 0 1.888 6.312c.314.871.63 1.873.946 3.014a45.059 45.059 0 0 0 1.062 3.365c.077.234.216.725.412 1.476.196.746.472 1.514.826 2.301.355.785.75 1.494 1.182 2.121.431.633.965.947 1.596.947.551 0 1.059-.314 1.533-.947.472-.627.884-1.314 1.239-2.064.354-.744.63-1.455.826-2.123.197-.666.297-1.041.297-1.123.235-.863.51-1.828.824-2.891.314-1.063.592-2.025.827-2.893.237-.629.513-1.416.829-2.361.312-.946.589-1.85.824-2.716.632-1.811 1.26-3.7 1.893-5.666a172.94 172.94 0 0 0 1.888-6.378c.39-1.493.783-2.654 1.178-3.483.393-.825.865-1.433 1.416-1.829.551-.393 1.222-.628 2.008-.707a29.256 29.256 0 0 1 2.832-.119h1.479c.59 0 1.16.077 1.711.235a3.574 3.574 0 0 1 1.418.769c.391.357.588.928.588 1.715z" fill="#fa0" stroke="#504d49" stroke-width=".5"/><path d="M78.272 77.531c0 3.926-9.189 7.113-20.523 7.113-11.336 0-20.524-3.188-20.524-7.113 0-3.928 9.188-7.109 20.524-7.109 11.333 0 20.523 3.182 20.523 7.109z" fill="#7c9fbb"/><ellipse cx="62.658" cy="78.139" rx="9.595" ry="2.639" fill="#63839b"/><path d="M62.657 16.214c0 .048-.053.154-.15.313a.903.903 0 0 1-.459.354c-.545.246-1.17.382-1.885.407-.715.025-1.445.036-2.189.036a96.9 96.9 0 0 1-1.733-.018 8.76 8.76 0 0 1-1.732-.203 1.903 1.903 0 0 1-.865-.425c-.238-.212-.355-.366-.355-.464V1.072c0-.05.049-.148.152-.293.102-.15.254-.276.457-.372.543-.248 1.189-.375 1.934-.388C56.579.004 57.293 0 57.973 0c.473 0 1.033.004 1.68.019.645.013 1.274.079 1.883.203.342.072.613.208.816.407.207.198.305.345.305.443v15.142z" fill="#504d49"/><path d="M62.657 36.682c0 .051-.053.157-.15.316a.89.89 0 0 1-.459.35c-.545.249-1.17.385-1.885.41-.715.023-1.445.035-2.189.035-.547 0-1.121-.005-1.733-.017a8.687 8.687 0 0 1-1.732-.202 1.869 1.869 0 0 1-.865-.428c-.238-.21-.355-.364-.355-.464v-15.14c0-.049.049-.147.152-.295a1.15 1.15 0 0 1 .457-.371c.543-.246 1.189-.375 1.934-.389a102.378 102.378 0 0 1 3.823 0c.645.014 1.274.08 1.883.205.342.073.613.209.816.407.207.195.305.346.305.443v15.14z" fill="#fa0" stroke="#504d49" stroke-width=".5"/><path d="M62.657 57.152c0 .047-.053.154-.15.314a.905.905 0 0 1-.459.352c-.545.246-1.17.383-1.885.408a75.085 75.085 0 0 1-3.922.017 8.611 8.611 0 0 1-1.732-.205 1.89 1.89 0 0 1-.865-.422c-.238-.213-.355-.367-.355-.465V42.013c0-.05.049-.15.152-.296.102-.15.254-.273.457-.373.543-.247 1.189-.376 1.934-.39.748-.012 1.463-.018 2.143-.018.473 0 1.033.006 1.68.018.645.014 1.274.082 1.883.205.342.072.613.208.816.407.207.198.305.345.305.446v15.14zM62.657 77.389c0 .051-.053.156-.15.314a.897.897 0 0 1-.459.357c-.545.244-1.17.379-1.885.404a65.179 65.179 0 0 1-3.922.019 8.76 8.76 0 0 1-1.732-.203 1.878 1.878 0 0 1-.865-.428c-.238-.211-.355-.363-.355-.465V62.252c0-.049.049-.148.152-.297.102-.15.254-.273.457-.371.543-.246 1.189-.375 1.934-.389.748-.012 1.463-.018 2.143-.018.473 0 1.033.006 1.68.018.645.014 1.274.08 1.883.205.342.072.613.209.816.404.207.199.305.348.305.447v15.138z" fill="#504d49"/></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 78.272 84.895"><g id="indice-rouge"><path d="M47.594 25.11c0 .233-.196.944-.589 2.123a576.842 576.842 0 0 1-1.299 3.838 365.141 365.141 0 0 1-1.358 3.895c-.432 1.22-.689 2.027-.77 2.421a90.678 90.678 0 0 1-1.413 4.134 131.995 131.995 0 0 0-1.533 4.367 82.954 82.954 0 0 1-1.421 4.312 747.465 747.465 0 0 0-1.533 4.307c-.316.869-.649 1.828-1.004 2.891a52.449 52.449 0 0 1-1.004 2.775 476.542 476.542 0 0 1-2.125 5.963 97.53 97.53 0 0 0-2.005 6.08c-.475 1.419-.944 2.499-1.418 3.246-.472.75-1.003 1.277-1.593 1.596-.592.313-1.281.489-2.065.53-.79.04-1.656.058-2.602.058-1.258 0-2.3-.039-3.124-.117-.829-.08-1.536-.372-2.127-.885-.589-.513-1.141-1.279-1.653-2.303-.51-1.022-1.044-2.479-1.596-4.369a706.134 706.134 0 0 0-7.377-21.9A1671.14 1671.14 0 0 1 .371 26.171a1.303 1.303 0 0 1-.12-.589v-.471c0-.787.194-1.379.592-1.773a2.828 2.828 0 0 1 1.416-.77 8.266 8.266 0 0 1 1.713-.175H7.69a4.96 4.96 0 0 1 2.008.415c.627.274 1.198.747 1.71 1.415.513.668.923 1.636 1.239 2.892.158.554.415 1.379.769 2.479.354 1.104.647 2.087.884 2.952a113.068 113.068 0 0 1 1.773 5.611 413.96 413.96 0 0 0 1.888 6.313c.314.87.63 1.873.946 3.014a44.882 44.882 0 0 0 1.061 3.364c.077.235.216.726.412 1.478.196.746.472 1.513.826 2.3.355.786.75 1.495 1.182 2.122.431.633.965.947 1.596.947.551 0 1.059-.314 1.533-.947.472-.627.884-1.314 1.239-2.064.354-.745.63-1.455.826-2.124.197-.666.297-1.041.297-1.122.235-.864.51-1.828.824-2.891.314-1.062.592-2.025.827-2.893.237-.63.513-1.416.829-2.362.312-.946.589-1.85.824-2.716.632-1.811 1.26-3.7 1.893-5.666a172.94 172.94 0 0 0 1.888-6.378c.39-1.493.783-2.654 1.178-3.483.393-.825.865-1.433 1.416-1.829.551-.393 1.222-.628 2.008-.707a29.242 29.242 0 0 1 2.832-.119h1.479c.589 0 1.159.077 1.71.235a3.59 3.59 0 0 1 1.419.769c.391.354.588.925.588 1.712z" fill="red" stroke="#504d49" stroke-width=".5"/><path d="M78.272 77.782c0 3.926-9.19 7.113-20.524 7.113-11.335 0-20.523-3.188-20.523-7.113 0-3.928 9.188-7.109 20.523-7.109 11.334-.001 20.524 3.181 20.524 7.109z" fill="#7c9fbb"/><ellipse cx="62.658" cy="78.389" rx="9.594" ry="2.639" fill="#63839b"/><path d="M62.656 16.464c0 .048-.053.154-.15.313a.896.896 0 0 1-.458.354c-.546.246-1.171.382-1.886.407-.714.025-1.444.036-2.19.036-.547 0-1.121-.007-1.732-.018a8.76 8.76 0 0 1-1.732-.203 1.9 1.9 0 0 1-.864-.425c-.239-.212-.356-.366-.356-.464V1.322c0-.05.05-.148.152-.293.102-.15.254-.276.458-.372.542-.248 1.188-.375 1.933-.388.748-.015 1.463-.019 2.143-.019.473 0 1.033.004 1.68.019.644.013 1.274.079 1.883.203.343.072.613.208.816.407.207.198.305.345.305.443v15.142z" fill="red" stroke="#504d49" stroke-width=".5"/><path d="M62.656 36.932c0 .051-.053.157-.15.316a.884.884 0 0 1-.458.35c-.546.249-1.171.385-1.886.41a71.04 71.04 0 0 1-2.19.035c-.547 0-1.121-.005-1.732-.017a8.687 8.687 0 0 1-1.732-.202 1.865 1.865 0 0 1-.864-.428c-.239-.21-.356-.364-.356-.464v-15.14c0-.049.05-.147.152-.295s.254-.271.458-.371c.542-.246 1.188-.375 1.933-.389a102.378 102.378 0 0 1 3.823 0c.644.014 1.274.08 1.883.205.343.073.613.209.816.407.207.195.305.346.305.443v15.14zM62.656 57.403c0 .047-.053.154-.15.313a.894.894 0 0 1-.458.353c-.546.246-1.171.383-1.886.408a71.04 71.04 0 0 1-3.922.017 8.611 8.611 0 0 1-1.732-.205 1.886 1.886 0 0 1-.864-.422c-.239-.213-.356-.367-.356-.465v-15.14c0-.05.05-.15.152-.296.102-.15.254-.273.458-.373.542-.247 1.188-.376 1.933-.39.748-.012 1.463-.018 2.143-.018.473 0 1.033.006 1.68.018.644.014 1.274.082 1.883.205.343.072.613.208.816.407.207.198.305.345.305.446v15.142zM62.656 77.639c0 .051-.053.156-.15.314a.9.9 0 0 1-.458.357c-.546.244-1.171.378-1.886.404a67.857 67.857 0 0 1-3.922.019 8.76 8.76 0 0 1-1.732-.203 1.884 1.884 0 0 1-.864-.428c-.239-.211-.356-.363-.356-.465V62.502c0-.05.05-.148.152-.297.102-.15.254-.273.458-.371.542-.246 1.188-.375 1.933-.389a93.232 93.232 0 0 1 3.823 0c.644.014 1.274.08 1.883.204.343.072.613.209.816.405.207.199.305.348.305.447v15.138z" fill="#504d49"/></g></svg>
\ No newline at end of file
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIII6DCCBtCgAwIBAgIRAKyivjc1wLX1ahBKQr2wLpwwDQYJKoZIhvcNAQELBQAw
+fTELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURISU1ZT1RJUzEcMBoGA1UECwwTMDAw
+MiA0ODE0NjMwODEwMDAzNjEdMBsGA1UEYQwUTlRSRlItNDgxNDYzMDgxMDAwMzYx
+HTAbBgNVBAMMFENlcnRpZ25hIFNlcnZpY2VzIENBMB4XDTIwMDkxMDA4NDM0NloX
+DTIxMDkxMDA4NDM0NlowgZgxCzAJBgNVBAYTAkZSMRkwFwYDVQQHDBBQQVJJUyBM
+QSBERUZFTlNFMSAwHgYDVQQKDBdTRyBNSU4gVFJBTlMgRUNPTCBTT0xJRDEcMBoG
+A1UECwwTMDAwMiAxMzAwMTk1NDAwMDAyNTEaMBgGA1UEAwwRdmlnaWNydWVzLmdv
+dXYuZnIxEjAQBgNVBAUTCVMxNzQ1ODAxNDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOxiWSS7UVERcuH6PI4Itkm3hNos8wiwmbI1mtHht4a94h9gxzQ1
+U0t/wxYkOgzdQ3VCSNgX27YOEsfJKlqREx3AaSdztPW27mXCgl3+IOF6bm0UooIn
+gcl0inpGYJCI4Mb+2xEpmeJt2eEojQgiYQZVJuf2yOoNpCdNJlS/ym3LM1AQVxBm
+/Jl24KWbhr7cjPye0773jqckwf3JSFbqVtxinctVnnS+qdwn/hQP3nNBkXXKOnJM
+71vD7S4MwIMy3oQHK8oYmTwQpliju8rOIVKu099hiQAcegh5b33gG2RagHaTd6vb
+sunW5GBWS4IfhQurQT5pjKnVT4zpF/h4b+kCAwEAAaOCBEUwggRBMAkGA1UdEwQC
+MAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMGUGA1UdHwRe
+MFwwK6ApoCeGJWh0dHA6Ly9jcmwuY2VydGlnbmEuZnIvc2VydmljZXNjYS5jcmww
+LaAroCmGJ2h0dHA6Ly9jcmwuZGhpbXlvdGlzLmNvbS9zZXJ2aWNlc2NhLmNybDCB
+5AYIKwYBBQUHAQEEgdcwgdQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9hdXRvcml0ZS5j
+ZXJ0aWduYS5mci9zZXJ2aWNlc2NhLmRlcjA4BggrBgEFBQcwAoYsaHR0cDovL2F1
+dG9yaXRlLmRoaW15b3Rpcy5jb20vc2VydmljZXNjYS5kZXIwLgYIKwYBBQUHMAGG
+Imh0dHA6Ly9zZXJ2aWNlc2NhLm9jc3AuY2VydGlnbmEuZnIwMAYIKwYBBQUHMAGG
+JGh0dHA6Ly9zZXJ2aWNlc2NhLm9jc3AuZGhpbXlvdGlzLmNvbTAdBgNVHQ4EFgQU
+nWuFMLSW73ZLFxHfrbThfMgS5n0wHwYDVR0jBBgwFoAUrOyGj0s3HLh/FxsZ0K7o
+TuM0XBIwMwYDVR0RBCwwKoIRdmlnaWNydWVzLmdvdXYuZnKCFXd3dy52aWdpY3J1
+ZXMuZ291di5mcjBUBgNVHSAETTBLMAgGBmeBDAECAjA/BgsqgXoBgTECBQEBATAw
+MC4GCCsGAQUFBwIBFiJodHRwczovL3d3dy5jZXJ0aWduYS5mci9hdXRvcml0ZXMv
+MIIB9AYKKwYBBAHWeQIEAgSCAeQEggHgAd4AdQBElGUusO7Or8RAB9io/ijA2uaC
+vtjLMbU/0zOWtbaBqAAAAXR3L2RPAAAEAwBGMEQCIDtx/No4sejdX5EvrDXcs4MD
+nqYZQymUgF6BEsDI8QBtAiAYOV8IqtH4+C2xMlaWWQSsu0+porprbou01OF7OaPM
+QQB2AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABdHcvZRMAAAQD
+AEcwRQIgSxFpdehqbsWZavfyc9/qfRp3mZDwxgKWtYWvKWD3jCYCIQDzBkPoaS0R
+5NwUKNy5M74Mki6NOJOX3oKpw/lRjKd5ywB1AFzcQ5L+5qtFRLFemtRW5hA3+9X6
+R9yhc5SyXub2xw7KAAABdHcvZ4MAAAQDAEYwRAIgfKfHw+kC5FHov2fo3Igs0Umu
+FSmG65zKYv6C5idM+NoCIEddMJWt2e+K6ejFxILKGSzx2pyj5f8J0N0jBwjquQt0
+AHYA9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOMAAAF0dy9oWgAABAMA
+RzBFAiEAimauUItb/FhM/KFNrF6pKpq3msq69vf8szYg6HDKq4cCIApAx7DUecse
+TeUBpfeJa6618/Yx4HWlbEDjXyToS0LEMA0GCSqGSIb3DQEBCwUAA4ICAQAXkoT/
+QGyvyNx9MlSuVE+CqRYJd2h0UsQjU1tjNWIwT/grodh3M5eYMbPv+mG1Kg8a95lG
+PazIJCxhXde77NQF5yXDEY6D7SFqs5s+Ah/oCxmkRNY+ctIrV++lyl24cVrLuMgc
+QHxtIDveFEHH7KsqrhcHkzrTG2tWcxD3j9p+mZu1t4xuOT46WfONMOBv9I0O+jVV
+2LpR5Qblf6Rvr9a0OopYCeTYT2q91A2XR7hUDxFXBVt+AB3eIOqbRUj0J+1xMjhJ
+XhqiaJpGQDf0CvSVE+ej5nafV/+eCS11u9U20B9vBl8JTKGq7lr8jhL3IPOb/EQG
+whh6/aYGT6bRh0L68FXRCclVDjFI7sTP0ZKcjGi1oCdCHP7d00xWg8z5fY4lEdkR
+6TfpkqBoYF5pnsikoDxX/U52w2+KvU84aOhSf+LkGigDUu+4+FJw4q4hdfevgdVW
+gM7vS4LzLTwnaXh+qGipuiWUUz6ublhM6iLn8ff2T82I6ggTHSvRswZfZciRrp4r
+m63QWF1dPQdvRBIeuAp772gKnUATGNKDF5XhE8NFvvIiLDFjHsiTGSx8UUzrPRdd
+0LXhFz8Dx4ciG4Y6sKnJbp4Lsu/is12n6eAzhId8YZSE0FGLSShKB27cWJ+VyIad
+qkouOhMnN18+YFQsMZM6qISdOTz+jQlU+DOMBg==
+-----END CERTIFICATE-----