[meteofrance] New binding (#15462)

* Adapting binding to new data feed

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2024-09-27 13:04:42 +02:00 committed by GitHub
parent 8332ab29c5
commit 953e082fcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 3984 additions and 1327 deletions

View File

@ -211,8 +211,8 @@
/bundles/org.openhab.binding.mecmeter/ @kaikreuzer
/bundles/org.openhab.binding.melcloud/ @lucacalcaterra @paulianttila @thewiep
/bundles/org.openhab.binding.mercedesme/ @weymann
/bundles/org.openhab.binding.meteoalerte/ @clinique
/bundles/org.openhab.binding.meteoblue/ @9037568
/bundles/org.openhab.binding.meteofrance/ @clinique
/bundles/org.openhab.binding.meteostick/ @cdjackson
/bundles/org.openhab.binding.mffan/ @mark-brooks-180
/bundles/org.openhab.binding.miele/ @kgoderis @jlaur

View File

@ -1053,12 +1053,12 @@
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.meteoalerte</artifactId>
<artifactId>org.openhab.binding.meteoblue</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.meteoblue</artifactId>
<artifactId>org.openhab.binding.meteofrance</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -1,113 +0,0 @@
# Meteo Alerte Binding
The Meteo Alerte binding gives alert level regarding major weather related risk factors.
This binding provides its own icon set and provides appropriate static and dynamic SVG icons (see items examples below).
## Supported Things
There is exactly one supported thing type, which represents the weather alerts for a given department.
It has the `department` id.
Of course, you can add multiple things, e.g. for getting alerts for different locations.
## Binding Configuration
The binding has no configuration options, all configuration is done at Thing level.
## Thing Configuration
The thing has a few configuration parameters:
| Parameter | Description |
|---------------|----------------------------------------------------------------------------------------|
| `department` | Name of the department. |
| refresh | Refresh interval in minutes. Optional, the default value is 1440 minutes (once a day). |
## Channels
The Météo Alerte information that are retrieved is available as these channels:
| Channel ID | Item Type | Description |
|-----------------------|-----------|-----------------------------------------------|
| observation-time | DateTime | Date and time of report validity start |
| end-time | DateTime | Date and time of report validity end |
| comment | String | General comments on alerts for the department |
| vent | Number | Wind alert level (*) |
| pluie-inondation | Number | Rain alert level (*) |
| orage | Number | Storm alert level (*) |
| inondation | Number | Flood alert level (*) |
| neige | Number | Snow alert level (*) |
| canicule | Number | Heat alert level (*) |
| grand-froid | Number | Cold alert level (*) |
| avalanches | Number | Avalanche alert level (*) |
| vague-submersion | Number | Wave submersion alert level (*) |
| pluie-inondation-icon | Image | Pictogram of the Rain alert level |
| vent-icon | Image | Pictogram of the Wind alert level |
| orage-icon | Image | Pictogram of Storm alert level |
| inondation-icon | Image | Pictogram of Flood alert level |
| neige-icon | Image | Pictogram of Snow alert level |
| canicule-icon | Image | Pictogram of Heat alert level |
| grand-froid-icon | Image | Pictogram of Cold alert level |
| avalanches-icon | Image | Pictogram of Avalanche alert level |
| vague-submersion-icon | Image | Pictogram of Wave Submersion alert level |
(*) Each alert level is described by a color :
| Code | Color | Description |
|------|--------|-------------------------------------------|
| 0 | Green | No particular vigilance |
| 1 | Yellow | Be attentive to the weather situation |
| 2 | Orange | Be "very vigilant" in the concerned areas |
| 3 | Red | Absolute vigilance required |
## Provided icon set
This binding has its own IconProvider and makes available the following list of icons
| Icon Name | Dynamic | Illustration |
|---------------------------------|---------|--------------|
| oh:meteoalerte:vent | Yes | ![](src/main/resources/icon/vent.svg) |
| oh:meteoalerte:pluie-inondation | Yes | ![](src/main/resources/icon/pluie-inondation.svg) |
| oh:meteoalerte:orage | Yes | ![](src/main/resources/icon/orage.svg) |
| oh:meteoalerte:inondation | Yes | ![](src/main/resources/icon/inondation.svg) |
| oh:meteoalerte:neige | Yes | ![](src/main/resources/icon/neige.svg) |
| oh:meteoalerte:canicule | Yes | ![](src/main/resources/icon/canicule.svg) |
| oh:meteoalerte:grand-froid | Yes | ![](src/main/resources/icon/grand-froid.svg) |
| oh:meteoalerte:avalanches | Yes | ![](src/main/resources/icon/avalanches.svg) |
| oh:meteoalerte:vague-submersion | Yes | ![](src/main/resources/icon/vague-submersion.svg) |
| oh:meteoalerte:meteo_france | No | ![](src/main/resources/icon/meteo_france.svg) |
## Full Example
meteoalert.things:
```java
Thing meteoalerte:department:yvelines @ "MyCity" [department="YVELINES", refresh=12]
```
meteoalert.items:
```java
Group gMeteoAlert "Alertes Météo" <oh:meteoalerte:meteo_france>
String MA_Dept78 "Département 78 [%s]" <text> (gMeteoAlert) {channel="meteoalerte:department:yvelines:comment"}
Number MA_etat_canicule "Canicule [%s]" <oh:meteoalerte:canicule> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule"}
Number MA_etat_grand_froid "Grand Froid [%s]" <oh:meteoalerte:grand-froid> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid"}
Number MA_etat_pluie_inondation "Pluie-Inondation [%s]" <oh:meteoalerte:pluie-inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation"}
Number MA_etat_neige "Neige [%s]" <oh:meteoalerte:neige> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige"}
Number MA_etat_vent "Vent [%s]" <oh:meteoalerte:vent> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent"}
Number MA_etat_inondation "Inondation [%s]" <oh:meteoalerte:inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation"}
Number MA_etat_orage "Orage [%s]" <oh:meteoalerte:orage> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage"}
Number MA_etat_avalanche "Avalanches [%s]" <oh:meteoalerte:avalanches> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches"}
Image MA_icon_canicule "Canicule" <oh:meteoalerte:canicule> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule-icon"}
Image MA_icon_grand_froid "Grand Froid" <oh:meteoalerte:grand-froid> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid-icon"}
Image MA_icon_pluie_inondation "Pluie-Inondation" <oh:meteoalerte:pluie-inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation-icon"}
Image MA_icon_neige "Neige" <oh:meteoalerte:neige> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige-icon"}
Image MA_icon_vent "Vent" <oh:meteoalerte:vent> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent-icon"}
Image MA_icon_inondation "Inondation" <oh:meteoalerte:inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation-icon"}
Image MA_icon_orage "Orage" <oh:meteoalerte:orage> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage-icon"}
Image MA_icon_avalanche "Avalanche" <oh:meteoalerte:avalanches> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches-icon"}
DateTime MA_ObservationTS "Timestamp [%1$tH:%1$tM]" <time> (gMeteoAlert) {channel="meteoalerte:department:yvelines:observation-time"}
```

View File

@ -1,44 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link MeteoAlerteBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoAlerteBindingConstants {
public static final String BINDING_ID = "meteoalerte";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_METEO_ALERT = new ThingTypeUID(BINDING_ID, "department");
// List of all Channel IDs
public static final String WAVE = "vague-submersion";
public static final String AVALANCHE = "avalanches";
public static final String HEAT = "canicule";
public static final String FREEZE = "grand-froid";
public static final String FLOOD = "inondation";
public static final String SNOW = "neige";
public static final String STORM = "orage";
public static final String RAIN = "pluie-inondation";
public static final String WIND = "vent";
public static final String OBSERVATION_TIME = "observation-time";
public static final String END_TIME = "end-time";
public static final String COMMENT = "comment";
}

View File

@ -1,69 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.internal;
import static org.openhab.binding.meteoalerte.internal.MeteoAlerteBindingConstants.THING_TYPE_METEO_ALERT;
import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteoalerte.internal.handler.MeteoAlerteHandler;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
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;
/**
* The {@link MeteoAlerteHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = ThingHandlerFactory.class)
@NonNullByDefault
public class MeteoAlerteHandlerFactory extends BaseThingHandlerFactory {
private final Gson gson;
private final MeteoAlertIconProvider iconProvider;
@Activate
public MeteoAlerteHandlerFactory(@Reference TimeZoneProvider timeZoneProvider,
@Reference MeteoAlertIconProvider iconProvider) {
this.iconProvider = iconProvider;
this.gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
.parse(json.getAsJsonPrimitive().getAsString())
.withZoneSameInstant(timeZoneProvider.getTimeZone()))
.create();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return THING_TYPE_METEO_ALERT.equals(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
return supportsThingType(thingTypeUID) ? new MeteoAlerteHandler(thing, gson, iconProvider) : null;
}
}

View File

@ -1,176 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.internal.handler;
import static org.openhab.binding.meteoalerte.internal.MeteoAlerteBindingConstants.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.meteoalerte.internal.MeteoAlertIconProvider;
import org.openhab.binding.meteoalerte.internal.MeteoAlerteConfiguration;
import org.openhab.binding.meteoalerte.internal.json.ApiResponse;
import org.openhab.binding.meteoalerte.internal.json.ResponseFieldDTO.AlertLevel;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* The {@link MeteoAlerteHandler} is responsible for updating channels
* and querying the API
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoAlerteHandler extends BaseThingHandler {
private static final int TIMEOUT_MS = 30000;
private static final String URL = """
https://public.opendatasoft.com/api/records/1.0/search/?dataset=risques-meteorologiques-copy&\
facet=etat_vent&facet=etat_pluie_inondation&facet=etat_orage&facet=etat_inondation&facet=etat_neige&facet=etat_canicule&\
facet=etat_grand_froid&facet=etat_avalanches&refine.nom_dept=%s\
""";
private final Logger logger = LoggerFactory.getLogger(MeteoAlerteHandler.class);
private final MeteoAlertIconProvider iconProvider;
private final Gson gson;
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
private String queryUrl = "";
public MeteoAlerteHandler(Thing thing, Gson gson, MeteoAlertIconProvider iconProvider) {
super(thing);
this.gson = gson;
this.iconProvider = iconProvider;
}
@Override
public void initialize() {
logger.debug("Initializing Météo Alerte handler.");
MeteoAlerteConfiguration config = getConfigAs(MeteoAlerteConfiguration.class);
logger.debug("config department = {}", config.department);
logger.debug("config refresh = {}", config.refresh);
updateStatus(ThingStatus.UNKNOWN);
queryUrl = URL.formatted(config.department);
refreshJob = Optional
.of(scheduler.scheduleWithFixedDelay(this::updateAndPublish, 0, config.refresh, TimeUnit.MINUTES));
}
@Override
public void dispose() {
logger.debug("Disposing the Météo Alerte handler.");
refreshJob.ifPresent(job -> job.cancel(true));
refreshJob = Optional.empty();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateAndPublish();
}
}
private void updateAndPublish() {
try {
if (queryUrl.isEmpty()) {
throw new MalformedURLException("queryUrl not initialized");
}
String response = HttpUtil.executeUrl("GET", queryUrl, TIMEOUT_MS);
if (response == null) {
throw new IOException("Empty response");
}
updateStatus(ThingStatus.ONLINE);
updateChannels(Objects.requireNonNull(gson.fromJson(response, ApiResponse.class)));
} catch (MalformedURLException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Querying '%s' error : %s".formatted(queryUrl, e.getMessage()));
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
/**
* Update the channel from the last Meteo Alerte data retrieved
*
* @param channelId the id identifying the channel to be updated
*/
private void updateChannels(ApiResponse apiResponse) {
apiResponse.getRecords().findFirst().ifPresent(record -> record.getResponseFieldDTO().ifPresent(fields -> {
updateAlert(WIND, fields.getVent());
updateAlert(RAIN, fields.getPluieInondation());
updateAlert(STORM, fields.getOrage());
updateAlert(FLOOD, fields.getInondation());
updateAlert(SNOW, fields.getNeige());
updateAlert(HEAT, fields.getCanicule());
updateAlert(FREEZE, fields.getGrandFroid());
updateAlert(AVALANCHE, fields.getAvalanches());
updateAlert(WAVE, fields.getVagueSubmersion());
updateState(COMMENT, StringType.valueOf(fields.getVigilanceComment()));
fields.getDateInsert().ifPresent(date -> updateDate(OBSERVATION_TIME, date));
fields.getDatePrevue().ifPresent(date -> updateDate(END_TIME, date));
}));
}
private void updateAlert(String channelId, AlertLevel value) {
State state = value != AlertLevel.UNKNOWN ? new DecimalType(value.ordinal()) : UnDefType.NULL;
if (isLinked(channelId)) {
updateState(channelId, state);
}
String channelIcon = channelId + "-icon";
if (isLinked(channelIcon)) {
InputStream icon = iconProvider.getIcon(channelId, state.toString());
if (icon != null) {
try {
State result = new RawType(icon.readAllBytes(), "image/svg+xml");
updateState(channelIcon, result);
} catch (IOException e) {
logger.warn("Error getting icon for channel {} and value {} : {}", channelId, value,
e.getMessage());
}
} else {
logger.warn("Null icon returned for channel {} and state {}", channelIcon, state);
}
}
}
private void updateDate(String channelId, ZonedDateTime zonedDateTime) {
if (isLinked(channelId)) {
updateState(channelId, new DateTimeType(zonedDateTime));
}
}
}

View File

@ -1,49 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.internal.json;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ApiResponse} is the Java class used to map the JSON
* response to the webservice request.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class ApiResponse {
@SerializedName("nhits")
private int nHits;
private @Nullable Parameters parameters;
private List<Record> records = new ArrayList<>();
public int getNHits() {
return nHits;
}
public Optional<Parameters> getParameters() {
return Optional.ofNullable(parameters);
}
public Stream<Record> getRecords() {
return records.stream();
}
}

View File

@ -1,56 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.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 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 = "";
@SerializedName("timezone")
private String timeZone = "";
private int rows;
private String format = "";
private @Nullable Refine refine;
public String getDataset() {
return dataset;
}
public String getTimezone() {
return timeZone;
}
public int getRows() {
return rows;
}
public String getFormat() {
return format;
}
public Optional<Refine> getRefine() {
return Optional.ofNullable(refine);
}
}

View File

@ -1,54 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.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 = "";
@SerializedName("fields")
private @Nullable ResponseFieldDTO responseFieldDTO;
public String getDatasetId() {
return datasetId;
}
public String getRecordId() {
return recordId;
}
public String getRecordTimestamp() {
return recordTimestamp;
}
public Optional<ResponseFieldDTO> getResponseFieldDTO() {
return Optional.ofNullable(responseFieldDTO);
}
}

View File

@ -1,161 +0,0 @@
/**
* Copyright (c) 2010-2024 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.meteoalerte.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 ResponseFieldDTO} is the Java class used to map the JSON
* response to the webservice request.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class ResponseFieldDTO {
public enum AlertLevel {
@SerializedName("Vert")
GREEN("00ff00"),
@SerializedName("Jaune")
YELLOW("ffff00"),
@SerializedName("Orange")
ORANGE("ef6c00"),
@SerializedName("Rouge")
RED("ff0000"),
UNKNOWN("3d3c3c");
public final String color;
AlertLevel(String color) {
this.color = color;
}
}
@SerializedName("nom_reg")
private String nomReg = "";
@SerializedName("typeprev")
private String typePrev = "";
@SerializedName("etat_canicule")
private AlertLevel canicule = AlertLevel.UNKNOWN;
@SerializedName("nom_dept")
private String nomDept = "";
@SerializedName("etat_grand_froid")
private AlertLevel grandFroid = AlertLevel.UNKNOWN;
@SerializedName("noversion")
private String noVersion = "";
@SerializedName("etat_pluie_inondation")
private AlertLevel pluieInondation = AlertLevel.UNKNOWN;
@SerializedName("etat_neige")
private AlertLevel neige = AlertLevel.UNKNOWN;
@SerializedName("etat_vent")
private AlertLevel vent = AlertLevel.UNKNOWN;
@SerializedName("etat_inondation")
private AlertLevel inondation = AlertLevel.UNKNOWN;
@SerializedName("etat_avalanches")
private AlertLevel avalanches = AlertLevel.UNKNOWN;
@SerializedName("etat_orage")
private AlertLevel orage = AlertLevel.UNKNOWN;
private int echeance;
@SerializedName("etat_vague_submersion")
private AlertLevel vagueSubmersion = AlertLevel.UNKNOWN;
private String dep = "";
@SerializedName("vigilancecommentaire_texte")
private String vigilanceComment = "";
@SerializedName("dateprevue")
private @Nullable ZonedDateTime datePrevue;
@SerializedName("dateinsert")
private @Nullable ZonedDateTime dateInsert;
@SerializedName("daterun")
private @Nullable ZonedDateTime dateRun;
public String getVigilanceComment() {
return vigilanceComment;
}
public String getNomReg() {
return nomReg;
}
public Optional<ZonedDateTime> getDatePrevue() {
return Optional.ofNullable(datePrevue);
}
public String getTypePrev() {
return typePrev;
}
public AlertLevel getCanicule() {
return canicule;
}
public String getNomDept() {
return nomDept;
}
public AlertLevel getGrandFroid() {
return grandFroid;
}
public String getNoVersion() {
return noVersion;
}
public AlertLevel getPluieInondation() {
return pluieInondation;
}
public AlertLevel getNeige() {
return neige;
}
public AlertLevel getVent() {
return vent;
}
public Optional<ZonedDateTime> getDateInsert() {
return Optional.ofNullable(dateInsert);
}
public AlertLevel getInondation() {
return inondation;
}
public AlertLevel getAvalanches() {
return avalanches;
}
public AlertLevel getOrage() {
return orage;
}
public int getEcheance() {
return echeance;
}
public AlertLevel getVagueSubmersion() {
return vagueSubmersion;
}
public String getDep() {
return dep;
}
public Optional<ZonedDateTime> getDateRun() {
return Optional.ofNullable(dateRun);
}
}

View File

@ -1,189 +0,0 @@
# add-on
addon.meteoalerte.name = Meteo Alerte Binding
addon.meteoalerte.description = Retrieves Meteo Alert levels in France
# thing types
thing-type.meteoalerte.department.label = Alertes Département
thing-type.meteoalerte.department.description = Fournit les niveaux d'alerte météo pour un département.
thing-type.meteoalerte.department.channel.avalanches-icon.label = Icône Avalanches
thing-type.meteoalerte.department.channel.canicule-icon.label = Icône Canicule
thing-type.meteoalerte.department.channel.end-time.label = Fin de validité
thing-type.meteoalerte.department.channel.grand-froid-icon.label = Icône Grand Froid
thing-type.meteoalerte.department.channel.inondation-icon.label = Icône Inondation
thing-type.meteoalerte.department.channel.neige-icon.label = Icône Neige
thing-type.meteoalerte.department.channel.observation-time.label = Début de validité
thing-type.meteoalerte.department.channel.orage-icon.label = Icône Orage
thing-type.meteoalerte.department.channel.pluie-inondation-icon.label = Icône Pluie Inondation
thing-type.meteoalerte.department.channel.vague-submersion-icon.label = Icône Vague Submersion
thing-type.meteoalerte.department.channel.vent-icon.label = Icône Vent
# thing types config
thing-type.config.meteoalerte.department.department.label = Département
thing-type.config.meteoalerte.department.department.description = Nom du département
thing-type.config.meteoalerte.department.department.option.AIN = Ain
thing-type.config.meteoalerte.department.department.option.AISNE = Aisne
thing-type.config.meteoalerte.department.department.option.ALLIER = Allier
thing-type.config.meteoalerte.department.department.option.ALPES-DE-HAUTE-PROVENCE = Alpes de Haute Provence
thing-type.config.meteoalerte.department.department.option.ALPES-MARITIMES = Alpes Maritimes
thing-type.config.meteoalerte.department.department.option.ARDECHE = Ardèche
thing-type.config.meteoalerte.department.department.option.ARDENNES = Ardennes
thing-type.config.meteoalerte.department.department.option.ARIEGE = Ariège
thing-type.config.meteoalerte.department.department.option.AUBE = Aube
thing-type.config.meteoalerte.department.department.option.AUDE = Aude
thing-type.config.meteoalerte.department.department.option.AVEYRON = Aveyron
thing-type.config.meteoalerte.department.department.option.BAS-RHIN = Bas-Rhin
thing-type.config.meteoalerte.department.department.option.BOUCHES-DU-RHONE = Bouches du Rhône
thing-type.config.meteoalerte.department.department.option.CALVADOS = Calvados
thing-type.config.meteoalerte.department.department.option.CANTAL = Cantal
thing-type.config.meteoalerte.department.department.option.CHARENTE = Charente
thing-type.config.meteoalerte.department.department.option.CHARENTE-MARITIME = Charente Maritime
thing-type.config.meteoalerte.department.department.option.CHER = Cher
thing-type.config.meteoalerte.department.department.option.CORREZE = Corrèze
thing-type.config.meteoalerte.department.department.option.CORSE-DU-SUD = Corse du Sud
thing-type.config.meteoalerte.department.department.option.COTE-D'OR = Côte D'Or
thing-type.config.meteoalerte.department.department.option.COTES-D'ARMOR = Côtes D'Armor
thing-type.config.meteoalerte.department.department.option.CREUSE = Creuse
thing-type.config.meteoalerte.department.department.option.DEUX-SEVRES = Deux Sèvres
thing-type.config.meteoalerte.department.department.option.DORDOGNE = Dordogne
thing-type.config.meteoalerte.department.department.option.DOUBS = Doubs
thing-type.config.meteoalerte.department.department.option.DROME = Drôme
thing-type.config.meteoalerte.department.department.option.ESSONNE = Essonne
thing-type.config.meteoalerte.department.department.option.EURE = Eure
thing-type.config.meteoalerte.department.department.option.EURE-ET-LOIR = Eure et Loir
thing-type.config.meteoalerte.department.department.option.FINISTERE = Finistère
thing-type.config.meteoalerte.department.department.option.GARD = Gard
thing-type.config.meteoalerte.department.department.option.GERS = Gers
thing-type.config.meteoalerte.department.department.option.GIRONDE = Gironde
thing-type.config.meteoalerte.department.department.option.HAUT-RHIN = Haut-Rhin
thing-type.config.meteoalerte.department.department.option.HAUTE-CORSE = Haute Corse
thing-type.config.meteoalerte.department.department.option.HAUTE-GARONNE = Haute Garonne
thing-type.config.meteoalerte.department.department.option.HAUTE-LOIRE = Haute Loire
thing-type.config.meteoalerte.department.department.option.HAUTE-MARNE = Haute Marne
thing-type.config.meteoalerte.department.department.option.HAUTE-SAONE = Haute Saône
thing-type.config.meteoalerte.department.department.option.HAUTE-SAVOIE = Haute Savoie
thing-type.config.meteoalerte.department.department.option.HAUTE-VIENNE = Haute Vienne
thing-type.config.meteoalerte.department.department.option.HAUTES-ALPES = Hautes Alpes
thing-type.config.meteoalerte.department.department.option.HAUTES-PYRENEES = Hautes Pyrénées
thing-type.config.meteoalerte.department.department.option.HAUTS-DE-SEINE = Hauts de Seine
thing-type.config.meteoalerte.department.department.option.HERAULT = Hérault
thing-type.config.meteoalerte.department.department.option.ILLE-ET-VILAINE = Ille et Vilaine
thing-type.config.meteoalerte.department.department.option.INDRE = Indre
thing-type.config.meteoalerte.department.department.option.INDRE-ET-LOIRE = Indre et Loire
thing-type.config.meteoalerte.department.department.option.ISERE = Isère
thing-type.config.meteoalerte.department.department.option.JURA = Jura
thing-type.config.meteoalerte.department.department.option.LANDES = Landes
thing-type.config.meteoalerte.department.department.option.LOIR-ET-CHER = Loir et Cher
thing-type.config.meteoalerte.department.department.option.LOIRE = Loire
thing-type.config.meteoalerte.department.department.option.LOIRE-ATLANTIQUE = Loire Atlantique
thing-type.config.meteoalerte.department.department.option.LOIRET = Loiret
thing-type.config.meteoalerte.department.department.option.LOT = Lot
thing-type.config.meteoalerte.department.department.option.LOT-ET-GARONNE = Lot et Garonne
thing-type.config.meteoalerte.department.department.option.LOZERE = Lozère
thing-type.config.meteoalerte.department.department.option.MAINE-ET-LOIRE = Maine et Loire
thing-type.config.meteoalerte.department.department.option.MANCHE = Manche
thing-type.config.meteoalerte.department.department.option.MARNE = Marne
thing-type.config.meteoalerte.department.department.option.MAYENNE = Mayenne
thing-type.config.meteoalerte.department.department.option.MEURTHE-ET-MOSELLE = Meurthe et Moselle
thing-type.config.meteoalerte.department.department.option.MEUSE = Meuse
thing-type.config.meteoalerte.department.department.option.MORBIHAN = Morbihan
thing-type.config.meteoalerte.department.department.option.MOSELLE = Moselle
thing-type.config.meteoalerte.department.department.option.NIEVRE = Nièvre
thing-type.config.meteoalerte.department.department.option.NORD = Nord
thing-type.config.meteoalerte.department.department.option.OISE = Oise
thing-type.config.meteoalerte.department.department.option.ORNE = Orne
thing-type.config.meteoalerte.department.department.option.PARIS = Paris
thing-type.config.meteoalerte.department.department.option.PAS-DE-CALAIS = Pas de Calais
thing-type.config.meteoalerte.department.department.option.PUY-DE-DOME = Puy de Dôme
thing-type.config.meteoalerte.department.department.option.PYRENEES-ATLANTIQUES = Pyrénées Atlantiques
thing-type.config.meteoalerte.department.department.option.PYRENEES-ORIENTALES = Pyrénées Orientales
thing-type.config.meteoalerte.department.department.option.RHONE = Rhône
thing-type.config.meteoalerte.department.department.option.SAONE-ET-LOIRE = Saône et Loire
thing-type.config.meteoalerte.department.department.option.SARTHE = Sarthe
thing-type.config.meteoalerte.department.department.option.SAVOIE = Savoie
thing-type.config.meteoalerte.department.department.option.SEINE-ET-MARNE = Seine et Marne
thing-type.config.meteoalerte.department.department.option.SEINE-MARITIME = Seine Maritime
thing-type.config.meteoalerte.department.department.option.SEINE-SAINT-DENIS = Seine Saint Denis
thing-type.config.meteoalerte.department.department.option.SOMME = Somme
thing-type.config.meteoalerte.department.department.option.TARN = Tarn
thing-type.config.meteoalerte.department.department.option.TARN-ET-GARONNE = Tarn et Garonne
thing-type.config.meteoalerte.department.department.option.TERRITOIRE\ DE\ BELFORT = Territoire de Belfort
thing-type.config.meteoalerte.department.department.option.VAL-D'OISE = Val D'Oise
thing-type.config.meteoalerte.department.department.option.VAL-DE-MARNE = Val de Marne
thing-type.config.meteoalerte.department.department.option.VAR = Var
thing-type.config.meteoalerte.department.department.option.VAUCLUSE = Vaucluse
thing-type.config.meteoalerte.department.department.option.VENDEE = Vendée
thing-type.config.meteoalerte.department.department.option.VIENNE = Vienne
thing-type.config.meteoalerte.department.department.option.VOSGES = Vosges
thing-type.config.meteoalerte.department.department.option.YONNE = Yonne
thing-type.config.meteoalerte.department.department.option.YVELINES = Yvelines
thing-type.config.meteoalerte.department.refresh.label = Fréquence de rafraichissement
thing-type.config.meteoalerte.department.refresh.description = Période d'actualisation des données en minutes.
# channel types
channel-type.meteoalerte.avalanches.label = Avalanches
channel-type.meteoalerte.avalanches.description = Niveau d'alerte aux avalanches
channel-type.meteoalerte.avalanches.state.option.0 = Vert
channel-type.meteoalerte.avalanches.state.option.1 = Jaune
channel-type.meteoalerte.avalanches.state.option.2 = Orange
channel-type.meteoalerte.avalanches.state.option.3 = Rouge
channel-type.meteoalerte.canicule.label = Canicule
channel-type.meteoalerte.canicule.description = Niveau d'alerte aux fortes températures
channel-type.meteoalerte.canicule.state.option.0 = Vert
channel-type.meteoalerte.canicule.state.option.1 = Jaune
channel-type.meteoalerte.canicule.state.option.2 = Orange
channel-type.meteoalerte.canicule.state.option.3 = Rouge
channel-type.meteoalerte.comment.label = Commentaire
channel-type.meteoalerte.condition-icon.label = Pictogramme
channel-type.meteoalerte.condition-icon.description = Pictogramme associé au niveau d'alerte.
channel-type.meteoalerte.grand-froid.label = Grand Froid
channel-type.meteoalerte.grand-froid.description = Niveau d'alerte aux températures négatives
channel-type.meteoalerte.grand-froid.state.option.0 = Vert
channel-type.meteoalerte.grand-froid.state.option.1 = Jaune
channel-type.meteoalerte.grand-froid.state.option.2 = Orange
channel-type.meteoalerte.grand-froid.state.option.3 = Rouge
channel-type.meteoalerte.inondation.label = Inondation
channel-type.meteoalerte.inondation.description = Niveau d'alerte aux inondations
channel-type.meteoalerte.inondation.state.option.0 = Vert
channel-type.meteoalerte.inondation.state.option.1 = Jaune
channel-type.meteoalerte.inondation.state.option.2 = Orange
channel-type.meteoalerte.inondation.state.option.3 = Rouge
channel-type.meteoalerte.neige.label = Neige
channel-type.meteoalerte.neige.description = Niveau d'alerte aux évènements neigeux
channel-type.meteoalerte.neige.state.option.0 = Vert
channel-type.meteoalerte.neige.state.option.1 = Jaune
channel-type.meteoalerte.neige.state.option.2 = Orange
channel-type.meteoalerte.neige.state.option.3 = Rouge
channel-type.meteoalerte.orage.label = Orage
channel-type.meteoalerte.orage.description = Niveau d'alerte aux évènements orageux
channel-type.meteoalerte.orage.state.option.0 = Vert
channel-type.meteoalerte.orage.state.option.1 = Jaune
channel-type.meteoalerte.orage.state.option.2 = Orange
channel-type.meteoalerte.orage.state.option.3 = Rouge
channel-type.meteoalerte.pluie-inondation.label = Pluie Inondation
channel-type.meteoalerte.pluie-inondation.description = Niveau d'alerte aux inondations causées par les précipitations
channel-type.meteoalerte.pluie-inondation.state.option.0 = Vert
channel-type.meteoalerte.pluie-inondation.state.option.1 = Jaune
channel-type.meteoalerte.pluie-inondation.state.option.2 = Orange
channel-type.meteoalerte.pluie-inondation.state.option.3 = Rouge
channel-type.meteoalerte.timestamp.label = Heure d'observation
channel-type.meteoalerte.vague-submersion.label = Vague Submersion
channel-type.meteoalerte.vague-submersion.description = Niveau d'alerte aux vagues submersion
channel-type.meteoalerte.vague-submersion.state.option.0 = Vert
channel-type.meteoalerte.vague-submersion.state.option.1 = Jaune
channel-type.meteoalerte.vague-submersion.state.option.2 = Orange
channel-type.meteoalerte.vague-submersion.state.option.3 = Rouge
channel-type.meteoalerte.vent.label = Vent
channel-type.meteoalerte.vent.description = Niveau d'alerte aux évènements éoliens
channel-type.meteoalerte.vent.state.option.0 = Vert
channel-type.meteoalerte.vent.state.option.1 = Jaune
channel-type.meteoalerte.vent.state.option.2 = Orange
channel-type.meteoalerte.vent.state.option.3 = Rouge
# iconprovider
iconset.label = Météo Alerte Icons
iconset.description = Icons illustrating weather events provided by Météo Alerte

View File

@ -1,329 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="meteoalerte"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="department">
<label>Alertes Département</label>
<description>Fournit les niveaux d'alerte météo pour un département.</description>
<channels>
<channel id="vague-submersion" typeId="vague-submersion"/>
<channel id="vent" typeId="vent"/>
<channel id="pluie-inondation" typeId="pluie-inondation"/>
<channel id="orage" typeId="orage"/>
<channel id="inondation" typeId="inondation"/>
<channel id="neige" typeId="neige"/>
<channel id="canicule" typeId="canicule"/>
<channel id="grand-froid" typeId="grand-froid"/>
<channel id="avalanches" typeId="avalanches"/>
<channel id="vague-submersion-icon" typeId="condition-icon">
<label>Icône Vague Submersion</label>
</channel>
<channel id="vent-icon" typeId="condition-icon">
<label>Icône Vent</label>
</channel>
<channel id="pluie-inondation-icon" typeId="condition-icon">
<label>Icône Pluie Inondation</label>
</channel>
<channel id="orage-icon" typeId="condition-icon">
<label>Icône Orage</label>
</channel>
<channel id="inondation-icon" typeId="condition-icon">
<label>Icône Inondation</label>
</channel>
<channel id="neige-icon" typeId="condition-icon">
<label>Icône Neige</label>
</channel>
<channel id="canicule-icon" typeId="condition-icon">
<label>Icône Canicule</label>
</channel>
<channel id="grand-froid-icon" typeId="condition-icon">
<label>Icône Grand Froid</label>
</channel>
<channel id="avalanches-icon" typeId="condition-icon">
<label>Icône Avalanches</label>
</channel>
<channel id="comment" typeId="comment"/>
<channel id="observation-time" typeId="timestamp">
<label>Début de validité</label>
</channel>
<channel id="end-time" typeId="timestamp">
<label>Fin de validité</label>
</channel>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
</properties>
<config-description>
<parameter name="department" type="text" required="true">
<label>Département</label>
<description>Nom du département</description>
<options>
<option value="AIN">Ain</option>
<option value="AISNE">Aisne</option>
<option value="ALLIER">Allier</option>
<option value="ALPES-DE-HAUTE-PROVENCE">Alpes de Haute Provence</option>
<option value="ALPES-MARITIMES">Alpes Maritimes</option>
<option value="ARDECHE">Ardèche</option>
<option value="ARDENNES">Ardennes</option>
<option value="ARIEGE">Ariège</option>
<option value="AUBE">Aube</option>
<option value="AUDE">Aude</option>
<option value="AVEYRON">Aveyron</option>
<option value="BAS-RHIN">Bas-Rhin</option>
<option value="BOUCHES-DU-RHONE">Bouches du Rhône</option>
<option value="CALVADOS">Calvados</option>
<option value="CANTAL">Cantal</option>
<option value="CHARENTE">Charente</option>
<option value="CHARENTE-MARITIME">Charente Maritime</option>
<option value="CHER">Cher</option>
<option value="CORREZE">Corrèze</option>
<option value="CORSE-DU-SUD">Corse du Sud</option>
<option value="COTE-D'OR">Côte D'Or</option>
<option value="COTES-D'ARMOR">Côtes D'Armor</option>
<option value="CREUSE">Creuse</option>
<option value="DEUX-SEVRES">Deux Sèvres</option>
<option value="DORDOGNE">Dordogne</option>
<option value="DOUBS">Doubs</option>
<option value="DROME">Drôme</option>
<option value="ESSONNE">Essonne</option>
<option value="EURE">Eure</option>
<option value="EURE-ET-LOIR">Eure et Loir</option>
<option value="FINISTERE">Finistère</option>
<option value="GARD">Gard</option>
<option value="GERS">Gers</option>
<option value="GIRONDE">Gironde</option>
<option value="HAUT-RHIN">Haut-Rhin</option>
<option value="HAUTE-CORSE">Haute Corse</option>
<option value="HAUTE-GARONNE">Haute Garonne</option>
<option value="HAUTE-LOIRE">Haute Loire</option>
<option value="HAUTE-MARNE">Haute Marne</option>
<option value="HAUTE-SAONE">Haute Saône</option>
<option value="HAUTE-SAVOIE">Haute Savoie</option>
<option value="HAUTE-VIENNE">Haute Vienne</option>
<option value="HAUTES-ALPES">Hautes Alpes</option>
<option value="HAUTES-PYRENEES">Hautes Pyrénées</option>
<option value="HAUTS-DE-SEINE">Hauts de Seine</option>
<option value="HERAULT">Hérault</option>
<option value="ILLE-ET-VILAINE">Ille et Vilaine</option>
<option value="INDRE">Indre</option>
<option value="INDRE-ET-LOIRE">Indre et Loire</option>
<option value="ISERE">Isère</option>
<option value="JURA">Jura</option>
<option value="LANDES">Landes</option>
<option value="LOIR-ET-CHER">Loir et Cher</option>
<option value="LOIRE">Loire</option>
<option value="LOIRE-ATLANTIQUE">Loire Atlantique</option>
<option value="LOIRET">Loiret</option>
<option value="LOT">Lot</option>
<option value="LOT-ET-GARONNE">Lot et Garonne</option>
<option value="LOZERE">Lozère</option>
<option value="MAINE-ET-LOIRE">Maine et Loire</option>
<option value="MANCHE">Manche</option>
<option value="MARNE">Marne</option>
<option value="MAYENNE">Mayenne</option>
<option value="MEURTHE-ET-MOSELLE">Meurthe et Moselle</option>
<option value="MEUSE">Meuse</option>
<option value="MORBIHAN">Morbihan</option>
<option value="MOSELLE">Moselle</option>
<option value="NIEVRE">Nièvre</option>
<option value="NORD">Nord</option>
<option value="OISE">Oise</option>
<option value="ORNE">Orne</option>
<option value="PARIS">Paris</option>
<option value="PAS-DE-CALAIS">Pas de Calais</option>
<option value="PUY-DE-DOME">Puy de Dôme</option>
<option value="PYRENEES-ATLANTIQUES">Pyrénées Atlantiques</option>
<option value="PYRENEES-ORIENTALES">Pyrénées Orientales</option>
<option value="RHONE">Rhône</option>
<option value="SAONE-ET-LOIRE">Saône et Loire</option>
<option value="SARTHE">Sarthe</option>
<option value="SAVOIE">Savoie</option>
<option value="SEINE-ET-MARNE">Seine et Marne</option>
<option value="SEINE-MARITIME">Seine Maritime</option>
<option value="SEINE-SAINT-DENIS">Seine Saint Denis</option>
<option value="SOMME">Somme</option>
<option value="TARN">Tarn</option>
<option value="TARN-ET-GARONNE">Tarn et Garonne</option>
<option value="TERRITOIRE DE BELFORT">Territoire de Belfort</option>
<option value="VAL-D'OISE">Val D'Oise</option>
<option value="VAL-DE-MARNE">Val de Marne</option>
<option value="VAR">Var</option>
<option value="VAUCLUSE">Vaucluse</option>
<option value="VENDEE">Vendée</option>
<option value="VIENNE">Vienne</option>
<option value="VOSGES">Vosges</option>
<option value="YONNE">Yonne</option>
<option value="YVELINES">Yvelines</option>
</options>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="refresh" type="integer" min="1" required="true" unit="min">
<label>Fréquence de rafraichissement</label>
<description>Période d'actualisation des données en minutes.</description>
<default>1440</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="vent">
<item-type>Number</item-type>
<label>Vent</label>
<description>Niveau d'alerte aux évènements éoliens</description>
<category>oh:meteoalerte:vent</category>
<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="orage">
<item-type>Number</item-type>
<label>Orage</label>
<description>Niveau d'alerte aux évènements orageux</description>
<category>oh:meteoalerte:orage</category>
<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="inondation">
<item-type>Number</item-type>
<label>Inondation</label>
<description>Niveau d'alerte aux inondations</description>
<category>oh:meteoalerte:inondation</category>
<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="neige">
<item-type>Number</item-type>
<label>Neige</label>
<description>Niveau d'alerte aux évènements neigeux</description>
<category>oh:meteoalerte:neige</category>
<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="canicule">
<item-type>Number</item-type>
<label>Canicule</label>
<description>Niveau d'alerte aux fortes températures</description>
<category>oh:meteoalerte:canicule</category>
<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="grand-froid">
<item-type>Number</item-type>
<label>Grand Froid</label>
<description>Niveau d'alerte aux températures négatives</description>
<category>oh:meteoalerte:grand-froid</category>
<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="avalanches">
<item-type>Number</item-type>
<label>Avalanches</label>
<description>Niveau d'alerte aux avalanches</description>
<category>oh:meteoalerte:avalanches</category>
<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="vague-submersion">
<item-type>Number</item-type>
<label>Vague Submersion</label>
<description>Niveau d'alerte aux vagues submersion</description>
<category>oh:meteoalerte:vague-submersion</category>
<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="pluie-inondation">
<item-type>Number</item-type>
<label>Pluie Inondation</label>
<description>Niveau d'alerte aux inondations causées par les précipitations</description>
<category>oh:meteoalerte:pluie-inondation</category>
<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>
<category>text</category>
<state readOnly="true" pattern="%s"/>
</channel-type>
<channel-type id="timestamp" advanced="true">
<item-type>DateTime</item-type>
<label>Heure d'observation</label>
<category>time</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="condition-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>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="meteoalerte:department">
<instruction-set targetVersion="1">
<update-channel id="vague-submersion">
<type>meteoalerte:vague-submersion</type>
</update-channel>
<update-channel id="vent">
<type>meteoalerte:vent</type>
</update-channel>
<update-channel id="pluie-inondation">
<type>meteoalerte:pluie-inondation</type>
</update-channel>
<update-channel id="orage">
<type>meteoalerte:orage</type>
</update-channel>
<update-channel id="inondation">
<type>meteoalerte:inondation</type>
</update-channel>
<update-channel id="neige">
<type>meteoalerte:neige</type>
</update-channel>
<update-channel id="canicule">
<type>meteoalerte:canicule</type>
</update-channel>
<update-channel id="grand-froid">
<type>meteoalerte:grand-froid</type>
</update-channel>
<update-channel id="avalanches">
<type>meteoalerte:avalanches</type>
</update-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>

View File

@ -0,0 +1,132 @@
# Meteo France Binding
This binding provides information made available by Meteo France:
* Alert level regarding major weather related risk factors (`vigilance` Thing)
* Rain intensity forecasts (`rain-forecast`Thing)
This binding provides its own icon set and provides appropriate static and dynamic SVG icons (see items examples below).
## Discovery
Once a bridge with the API key has been created, appropriate departments can be auto discovered based on system location.
## Binding Configuration
The binding has no configuration options, all configuration is done at Bridge and Thing level.
## Bridge Configuration
The `api` bridge has only one configuration parameter :
| Parameter | Description |
|-----------|-------------------------------------------------------|
| apikey | Data-platform token to access the service. Mandatory. |
To obtain an API key, you must create an account on https://portail-api.meteofrance.fr/web/fr/
Inside the API Portal, create an application on "Bulletin Vigilance" and generate they API key (set duration to 0).
## Thing Configuration
The `vigilance` thing has these configuration parameters:
| Parameter | Description |
|------------|-------------------------------------------------------------------------|
| department | Code of the department (two digits). |
| refresh | Refresh interval in minutes. Optional, the default value is 60 minutes. |
The `rain-forecast` thing has this configuration parameters:
| Parameter | Description |
|-----------|--------------------------------------------------------------|
| location | Geo coordinates to be considered by the service. |
## Channels
The information that are retrieved is available as these channels:
| Channel ID | Item Type | Description |
|-----------------------|-----------|-----------------------------------------------|
| observation-time | DateTime | Date and time of report validity start |
| end-time | DateTime | Date and time of report validity end |
| comment | String | General comments on alerts for the department |
| vent | Number | Wind alert level (*) |
| pluie-inondation | Number | Rain alert level (*) |
| orage | Number | Storm alert level (*) |
| inondation | Number | Flood alert level (*) |
| neige | Number | Snow alert level (*) |
| canicule | Number | Heat alert level (*) |
| grand-froid | Number | Cold alert level (*) |
| avalanches | Number | Avalanche alert level (*) |
| vague-submersion | Number | Wave submersion alert level (*) |
| pluie-inondation-icon | Image | Pictogram of the Rain alert level |
| vent-icon | Image | Pictogram of the Wind alert level |
| orage-icon | Image | Pictogram of Storm alert level |
| inondation-icon | Image | Pictogram of Flood alert level |
| neige-icon | Image | Pictogram of Snow alert level |
| canicule-icon | Image | Pictogram of Heat alert level |
| grand-froid-icon | Image | Pictogram of Cold alert level |
| avalanches-icon | Image | Pictogram of Avalanche alert level |
| vague-submersion-icon | Image | Pictogram of Wave Submersion alert level |
(*) Each alert level has an associated color and description:
| Code | Color | Description |
|------|--------|-------------------------------------------|
| 0 | Green | No particular vigilance |
| 1 | Yellow | Be attentive to the weather situation |
| 2 | Orange | Be "very vigilant" in the concerned areas |
| 3 | Red | Absolute vigilance required |
## Provided icon set
This binding has its own IconProvider and makes available the following list of icons
| Icon Name | Dynamic | Illustration |
|---------------------------------|---------|--------------|
| oh:meteofrance:vent | Yes | ![](doc/images/vent.svg) |
| oh:meteofrance:pluie-inondation | Yes | ![](doc/images/pluie-inondation.svg) |
| oh:meteofrance:orage | Yes | ![](doc/images/orage.svg) |
| oh:meteofrance:inondation | Yes | ![](doc/images/inondation.svg) |
| oh:meteofrance:neige | Yes | ![](doc/images/neige.svg) |
| oh:meteofrance:canicule | Yes | ![](doc/images/canicule.svg) |
| oh:meteofrance:grand-froid | Yes | ![](doc/images/grand-froid.svg) |
| oh:meteofrance:avalanches | Yes | ![](doc/images/avalanches.svg) |
| oh:meteofrance:vague-submersion | Yes | ![](doc/images/vague-submersion.svg) |
| oh:meteofrance:meteo_france | No | ![](doc/images/meteo_france.svg) |
| oh:meteofrance:intensity | Yes | ![](doc/images/intensity.svg) |
## Full Example
meteoalert.things:
```java
Thing meteofrance:department:yvelines @ "MyCity" [department="78", refresh=12]
```
meteoalert.items:
```java
Group gMeteoAlert "Alertes Météo" <oh:meteofrance:meteo_france>
String MA_Dept78 "Département 78 [%s]" <text> (gMeteoAlert) {channel="meteofrance:department:yvelines:comment"}
Number MA_etat_canicule "Canicule [%s]" <oh:meteofrance:canicule> (gMeteoAlert) {channel="meteofrance:department:yvelines:canicule"}
Number MA_etat_grand_froid "Grand Froid [%s]" <oh:meteofrance:grand-froid> (gMeteoAlert) {channel="meteofrance:department:yvelines:grand-froid"}
Number MA_etat_pluie_inondation "Pluie-Inondation [%s]" <oh:meteofrance:pluie-inondation> (gMeteoAlert) {channel="meteofrance:department:yvelines:pluie-inondation"}
Number MA_etat_neige "Neige [%s]" <oh:meteofrance:neige> (gMeteoAlert) {channel="meteofrance:department:yvelines:neige"}
Number MA_etat_vent "Vent [%s]" <oh:meteofrance:vent> (gMeteoAlert) {channel="meteofrance:department:yvelines:vent"}
Number MA_etat_inondation "Inondation [%s]" <oh:meteofrance:inondation> (gMeteoAlert) {channel="meteofrance:department:yvelines:inondation"}
Number MA_etat_orage "Orage [%s]" <oh:meteofrance:orage> (gMeteoAlert) {channel="meteofrance:department:yvelines:orage"}
Number MA_etat_avalanche "Avalanches [%s]" <oh:meteofrance:avalanches> (gMeteoAlert) {channel="meteofrance:department:yvelines:avalanches"}
Image MA_icon_canicule "Canicule" <oh:meteofrance:canicule> (gMeteoAlert) {channel="meteofrance:department:yvelines:canicule-icon"}
Image MA_icon_grand_froid "Grand Froid" <oh:meteofrance:grand-froid> (gMeteoAlert) {channel="meteofrance:department:yvelines:grand-froid-icon"}
Image MA_icon_pluie_inondation "Pluie-Inondation" <oh:meteofrance:pluie-inondation> (gMeteoAlert) {channel="meteofrance:department:yvelines:pluie-inondation-icon"}
Image MA_icon_neige "Neige" <oh:meteofrance:neige> (gMeteoAlert) {channel="meteofrance:department:yvelines:neige-icon"}
Image MA_icon_vent "Vent" <oh:meteofrance:vent> (gMeteoAlert) {channel="meteofrance:department:yvelines:vent-icon"}
Image MA_icon_inondation "Inondation" <oh:meteofrance:inondation> (gMeteoAlert) {channel="meteofrance:department:yvelines:inondation-icon"}
Image MA_icon_orage "Orage" <oh:meteofrance:orage> (gMeteoAlert) {channel="meteofrance:department:yvelines:orage-icon"}
Image MA_icon_avalanche "Avalanche" <oh:meteofrance:avalanches> (gMeteoAlert) {channel="meteofrance:department:yvelines:avalanches-icon"}
DateTime MA_ObservationTS "Timestamp [%1$tH:%1$tM]" <time> (gMeteoAlert) {channel="meteofrance:department:yvelines:observation-time"}
```

View File

@ -0,0 +1,67 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55" height="63">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<path
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
id="prefix__a" />
<path
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
id="prefix__b" />
<path
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2" width="27.056"
height="36.375">
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
<use xlink:href="#prefix__c" />
</mask>
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
</defs>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#000" filter="url(#prefix__e)" />
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__a" mask="url(#prefix__f)" stroke="#fff" stroke-width="4" />
</g>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#000" filter="url(#prefix__g)" />
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__b" mask="url(#prefix__h)" stroke="#fff"
stroke-width="2.16" />
</g>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#000" filter="url(#prefix__i)" />
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__c" mask="url(#prefix__j)" stroke="#fff"
stroke-width="2.88" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -10,8 +10,8 @@
<version>4.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.meteoalerte</artifactId>
<artifactId>org.openhab.binding.meteofrance</artifactId>
<name>openHAB Add-ons :: Bundles :: Meteo Alerte Binding</name>
<name>openHAB Add-ons :: Bundles :: Meteo France Binding</name>
</project>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.meteoalerte-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<features name="org.openhab.binding.meteofrance-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-meteoalerte" description="Meteo Alerte Binding" version="${project.version}">
<feature name="openhab-binding-meteofrance" description="Meteo France Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.meteoalerte/${project.version}</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.meteofrance/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link MeteoFranceBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoFranceBindingConstants {
public static final String BINDING_ID = "meteofrance";
public static final String LOCAL = "local";
public static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(45);
// List of Bridge Type UIDs
public static final ThingTypeUID BRIDGE_TYPE_API = new ThingTypeUID(BINDING_ID, "api");
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_VIGILANCE = new ThingTypeUID(BINDING_ID, "vigilance");
public static final ThingTypeUID THING_TYPE_RAIN_FORECAST = new ThingTypeUID(BINDING_ID, "rain-forecast");
// List of all Channel IDs
public static final String OBSERVATION_TIME = "observation-time";
public static final String UPDATE_TIME = "update-time";
public static final String INTENSITY = "intensity";
public static final String END_TIME = "end-time";
public static final String COMMENT = "comment";
public static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Set.of(THING_TYPE_VIGILANCE,
THING_TYPE_RAIN_FORECAST);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
.of(Set.of(BRIDGE_TYPE_API), DISCOVERABLE_THING_TYPES_UIDS).flatMap(Set::stream)
.collect(Collectors.toSet());
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* An exception that occurred while communicating with Meteo France API server or related processes.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoFranceException extends Exception {
private static final long serialVersionUID = 7613161188837438233L;
public MeteoFranceException(String format, Object... args) {
super(format.formatted(args));
}
public MeteoFranceException(Exception e, String format, Object... args) {
super(format.formatted(args), e);
}
}

View File

@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.*;
import java.time.ZoneId;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteofrance.internal.db.DepartmentDbService;
import org.openhab.binding.meteofrance.internal.deserialization.MeteoFranceDeserializer;
import org.openhab.binding.meteofrance.internal.handler.MeteoFranceBridgeHandler;
import org.openhab.binding.meteofrance.internal.handler.RainForecastHandler;
import org.openhab.binding.meteofrance.internal.handler.VigilanceHandler;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link MeteoFranceHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = ThingHandlerFactory.class)
@NonNullByDefault
public class MeteoFranceHandlerFactory extends BaseThingHandlerFactory {
private final MeteoFranceIconProvider iconProvider;
private final MeteoFranceDeserializer deserializer;
private final ZoneId zoneId;
@Activate
public MeteoFranceHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider,
final @Reference LocationProvider locationProvider, final @Reference DepartmentDbService dbService,
final @Reference MeteoFranceIconProvider iconProvider,
final @Reference MeteoFranceDeserializer deserializer) {
this.iconProvider = iconProvider;
this.deserializer = deserializer;
this.zoneId = timeZoneProvider.getTimeZone();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
return BRIDGE_TYPE_API.equals(thingTypeUID) ? new MeteoFranceBridgeHandler((Bridge) thing, deserializer)
: THING_TYPE_VIGILANCE.equals(thingTypeUID) ? new VigilanceHandler(thing, zoneId, iconProvider)
: THING_TYPE_RAIN_FORECAST.equals(thingTypeUID) ? new RainForecastHandler(thing, zoneId) : null;
}
}

View File

@ -10,21 +10,24 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.meteoalerte.internal;
package org.openhab.binding.meteofrance.internal;
import static org.openhab.binding.meteoalerte.internal.MeteoAlerteBindingConstants.*;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteoalerte.internal.json.ResponseFieldDTO.AlertLevel;
import org.openhab.binding.meteofrance.internal.dto.Hazard;
import org.openhab.binding.meteofrance.internal.dto.Risk;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.IconProvider;
import org.openhab.core.ui.icon.IconSet;
@ -37,24 +40,27 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link MeteoAlertIconProvider} is the class providing binding related icons.
* The {@link MeteoFranceIconProvider} is the class providing binding related icons.
*
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = { IconProvider.class, MeteoAlertIconProvider.class })
@Component(service = { IconProvider.class, MeteoFranceIconProvider.class })
@NonNullByDefault
public class MeteoAlertIconProvider implements IconProvider {
private static final String DEFAULT_LABEL = "Météo Alerte Icons";
private static final String DEFAULT_DESCRIPTION = "Icons illustrating weather events provided by Météo Alerte";
private static final Set<String> ICONS = Set.of(WAVE, AVALANCHE, HEAT, FREEZE, FLOOD, SNOW, STORM, RAIN, WIND,
"meteo_france");
public class MeteoFranceIconProvider implements IconProvider {
private static final String NEUTRAL_COLOR = "#3d3c3c";
private static final String DEFAULT_LABEL = "Météo France Icons";
private static final String DEFAULT_DESCRIPTION = "Icons illustrating weather alerts provided by Météo France";
private static final List<String> HAZARD_ICONS = Hazard.AS_SET.stream().filter(Hazard::isChannel)
.map(h -> h.channelName).toList();
private static final List<String> ICONS = Stream.concat(Stream.of("meteo_france", INTENSITY), HAZARD_ICONS.stream())
.toList();
private final Logger logger = LoggerFactory.getLogger(MeteoAlertIconProvider.class);
private final Logger logger = LoggerFactory.getLogger(MeteoFranceIconProvider.class);
private final BundleContext context;
private final TranslationProvider i18nProvider;
@Activate
public MeteoAlertIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
public MeteoFranceIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
this.context = context;
this.i18nProvider = i18nProvider;
}
@ -83,7 +89,7 @@ public class MeteoAlertIconProvider implements IconProvider {
@Override
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
return ICONS.contains(category) && iconSetId.equals(BINDING_ID) && format == Format.SVG ? 0 : null;
return Format.SVG.equals(format) && iconSetId.equals(BINDING_ID) && ICONS.contains(category) ? 0 : null;
}
public @Nullable InputStream getIcon(String category, String state) {
@ -92,17 +98,25 @@ public class MeteoAlertIconProvider implements IconProvider {
@Override
public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) {
String icon = getResource(category);
String iconName = category;
if (INTENSITY.equals(category) && state != null) {
String localState = "on".equalsIgnoreCase(state) ? "3" : "off".equalsIgnoreCase(state) ? "0" : state;
try {
iconName = "%s-%d".formatted(category, Double.valueOf(localState).intValue());
} catch (NumberFormatException e) {
logger.debug("Unable to parse {} to a numeric value", state);
}
}
String icon = getResource(iconName);
if (icon.isEmpty()) {
return null;
}
if (state != null) {
if (state != null && HAZARD_ICONS.contains(category)) {
try {
Integer ordinal = Integer.valueOf(state);
AlertLevel alertLevel = ordinal < AlertLevel.values().length ? AlertLevel.values()[ordinal]
: AlertLevel.UNKNOWN;
icon = icon.replaceAll(AlertLevel.UNKNOWN.color, alertLevel.color);
int ordinal = Integer.valueOf(state);
Risk alertLevel = ordinal < Risk.values().length ? Risk.values()[ordinal] : Risk.UNKNOWN;
icon = icon.replaceAll(NEUTRAL_COLOR, alertLevel.rgbColor);
} catch (NumberFormatException e) {
logger.debug("{} is not a valid DecimalType", state);
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BridgeConfiguration} is the class used to match the bridge configuration.
*
* @author Gaël L"hopital - Initial contribution
*/
@NonNullByDefault
public class BridgeConfiguration {
public String apikey = "";
}

View File

@ -10,18 +10,19 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.meteoalerte.internal;
package org.openhab.binding.meteofrance.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link MeteoAlerteConfiguration} is the class used to match the
* The {@link DepartmentConfiguration} is the class used to match the
* thing configuration.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoAlerteConfiguration {
public class DepartmentConfiguration {
public static final String DEPARTMENT = "department";
public String department = "";
public int refresh = 1440;
public int refresh = 60;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link ForecastConfiguration} is the class used to match the rain forecast thing configuration.
*
* @author Gaël L"hopital - Initial contribution
*/
@NonNullByDefault
public class ForecastConfiguration {
public static final String LOCATION = "location";
public String location = "";
}

View File

@ -0,0 +1,77 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.db;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
/**
* The {@link DepartmentDbService} makes available a list of known French Metropolitan departments.
*
* @author Gaël L'hopital - Initial Contribution
*/
@Component(service = DepartmentDbService.class)
@NonNullByDefault
public class DepartmentDbService {
private final Logger logger = LoggerFactory.getLogger(DepartmentDbService.class);
private final List<Department> departments = new ArrayList<>();
public record Department(String id, String name, double northestLat, double southestLat, double eastestLon,
double westestLon) {
}
@Activate
public DepartmentDbService() {
try (InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("/db/departments.json");
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());
} catch (IOException | JsonSyntaxException | JsonIOException e) {
logger.warn("Unable to load departments list: {}", e.getMessage());
}
}
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);
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.deserialization;
import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteofrance.internal.MeteoFranceException;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Periods;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Timelaps;
import org.openhab.core.i18n.TimeZoneProvider;
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.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSyntaxException;
/**
* The {@link MeteoFranceDeserializer} is responsible to instantiate suitable Gson (de)serializer
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
@Component(service = MeteoFranceDeserializer.class)
public class MeteoFranceDeserializer {
private final Gson gson;
@Activate
public MeteoFranceDeserializer(final @Reference TimeZoneProvider timeZoneProvider) {
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
.registerTypeAdapter(Periods.class, new PeriodsDeserializer())
.registerTypeAdapter(Timelaps.class, new TimelapsDeserializer())
.registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, context) -> ZonedDateTime
.parse(json.getAsJsonPrimitive().getAsString())
.withZoneSameInstant(timeZoneProvider.getTimeZone()))
.create();
}
public <T> T deserialize(Class<T> clazz, String json) throws MeteoFranceException {
try {
@Nullable
T result = gson.fromJson(json, clazz);
if (result != null) {
return result;
}
throw new MeteoFranceException("Deserialization of '%s' resulted in null value", json);
} catch (JsonSyntaxException e) {
throw new MeteoFranceException(e, "Unexpected error deserializing '%s'", json);
}
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.deserialization;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Period;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Periods;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link PeriodsDeserializer} is a specialized deserializer aimed to transform
* a list of `Period` into a map identified by the Period Term.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
class PeriodsDeserializer implements JsonDeserializer<Periods> {
@Override
public @NonNull Periods deserialize(JsonElement json, Type clazz, JsonDeserializationContext context)
throws JsonParseException {
Periods result = new Periods();
if (json instanceof JsonArray jsonArray) {
jsonArray.forEach(item -> {
Period period = context.deserialize(item, Period.class);
result.put(period.echeance(), period);
});
}
return result;
}
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.deserialization;
import java.io.IOException;
import java.io.StringReader;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
/**
* Enforces a fallback to UNKNOWN when deserializing enum types, marked as @NonNull whereas they were valued
* to null if the appropriate value is absent.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
class StrictEnumTypeAdapterFactory implements TypeAdapterFactory {
private static final StringReader UNKNOWN = new StringReader("\"UNKNOWN\"");
@Override
public @Nullable <T> TypeAdapter<T> create(@NonNullByDefault({}) Gson gson,
@NonNullByDefault({}) TypeToken<T> type) {
@SuppressWarnings("unchecked")
Class<T> rawType = (Class<T>) type.getRawType();
return rawType.isEnum() ? newStrictEnumAdapter(gson.getDelegateAdapter(this, type)) : null;
}
private <T> TypeAdapter<T> newStrictEnumAdapter(@NonNullByDefault({}) TypeAdapter<T> delegateAdapter) {
return new TypeAdapter<>() {
@Override
public void write(JsonWriter out, @Nullable T value) throws IOException {
delegateAdapter.write(out, value);
}
@Override
public @Nullable T read(JsonReader in) throws IOException {
JsonReader delegateReader = new JsonReader(new StringReader('"' + in.nextString() + '"'));
@Nullable
T value = delegateAdapter.read(delegateReader);
delegateReader.close();
if (value == null) {
UNKNOWN.reset();
value = delegateAdapter.read(new JsonReader(UNKNOWN));
}
return value;
}
};
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.deserialization;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.DomainId;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Timelaps;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link TimelapsDeserializer} is a specialized deserializer aimed to transform
* a list of `Period` into a map identified by the Period Term.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
class TimelapsDeserializer implements JsonDeserializer<Timelaps> {
@Override
public @NonNull Timelaps deserialize(JsonElement json, Type clazz, JsonDeserializationContext context)
throws JsonParseException {
Timelaps result = new Timelaps();
JsonElement domains = json.getAsJsonObject().get("domain_ids");
if (domains instanceof JsonArray jsonArray) {
jsonArray.forEach(item -> {
DomainId domainId = context.deserialize(item, DomainId.class);
result.put(domainId.domain(), domainId);
});
}
return result;
}
}

View File

@ -0,0 +1,111 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.discovery;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.*;
import static org.openhab.binding.meteofrance.internal.config.ForecastConfiguration.LOCATION;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.meteofrance.internal.config.DepartmentConfiguration;
import org.openhab.binding.meteofrance.internal.db.DepartmentDbService;
import org.openhab.binding.meteofrance.internal.db.DepartmentDbService.Department;
import org.openhab.binding.meteofrance.internal.handler.MeteoFranceBridgeHandler;
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.PointType;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link MeteoFranceDiscoveryService} discovers departments on the configured location.
*
* @author Gaël L'hopital - Initial Contribution
*/
@Component(service = ThingHandlerService.class)
@NonNullByDefault
public class MeteoFranceDiscoveryService extends AbstractThingHandlerDiscoveryService<MeteoFranceBridgeHandler> {
private static final int DISCOVER_TIMEOUT_SECONDS = 2;
private @NonNullByDefault({}) LocationProvider locationProvider;
private @NonNullByDefault({}) DepartmentDbService dbService;
private final Logger logger = LoggerFactory.getLogger(MeteoFranceDiscoveryService.class);
public MeteoFranceDiscoveryService() {
super(MeteoFranceBridgeHandler.class, DISCOVERABLE_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS);
}
@Reference(unbind = "-")
public void bindTranslationProvider(TranslationProvider translationProvider) {
this.i18nProvider = translationProvider;
}
@Reference(unbind = "-")
public void bindLocaleProvider(LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
@Reference(unbind = "-")
public void bindLocationProvider(LocationProvider locationProvider) {
this.locationProvider = locationProvider;
}
@Reference(unbind = "-")
public void bindDepartmentDbService(DepartmentDbService dbService) {
this.dbService = dbService;
}
@Override
public void startScan() {
logger.debug("Starting Meteo France department discovery scan");
LocationProvider localLocation = locationProvider;
PointType location = localLocation != null ? localLocation.getLocation() : null;
if (location == null) {
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
return;
}
createDepartmentResults(location);
createForecastResults(location);
}
private void createForecastResults(PointType location) {
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_RAIN_FORECAST, LOCAL))
.withLabel("@text/discovery.meteofrance.rain-forecast.local.label")
.withProperty(LOCATION, location.toString()).withRepresentationProperty(LOCATION).build());
}
private void createDepartmentResults(PointType serverLocation) {
List<Department> candidates = dbService.getBounding(serverLocation);
ThingUID bridgeUID = thingHandler.getThing().getUID();
if (!candidates.isEmpty()) {
candidates.forEach(dep -> thingDiscovered(
DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_VIGILANCE, bridgeUID, dep.id()))//
.withLabel("Vigilance Météo: %s".formatted(dep.name())) //
.withProperty(DepartmentConfiguration.DEPARTMENT, dep.id()) //
.withRepresentationProperty(DepartmentConfiguration.DEPARTMENT) //
.withBridge(bridgeUID).build()));
} else {
logger.info("No department could be discovered matching server location");
}
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum BlocType {
SITUATION, // Situation Actuelle
INCERTITUDE, // Prévisibilité et incertitudes
QUALIFICATION, // Qualification
EVOLUTION, // Évolution prévue
UNKNOWN // Inconnue
}

View File

@ -0,0 +1,269 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import java.lang.reflect.Field;
import java.util.EnumSet;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Domain {
@SerializedName("FRA")
FRANCE("France"),
@SerializedName("ZDF_EST")
ZDF_EST("Défense Est"),
@SerializedName("ZDF_PARIS")
ZDF_PARIS("Défense Paris"),
@SerializedName("ZDF_NORD")
ZDF_NORD("Défense Nord"),
@SerializedName("ZDF_SUD_EST")
ZDF_SUD_EST("Défense Sud-Est"),
@SerializedName("ZDF_SUD_OUEST")
ZDF_SUD_OUEST("Défense Sud-Ouest"),
@SerializedName("ZDF_SUD")
ZDF_SUD("Défense Sud"),
@SerializedName("ZDF_OUEST")
ZDF_OUEST("Défense Ouest"),
@SerializedName("01")
AIN("Ain"),
@SerializedName("02")
AISNE("Aisne"),
@SerializedName("03")
ALLIER("Allier"),
@SerializedName("04")
ALPES_DE_HAUTE_PROVENCE("Alpes-de-Haute-Provence"),
@SerializedName("05")
HAUTES_ALPES("Hautes-Alpes"),
@SerializedName("06")
ALPES_MARITIMES("Alpes-Maritimes"),
@SerializedName("07")
ARDECHE("Ardèche"),
@SerializedName("08")
ARDENNES("Ardennes"),
@SerializedName("09")
ARIEGE("Ariège"),
@SerializedName("10")
AUBE("Aube"),
@SerializedName("11")
AUDE("Aude"),
@SerializedName("12")
AVEYRON("Aveyron"),
@SerializedName("13")
BOUCHES_DU_RHONE("Bouches-du-Rhône"),
@SerializedName("14")
CALVADOS("Calvados"),
@SerializedName("15")
CANTAL("Cantal"),
@SerializedName("16")
CHARENTE("Charente"),
@SerializedName("17")
CHARENTE_MARITIME("Charente-Maritime"),
@SerializedName("18")
CHER("Cher"),
@SerializedName("19")
CORREZE("Corrèze"),
@SerializedName("2A")
CORSE_DU_SUD("Corse-du-Sud"),
@SerializedName("2B")
HAUTE_CORSE("Haute-Corse"),
@SerializedName("21")
COTE_D_OR("Côte-d'Or"),
@SerializedName("22")
COTES_D_ARMOR("Côtes-d'Armor"),
@SerializedName("23")
CREUSE("Creuse"),
@SerializedName("24")
DORDOGNE("Dordogne"),
@SerializedName("25")
DOUBS("Doubs"),
@SerializedName("26")
DROME("Drôme"),
@SerializedName("27")
EURE("Eure"),
@SerializedName("28")
EURE_ET_LOIR("Eure-et-Loir"),
@SerializedName("29")
FINISTERE("Finistère"),
@SerializedName("30")
GARD("Gard"),
@SerializedName("31")
HAUTE_GARONNE("Haute-Garonne"),
@SerializedName("32")
GERS("Gers"),
@SerializedName("33")
GIRONDE("Gironde"),
@SerializedName("34")
HERAULT("Hérault"),
@SerializedName("35")
ILLE_ET_VILAINE("Ille-et-Vilaine"),
@SerializedName("36")
INDRE("Indre"),
@SerializedName("37")
INDRE_ET_LOIRE("Indre-et-Loire"),
@SerializedName("38")
ISERE("Isère"),
@SerializedName("39")
JURA("Jura"),
@SerializedName("40")
LANDES("Landes"),
@SerializedName("41")
LOIR_ET_CHER("Loir-et-Cher"),
@SerializedName("42")
LOIRE("Loire"),
@SerializedName("43")
HAUTE_LOIRE("Haute-Loire"),
@SerializedName("44")
LOIRE_ATLANTIQUE("Loire-Atlantique"),
@SerializedName("45")
LOIRET("Loiret"),
@SerializedName("46")
LOT("Lot"),
@SerializedName("47")
LOT_ET_GARONNE("Lot-et-Garonne"),
@SerializedName("48")
LOZERE("Lozère"),
@SerializedName("49")
MAINE_ET_LOIRE("Maine-et-Loire"),
@SerializedName("50")
MANCHE("Manche"),
@SerializedName("51")
MARNE("Marne"),
@SerializedName("52")
HAUTE_MARNE("Haute-Marne"),
@SerializedName("53")
MAYENNE("Mayenne"),
@SerializedName("54")
MEURTHE_ET_MOSELLE("Meurthe-et-Moselle"),
@SerializedName("55")
MEUSE("Meuse"),
@SerializedName("56")
MORBIHAN("Morbihan"),
@SerializedName("57")
MOSELLE("Moselle"),
@SerializedName("58")
NIEVRE("Nièvre"),
@SerializedName("59")
NORD("Nord"),
@SerializedName("60")
OISE("Oise"),
@SerializedName("61")
ORNE("Orne"),
@SerializedName("62")
PAS_DE_CALAIS("Pas-de-Calais"),
@SerializedName("63")
PUY_DE_DOME("Puy-de-Dôme"),
@SerializedName("64")
PYRENEES_ATLANTIQUES("Pyrénées-Atlantiques"),
@SerializedName("65")
HAUTES_PYRENEES("Hautes-Pyrénées"),
@SerializedName("66")
PYRENEES_ORIENTALES("Pyrénées-Orientales"),
@SerializedName("67")
BAS_RHIN("Bas-Rhin"),
@SerializedName("68")
HAUT_RHIN("Haut-Rhin"),
@SerializedName("69")
RHONE("Rhône"),
@SerializedName("70")
HAUTE_SAONE("Haute-Saône"),
@SerializedName("71")
SAONE_ET_LOIRE("Saône-et-Loire"),
@SerializedName("72")
SARTHE("Sarthe"),
@SerializedName("73")
SAVOIE("Savoie"),
@SerializedName("74")
HAUTE_SAVOIE("Haute-Savoie"),
@SerializedName("75")
PARIS("Paris"),
@SerializedName("76")
SEINE_MARITIME("Seine-Maritime"),
@SerializedName("77")
SEINE_ET_MARNE("Seine-et-Marne"),
@SerializedName("78")
YVELINES("Yvelines"),
@SerializedName("79")
DEUX_SEVRES("Deux-Sèvres"),
@SerializedName("80")
SOMME("Somme"),
@SerializedName("81")
TARN("Tarn"),
@SerializedName("82")
TARN_ET_GARONNE("Tarn-et-Garonne"),
@SerializedName("83")
VAR("Var"),
@SerializedName("84")
VAUCLUSE("Vaucluse"),
@SerializedName("85")
VENDEE("Vendée"),
@SerializedName("86")
VIENNE("Vienne"),
@SerializedName("87")
HAUTE_VIENNE("Haute-Vienne"),
@SerializedName("88")
VOSGES("Vosges"),
@SerializedName("89")
YONNE("Yonne"),
@SerializedName("90")
TERRITOIRE_DE_BELFORT("Territoire de Belfort"),
@SerializedName("91")
ESSONNE("Essonne"),
@SerializedName("92")
HAUTS_DE_SEINE("Hauts-de-Seine"),
@SerializedName("93")
SEINE_SAINT_DENIS("Seine-Saint-Denis"),
@SerializedName("94")
VAL_DE_MARNE("Val-de-Marne"),
@SerializedName("95")
VAL_D_OISE("Val-d'Oise"),
@SerializedName("99")
ANDORRE("Andorre"),
UNKNOWN("Unknown");
public final String description;
private final @Nullable String apiId;
Domain(String description) {
this.description = description;
this.apiId = getSerializedName();
}
@Nullable
private String getSerializedName() {
try {
Field f = getClass().getField(this.name());
SerializedName a = f.getAnnotation(SerializedName.class);
if (a != null) {
return a.value();
}
} catch (NoSuchFieldException ignored) {
}
return null;
}
public static Domain getByApiId(String searched) {
return Objects.requireNonNull(EnumSet.allOf(Domain.class).stream().filter(dom -> searched.equals(dom.apiId))
.findFirst().orElse(UNKNOWN));
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import java.util.EnumSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Hazard {
@SerializedName("1")
VENT("vent"),
@SerializedName("2")
PLUIE("pluie-inondation"),
@SerializedName("3")
ORAGES("orage"),
@SerializedName("4")
CRUES("inondation"),
@SerializedName("5")
NEIGE_VERGLAS("neige"),
@SerializedName("6")
CANICULE("canicule"),
@SerializedName("7")
GRAND_FROID("grand-froid"),
@SerializedName("8")
AVALANCHES("avalanches"),
@SerializedName("9")
VAGUES_SUBMERSION("vague-submersion"),
ALL(""),
UNKNOWN("");
public static final EnumSet<Hazard> AS_SET = EnumSet.allOf(Hazard.class);
public final String channelName;
Hazard(String channelName) {
this.channelName = channelName;
}
public boolean isChannel() {
return !channelName.isEmpty();
}
}

View File

@ -0,0 +1,174 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class MeteoFrance {
public record VigilanceEnCours(//
@Nullable Product product, //
@Nullable Meta meta, //
int code, // error code, 0 if all went well
@Nullable String message, // error message when code != 0
@Nullable String detail) {
}
public record Meta(//
String snapshotId, // identifiant du snapshot de la saisie validée
ZonedDateTime productDatetime, // date/heure de diffusion du produit
ZonedDateTime generationTimestamp) { // date/heure de début de validité du produit
}
public record Product(//
String warningType, // "vigilance" - produit de la famille Vigilance
String typeCdp, // "cdp_textes" - nom générique du produit
String versionVigilance, // "V6" - Version du système développé
String versionCdp, // "x.y.z" - version du format pour pouvoir gérer d'anciens jeux de données
ZonedDateTime updateTime, // "AAAA-MM-JJThh:mm:ssZ" - date/heure de diffusion du produit
@SerializedName("domain_id") Domain domain, //
@Nullable List<TextBlocItem> textBlocItems, // contenu textuel des bulletins
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();
}
public Optional<Period> getPeriod(Term term) {
return Optional.ofNullable(periods.get(term));
}
}
public static class Periods extends HashMap<Term, @Nullable Period> {
private static final long serialVersionUID = -4448877461442293135L;
}
public static class Timelaps extends HashMap<Domain, @Nullable DomainId> {
private static final long serialVersionUID = -441522278695388073L;
}
public record TextBlocItem( //
@SerializedName("domain_id") Domain domain, // code du domaine sur lequel s'applique le bloc textuel
String blocTitle, //
String blocId, // indique le type de bloc
List<BlocItem> blocItems) { // rubriques du bulletin : SITUATION,QUALIFICATION, INCERTITUDE,EVOLUTION
private boolean validFor(Domain searched) {
return searched.equals(domain);
}
}
public record Period( //
Term echeance, //
ZonedDateTime beginValidityTime, //
ZonedDateTime endValidityTime, //
TextItems textItems, //
Timelaps timelaps, //
List<PhenomenonCount> maxCountItems, //
List<PerPhenomenonItem> perPhenomenonItems) {//
}
private record TextItems( //
String title, //
List<String> text) {//
}
public record DomainId( //
@SerializedName("domain_id") Domain domain, //
int maxColorId, //
List<PhenomenonItem> phenomenonItems) { //
}
public record PhenomenonItem( //
Hazard phenomenonId, //
Risk phenomenonMaxColorId, //
List<TimelapsItem> timelapsItems) { //
}
public record TimelapsItem( //
ZonedDateTime beginTime, //
ZonedDateTime endTime, //
Risk colorId) { //
public boolean contains(ZonedDateTime moment) {
return beginTime.isBefore(moment) && endTime.isAfter(moment);
}
}
private record PhenomenonCount(// )
int colorId, //
String colorName, //
int count, //
String textCount) { //
}
private record PerPhenomenonItem( //
String phenomenonId, //
int anyColorCount, //
List<PhenomenonCount> phenomenonCounts) { //
}
public record BlocItem( //
String id, // identifiant de la rubrique texte
@SerializedName("type_group") BlocType type, // code du groupe de la rubrique
List<TextItem> textItems) { // textes à associer à la carte comme les commentaires
}
public record TextItem( //
@Nullable String hazardCode, // code du phénomène
String typeCode, // code de l'élément de suivi
List<TermItem> termItems) {// tableau des éléments de suivi par échéance : J, J1 ou J+J1
public Hazard getHazard() {
return hazardCode == null ? Hazard.ALL : Hazard.valueOf(Objects.requireNonNull(hazardCode));
}
}
public record TermItem(//
@SerializedName("term_names") Term term, // code de l'échéance
@SerializedName("risk_code") Risk risk, // code du risque
ZonedDateTime startTime, // date/heure début d'échéance
ZonedDateTime endTime, // date/heure fin d'échéance
List<SubdivisionText> subdivisionText) {// sous_textes de même type d'éléments de suivi
public String getText(ZonedDateTime moment) {
return startTime.isBefore(moment) && endTime.isAfter(moment) ? subdivisionText.stream()
.map(SubdivisionText::getText).collect(Collectors.joining(System.lineSeparator())) : "";
}
}
private record SubdivisionText( //
String underlineText, // texte devant être souligné dans le bulletin
List<String> text) { // contenu du texte ligne par ligne
String getText() {
return (underlineText.isEmpty() ? "" : underlineText + System.lineSeparator()) + text.stream()
.filter(Predicate.not(String::isEmpty)).collect(Collectors.joining(System.lineSeparator()));
}
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import java.time.ZonedDateTime;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class RainForecast {
public enum RainIntensity {
@SerializedName("1")
TEMPS_SEC,
@SerializedName("2")
FAIBLE,
@SerializedName("3")
MODEREE,
@SerializedName("4")
FORTE,
UNKNOWN;
}
public record Forecast(ZonedDateTime time, RainIntensity rainIntensity, String rainIntensityDescription) {
}
public record Geometry(String type, List<Double> coordinates) {
}
public class Properties {
public int altitude;
public String name = "";
public String country = "";
public String frenchDepartment = "";
public int rainProductAvailable;
public String timezone = "";
public int confidence;
public List<Forecast> forecast = List.of();
}
public @Nullable ZonedDateTime updateTime;
public String type = "";
public @Nullable Geometry geometry;
public @Nullable Properties properties;
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Risk {
@SerializedName("1")
VERT("#15ed13", 0),
@SerializedName("2")
JAUNE("#f9ff00", 1),
@SerializedName("3")
ORANGE("#f7a401", 2),
@SerializedName("4")
ROUGE("#e71919", 3),
UNKNOWN("#000000", -1);
public final String rgbColor;
public final int riskLevel;
Risk(String rgbColor, int riskLevel) {
this.rgbColor = rgbColor;
this.riskLevel = riskLevel;
}
}

View File

@ -10,24 +10,22 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.meteoalerte.internal.json;
package org.openhab.binding.meteofrance.internal.dto;
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 nomDept = "";
public String getNomDept() {
return nomDept;
}
public enum Term {
@SerializedName("J")
TODAY,
@SerializedName("J1")
TOMORROW,
@SerializedName("J+J1")
TODAY_AND_TOMORROW,
UNKNOWN
}

View File

@ -0,0 +1,175 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.handler;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.REQUEST_TIMEOUT_MS;
import java.io.IOException;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.HttpMethod;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteofrance.internal.MeteoFranceException;
import org.openhab.binding.meteofrance.internal.config.BridgeConfiguration;
import org.openhab.binding.meteofrance.internal.deserialization.MeteoFranceDeserializer;
import org.openhab.binding.meteofrance.internal.dto.Domain;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.DomainId;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Meta;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Period;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Product;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.TextBlocItem;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.VigilanceEnCours;
import org.openhab.binding.meteofrance.internal.dto.RainForecast;
import org.openhab.binding.meteofrance.internal.dto.Term;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.PointType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link MeteoFranceBridgeHandler} is the handler for Meteo France bridge and connects it
* to the Meteo France API Portal
*
* @author Gaël L'hopital - Initial contribution
*
*/
@NonNullByDefault
public class MeteoFranceBridgeHandler extends BaseBridgeHandler {
private static final String PORTAIL_API_BASE_URL = "https://public-api.meteofrance.fr/public/DPVigilance/v1/%s/encours";
private static final String TEXTE_VIGILANCE_URL = PORTAIL_API_BASE_URL.formatted("textesvigilance");
private static final String CARTE_VIGILANCE_URL = PORTAIL_API_BASE_URL.formatted("cartevigilance");
private static final String RAIN_FORECAST_BASE_URL = "https://rpcache-aa.meteofrance.com/internet2018client/2.0/nowcast/rain?lat=%.4f&lon=%.4f&token=__Wj7dVSTjV9YGu1guveLyDq0g7S7TfTjaHBTPTpO0kj8__";
private static final long CACHE_EXPIRY = TimeUnit.MINUTES.toMillis(10);
private final Logger logger = LoggerFactory.getLogger(MeteoFranceBridgeHandler.class);
private final Properties header = new Properties();
private final MeteoFranceDeserializer deserializer;
private final ExpiringCache<VigilanceEnCours> vigilanceText;
private final ExpiringCache<VigilanceEnCours> vigilanceMap;
public MeteoFranceBridgeHandler(Bridge bridge, MeteoFranceDeserializer deserializer) {
super(bridge);
this.deserializer = deserializer;
vigilanceText = new ExpiringCache<>(CACHE_EXPIRY, () -> this.getVigilanceEnCours(TEXTE_VIGILANCE_URL));
vigilanceMap = new ExpiringCache<>(CACHE_EXPIRY, () -> this.getVigilanceEnCours(CARTE_VIGILANCE_URL));
}
@Override
public void initialize() {
logger.debug("Initializing Meteo-France API bridge handler.");
BridgeConfiguration config = getConfigAs(BridgeConfiguration.class);
if (config.apikey.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-apikey");
return;
}
header.put("apikey", config.apikey);
header.put("accept", "*/*");
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(() -> vigilanceText.getValue());
}
@Override
public void dispose() {
header.clear();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// The bridge does not handle commands
}
private @Nullable VigilanceEnCours getVigilanceEnCours(String url) {
try {
String answer = HttpUtil.executeUrl(HttpMethod.GET, url, header, null, null, REQUEST_TIMEOUT_MS);
logger.trace(answer);
VigilanceEnCours vigilance = deserializer.deserialize(MeteoFrance.VigilanceEnCours.class, answer);
if (vigilance.code() != 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, vigilance.message());
} else {
updateStatus(ThingStatus.ONLINE);
return vigilance;
}
} catch (MeteoFranceException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Exception deserializing API answer: %s".formatted(e.getMessage()));
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
return null;
}
public @Nullable TextBlocItem requestTextData(Domain domain) {
VigilanceEnCours local = vigilanceText.getValue();
if (local != null) {
Product product = local.product();
if (product != null) {
return product.getBlocItem(domain).get();
}
}
return null;
}
public @Nullable DomainId requestMapData(Domain domain, Term term) {
Period period = requestPeriod(term);
return period != null ? period.timelaps().get(domain) : null;
}
public @Nullable Period requestPeriod(Term term) {
VigilanceEnCours local = vigilanceMap.getValue();
if (local != null) {
Product product = local.product();
if (product != null) {
return product.getPeriod(term).get();
}
}
return null;
}
public Optional<Meta> getMeta() {
VigilanceEnCours local = vigilanceText.getValue();
return Optional.ofNullable(local != null ? local.meta() : null);
}
public @Nullable RainForecast getRainForecast(PointType location) {
String url = RAIN_FORECAST_BASE_URL.formatted(location.getLatitude().doubleValue(),
location.getLongitude().doubleValue());
try {
String answer = HttpUtil.executeUrl(HttpMethod.GET, url, REQUEST_TIMEOUT_MS);
return deserializer.deserialize(RainForecast.class, answer);
} catch (MeteoFranceException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Exception deserializing API answer: %s".formatted(e.getMessage()));
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.handler;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Bridge;
/**
* {@link MeteoFranceChildHandler} is a common interface for Things having a
* {@link MeteoFranceBridgeHandler} Bridge
*
* @author Gaël L'hopital - Initial contribution
*
*/
@NonNullByDefault
public interface MeteoFranceChildHandler {
default Optional<MeteoFranceBridgeHandler> getBridgeHandler() {
Bridge bridge = getBridge();
if (bridge != null) {
if (bridge.getHandler() instanceof MeteoFranceBridgeHandler maHandler) {
return Optional.of(maHandler);
}
}
return Optional.empty();
}
@Nullable
Bridge getBridge();
}

View File

@ -0,0 +1,167 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.handler;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.*;
import static org.openhab.core.types.TimeSeries.Policy.REPLACE;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteofrance.internal.config.ForecastConfiguration;
import org.openhab.binding.meteofrance.internal.dto.RainForecast;
import org.openhab.binding.meteofrance.internal.dto.RainForecast.Forecast;
import org.openhab.binding.meteofrance.internal.dto.RainForecast.Properties;
import org.openhab.binding.meteofrance.internal.dto.RainForecast.RainIntensity;
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.thing.Bridge;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.TimeSeries;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link RainForecastHandler} is responsible for updating channels
* and querying the API
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class RainForecastHandler extends BaseThingHandler implements MeteoFranceChildHandler {
private final Logger logger = LoggerFactory.getLogger(RainForecastHandler.class);
private final ChannelUID intensityChannelUID;
private final ZoneId systemZoneId;
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
private Optional<PointType> location = Optional.empty();
public RainForecastHandler(Thing thing, ZoneId zoneId) {
super(thing);
this.intensityChannelUID = new ChannelUID(getThing().getUID(), INTENSITY);
this.systemZoneId = zoneId;
}
@Override
public @Nullable Bridge getBridge() {
return super.getBridge();
}
@Override
public void initialize() {
logger.debug("Initializing Rain Forecast handler.");
disposeJob();
updateStatus(ThingStatus.UNKNOWN);
ForecastConfiguration config = getConfigAs(ForecastConfiguration.class);
try {
this.location = Optional.of(new PointType(config.location));
} catch (IllegalArgumentException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Incorrect 'location' value.");
return;
}
this.refreshJob = Optional.of(scheduler.schedule(this::updateAndPublish, 2, TimeUnit.SECONDS));
}
private void disposeJob() {
refreshJob.ifPresent(job -> job.cancel(true));
refreshJob = Optional.empty();
}
@Override
public void dispose() {
logger.debug("Disposing the Rain Forecast handler.");
disposeJob();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateAndPublish();
}
}
private void updateAndPublish() {
getBridgeHandler().ifPresentOrElse(handler -> {
location.ifPresent(loc -> {
RainForecast forecast = handler.getRainForecast(loc);
if (forecast != null) {
setProperties(forecast.properties);
updateDate(UPDATE_TIME, forecast.updateTime);
updateStatus(ThingStatus.ONLINE);
}
});
}, () -> logger.warn("No viable bridge"));
}
private void setProperties(@Nullable Properties forecastProps) {
if (forecastProps == null) {
return;
}
Map<String, String> properties = editProperties();
properties.put("altitude", "%d m".formatted(forecastProps.altitude));
properties.put("name", forecastProps.name);
properties.put("country", forecastProps.country);
properties.put("department", forecastProps.frenchDepartment);
properties.put("timezone", forecastProps.timezone);
properties.put("comfidence", "%d".formatted(forecastProps.confidence));
this.updateProperties(properties);
setForecast(forecastProps.forecast);
}
private void setForecast(List<Forecast> forecast) {
TimeSeries timeSeries = new TimeSeries(REPLACE);
forecast.forEach(prevision -> {
State state = prevision.rainIntensity() != RainIntensity.UNKNOWN
? new DecimalType(prevision.rainIntensity().ordinal())
: UnDefType.UNDEF;
timeSeries.add(prevision.time().toInstant(), state);
});
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));
}
private void updateDate(String channelId, @Nullable ZonedDateTime zonedDateTime) {
if (isLinked(channelId)) {
updateState(channelId,
zonedDateTime != null ? new DateTimeType(zonedDateTime.withZoneSameInstant(systemZoneId))
: UnDefType.NULL);
}
}
}

View File

@ -0,0 +1,189 @@
/**
* Copyright (c) 2010-2024 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.meteofrance.internal.handler;
import static org.openhab.binding.meteofrance.internal.MeteoFranceBindingConstants.*;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteofrance.internal.MeteoFranceIconProvider;
import org.openhab.binding.meteofrance.internal.config.DepartmentConfiguration;
import org.openhab.binding.meteofrance.internal.dto.BlocType;
import org.openhab.binding.meteofrance.internal.dto.Domain;
import org.openhab.binding.meteofrance.internal.dto.Hazard;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.DomainId;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.Period;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.TextBlocItem;
import org.openhab.binding.meteofrance.internal.dto.MeteoFrance.TimelapsItem;
import org.openhab.binding.meteofrance.internal.dto.Risk;
import org.openhab.binding.meteofrance.internal.dto.Term;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link VigilanceHandler} is responsible for updating channels
* and querying the API
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class VigilanceHandler extends BaseThingHandler implements MeteoFranceChildHandler {
private final Logger logger = LoggerFactory.getLogger(VigilanceHandler.class);
private final MeteoFranceIconProvider iconProvider;
private final ZoneId systemZoneId;
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
private @NonNullByDefault({}) Domain domain;
public VigilanceHandler(Thing thing, ZoneId zoneId, MeteoFranceIconProvider iconProvider) {
super(thing);
this.iconProvider = iconProvider;
this.systemZoneId = zoneId;
}
@Override
public @Nullable Bridge getBridge() {
return super.getBridge();
}
@Override
public void initialize() {
logger.debug("Initializing Vigilance Météo handler.");
disposeJob();
DepartmentConfiguration config = getConfigAs(DepartmentConfiguration.class);
domain = Domain.getByApiId(config.department);
if (Domain.UNKNOWN.equals(domain)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Wrong department: %s".formatted((config.department)));
return;
}
logger.debug("config department= {}", config.department);
updateStatus(ThingStatus.UNKNOWN);
refreshJob = Optional
.of(scheduler.scheduleWithFixedDelay(this::updateAndPublish, 5, config.refresh * 60, TimeUnit.SECONDS));
}
private void disposeJob() {
refreshJob.ifPresent(job -> job.cancel(true));
refreshJob = Optional.empty();
}
@Override
public void dispose() {
logger.debug("Disposing the Vigilance Météo handler.");
disposeJob();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateAndPublish();
}
}
private void updateAndPublish() {
getBridgeHandler().ifPresentOrElse(handler -> {
Period period = handler.requestPeriod(Term.TODAY);
if (period != null) {
updateDate(OBSERVATION_TIME, period.beginValidityTime());
updateDate(END_TIME, period.endValidityTime());
}
DomainId domainId = handler.requestMapData(domain, Term.TODAY);
ZonedDateTime now = ZonedDateTime.now().withZoneSameInstant(systemZoneId);
if (domainId != null) {
Map<Hazard, Risk> channels = new HashMap<>(Hazard.values().length);
Hazard.AS_SET.stream().filter(Hazard::isChannel).forEach(hazard -> channels.put(hazard, Risk.VERT));
domainId.phenomenonItems().forEach(phenomenon -> {
Risk risk = phenomenon.phenomenonMaxColorId();
for (TimelapsItem item : phenomenon.timelapsItems()) {
if (item.contains(now)) {
risk = item.colorId();
}
}
channels.put(phenomenon.phenomenonId(), risk);
});
channels.forEach((k, v) -> updateAlert(k.channelName, v));
TextBlocItem bloc = handler.requestTextData(domain);
if (bloc != null) {
String comment = bloc.blocItems().stream().filter(bi -> bi.type().equals(BlocType.SITUATION))
.flatMap(bi -> bi.textItems().stream()).flatMap(ti -> ti.termItems().stream())
.map(term -> term.getText(now)).collect(Collectors.joining("."));
updateState(COMMENT, comment.isEmpty() ? UnDefType.NULL : StringType.valueOf(comment));
}
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "No data available for the department");
}
}, () -> logger.warn("No viable bridge"));
}
private void updateAlert(String channelId, Risk value) {
State state = value != Risk.UNKNOWN ? new DecimalType(value.riskLevel) : UnDefType.NULL;
if (isLinked(channelId)) {
updateState(channelId, state);
}
String channelIcon = channelId + "-icon";
if (isLinked(channelIcon)) {
InputStream icon = iconProvider.getIcon(channelId, state.toString());
if (icon != null) {
try {
State result = new RawType(icon.readAllBytes(), "image/svg+xml");
updateState(channelIcon, result);
} catch (IOException e) {
logger.warn("Error getting icon for channel {} and value {}: {}", channelId, value, e.getMessage());
}
} else {
logger.warn("Null icon returned for channel {} and state {}", channelIcon, state);
}
}
}
private void updateDate(String channelId, ZonedDateTime zonedDateTime) {
if (isLinked(channelId)) {
updateState(channelId, new DateTimeType(zonedDateTime.withZoneSameInstant(systemZoneId)));
}
}
}

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="meteoalerte" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<addon:addon id="meteofrance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>binding</type>
<name>Meteo Alerte Binding</name>
<description>Retrieves Meteo Alert levels in France</description>
<name>Météo France Binding</name>
<description>Retrieves various weather information from Météo France APIs</description>
<connection>cloud</connection>
<countries>fr</countries>

View File

@ -0,0 +1,211 @@
# add-on
addon.meteofrance.name = Météo France Binding
addon.meteofrance.description = Retrieves various weather information from Météo France APIs
# thing types
thing-type.meteofrance.api.label = Météo-France API Portal
thing-type.meteofrance.api.description = Bridge to the Météo-France API Portal. In order to receive the data, you must register an account on https://portail-api.meteofrance.fr/devportal/apis and get your API token.
thing-type.meteofrance.rain-forecast.label = Precipitation Forecast
thing-type.meteofrance.rain-forecast.description = Provides precipitation forecast for the next hour
thing-type.meteofrance.vigilance.label = Department Awareness
thing-type.meteofrance.vigilance.description = Provides weather alert levels for a department.
thing-type.meteofrance.vigilance.channel.avalanches-icon.label = Avalanche Icon
thing-type.meteofrance.vigilance.channel.canicule-icon.label = Heat Wave Icon
thing-type.meteofrance.vigilance.channel.end-time.label = End of Validity
thing-type.meteofrance.vigilance.channel.grand-froid-icon.label = Extreme Cold Icon
thing-type.meteofrance.vigilance.channel.inondation-icon.label = Flood Icon
thing-type.meteofrance.vigilance.channel.neige-icon.label = Snow Icon
thing-type.meteofrance.vigilance.channel.observation-time.label = Start of Validity
thing-type.meteofrance.vigilance.channel.orage-icon.label = Storm Icon
thing-type.meteofrance.vigilance.channel.pluie-inondation-icon.label = Rain Flood Icon
thing-type.meteofrance.vigilance.channel.vague-submersion-icon.label = Wave Submersion Icon
thing-type.meteofrance.vigilance.channel.vent-icon.label = Wind Icon
# thing types config
thing-type.config.meteofrance.api.apikey.label = API Key
thing-type.config.meteofrance.api.apikey.description = Data-platform token to access the service
thing-type.config.meteofrance.rain-forecast.location.label = Location
thing-type.config.meteofrance.rain-forecast.location.description = Your geo coordinates separated with comma (e.g. "37.8,-122.4,177").
thing-type.config.meteofrance.vigilance.department.label = Department
thing-type.config.meteofrance.vigilance.department.description = Name of the department
thing-type.config.meteofrance.vigilance.department.option.01 = Ain
thing-type.config.meteofrance.vigilance.department.option.02 = Aisne
thing-type.config.meteofrance.vigilance.department.option.03 = Allier
thing-type.config.meteofrance.vigilance.department.option.04 = Alpes de Haute Provence
thing-type.config.meteofrance.vigilance.department.option.06 = Alpes Maritimes
thing-type.config.meteofrance.vigilance.department.option.07 = Ardèche
thing-type.config.meteofrance.vigilance.department.option.08 = Ardennes
thing-type.config.meteofrance.vigilance.department.option.09 = Ariège
thing-type.config.meteofrance.vigilance.department.option.10 = Aube
thing-type.config.meteofrance.vigilance.department.option.11 = Aude
thing-type.config.meteofrance.vigilance.department.option.12 = Aveyron
thing-type.config.meteofrance.vigilance.department.option.67 = Bas-Rhin
thing-type.config.meteofrance.vigilance.department.option.13 = Bouches du Rhône
thing-type.config.meteofrance.vigilance.department.option.14 = Calvados
thing-type.config.meteofrance.vigilance.department.option.15 = Cantal
thing-type.config.meteofrance.vigilance.department.option.16 = Charente
thing-type.config.meteofrance.vigilance.department.option.17 = Charente Maritime
thing-type.config.meteofrance.vigilance.department.option.18 = Cher
thing-type.config.meteofrance.vigilance.department.option.19 = Corrèze
thing-type.config.meteofrance.vigilance.department.option.2A = Corse du Sud
thing-type.config.meteofrance.vigilance.department.option.21 = Côte D'Or
thing-type.config.meteofrance.vigilance.department.option.22 = Côtes D'Armor
thing-type.config.meteofrance.vigilance.department.option.23 = Creuse
thing-type.config.meteofrance.vigilance.department.option.79 = Deux Sèvres
thing-type.config.meteofrance.vigilance.department.option.24 = Dordogne
thing-type.config.meteofrance.vigilance.department.option.25 = Doubs
thing-type.config.meteofrance.vigilance.department.option.26 = Drôme
thing-type.config.meteofrance.vigilance.department.option.91 = Essonne
thing-type.config.meteofrance.vigilance.department.option.27 = Eure
thing-type.config.meteofrance.vigilance.department.option.28 = Eure et Loir
thing-type.config.meteofrance.vigilance.department.option.29 = Finistère
thing-type.config.meteofrance.vigilance.department.option.30 = Gard
thing-type.config.meteofrance.vigilance.department.option.32 = Gers
thing-type.config.meteofrance.vigilance.department.option.33 = Gironde
thing-type.config.meteofrance.vigilance.department.option.68 = Haut-Rhin
thing-type.config.meteofrance.vigilance.department.option.2B = Haute Corse
thing-type.config.meteofrance.vigilance.department.option.31 = Haute Garonne
thing-type.config.meteofrance.vigilance.department.option.43 = Haute Loire
thing-type.config.meteofrance.vigilance.department.option.52 = Haute Marne
thing-type.config.meteofrance.vigilance.department.option.70 = Haute Saône
thing-type.config.meteofrance.vigilance.department.option.74 = Haute Savoie
thing-type.config.meteofrance.vigilance.department.option.87 = Haute Vienne
thing-type.config.meteofrance.vigilance.department.option.05 = Hautes Alpes
thing-type.config.meteofrance.vigilance.department.option.65 = Hautes Pyrénées
thing-type.config.meteofrance.vigilance.department.option.92 = Hauts de Seine
thing-type.config.meteofrance.vigilance.department.option.34 = Hérault
thing-type.config.meteofrance.vigilance.department.option.35 = Ille et Vilaine
thing-type.config.meteofrance.vigilance.department.option.36 = Indre
thing-type.config.meteofrance.vigilance.department.option.37 = Indre et Loire
thing-type.config.meteofrance.vigilance.department.option.38 = Isère
thing-type.config.meteofrance.vigilance.department.option.39 = Jura
thing-type.config.meteofrance.vigilance.department.option.40 = Landes
thing-type.config.meteofrance.vigilance.department.option.41 = Loir et Cher
thing-type.config.meteofrance.vigilance.department.option.42 = Loire
thing-type.config.meteofrance.vigilance.department.option.44 = Loire Atlantique
thing-type.config.meteofrance.vigilance.department.option.45 = Loiret
thing-type.config.meteofrance.vigilance.department.option.46 = Lot
thing-type.config.meteofrance.vigilance.department.option.47 = Lot et Garonne
thing-type.config.meteofrance.vigilance.department.option.48 = Lozère
thing-type.config.meteofrance.vigilance.department.option.49 = Maine et Loire
thing-type.config.meteofrance.vigilance.department.option.50 = Manche
thing-type.config.meteofrance.vigilance.department.option.51 = Marne
thing-type.config.meteofrance.vigilance.department.option.53 = Mayenne
thing-type.config.meteofrance.vigilance.department.option.54 = Meurthe et Moselle
thing-type.config.meteofrance.vigilance.department.option.55 = Meuse
thing-type.config.meteofrance.vigilance.department.option.56 = Morbihan
thing-type.config.meteofrance.vigilance.department.option.57 = Moselle
thing-type.config.meteofrance.vigilance.department.option.58 = Nièvre
thing-type.config.meteofrance.vigilance.department.option.59 = Nord
thing-type.config.meteofrance.vigilance.department.option.60 = Oise
thing-type.config.meteofrance.vigilance.department.option.61 = Orne
thing-type.config.meteofrance.vigilance.department.option.75 = Paris
thing-type.config.meteofrance.vigilance.department.option.62 = Pas de Calais
thing-type.config.meteofrance.vigilance.department.option.63 = Puy de Dôme
thing-type.config.meteofrance.vigilance.department.option.64 = Pyrénées Atlantiques
thing-type.config.meteofrance.vigilance.department.option.66 = Pyrénées Orientales
thing-type.config.meteofrance.vigilance.department.option.69 = Rhône
thing-type.config.meteofrance.vigilance.department.option.71 = Saône et Loire
thing-type.config.meteofrance.vigilance.department.option.72 = Sarthe
thing-type.config.meteofrance.vigilance.department.option.73 = Savoie
thing-type.config.meteofrance.vigilance.department.option.77 = Seine et Marne
thing-type.config.meteofrance.vigilance.department.option.76 = Seine Maritime
thing-type.config.meteofrance.vigilance.department.option.93 = Seine Saint Denis
thing-type.config.meteofrance.vigilance.department.option.80 = Somme
thing-type.config.meteofrance.vigilance.department.option.81 = Tarn
thing-type.config.meteofrance.vigilance.department.option.82 = Tarn et Garonne
thing-type.config.meteofrance.vigilance.department.option.90 = Territoire de Belfort
thing-type.config.meteofrance.vigilance.department.option.95 = Val D'Oise
thing-type.config.meteofrance.vigilance.department.option.94 = Val de Marne
thing-type.config.meteofrance.vigilance.department.option.83 = Var
thing-type.config.meteofrance.vigilance.department.option.84 = Vaucluse
thing-type.config.meteofrance.vigilance.department.option.85 = Vendée
thing-type.config.meteofrance.vigilance.department.option.86 = Vienne
thing-type.config.meteofrance.vigilance.department.option.88 = Vosges
thing-type.config.meteofrance.vigilance.department.option.89 = Yonne
thing-type.config.meteofrance.vigilance.department.option.78 = Yvelines
thing-type.config.meteofrance.vigilance.refresh.label = Refresh Rate
thing-type.config.meteofrance.vigilance.refresh.description = Data refresh period in minutes.
# channel types
channel-type.meteofrance.avalanches.label = Avalanches
channel-type.meteofrance.avalanches.description = Avalanche alert level
channel-type.meteofrance.avalanches.state.option.0 = No special vigilance
channel-type.meteofrance.avalanches.state.option.1 = Be attentive
channel-type.meteofrance.avalanches.state.option.2 = Be very vigilant
channel-type.meteofrance.avalanches.state.option.3 = Absolute vigilance
channel-type.meteofrance.canicule.label = Heat Wave
channel-type.meteofrance.canicule.description = High temperature alert level
channel-type.meteofrance.canicule.state.option.0 = No special vigilance
channel-type.meteofrance.canicule.state.option.1 = Be attentive
channel-type.meteofrance.canicule.state.option.2 = Be very vigilant
channel-type.meteofrance.canicule.state.option.3 = Absolute vigilance
channel-type.meteofrance.comment.label = Comment
channel-type.meteofrance.condition-icon.label = Icon
channel-type.meteofrance.condition-icon.description = Pictogram associated with the alert level.
channel-type.meteofrance.grand-froid.label = Extreme Cold
channel-type.meteofrance.grand-froid.description = Negative temperature alert level
channel-type.meteofrance.grand-froid.state.option.0 = No special vigilance
channel-type.meteofrance.grand-froid.state.option.1 = Be attentive
channel-type.meteofrance.grand-froid.state.option.2 = Be very vigilant
channel-type.meteofrance.grand-froid.state.option.3 = Absolute vigilance
channel-type.meteofrance.inondation.label = Flood
channel-type.meteofrance.inondation.description = Flood alert level
channel-type.meteofrance.inondation.state.option.0 = No special vigilance
channel-type.meteofrance.inondation.state.option.1 = Be attentive
channel-type.meteofrance.inondation.state.option.2 = Be very vigilant
channel-type.meteofrance.inondation.state.option.3 = Absolute vigilance
channel-type.meteofrance.neige.label = Snow
channel-type.meteofrance.neige.description = Snow event alert level
channel-type.meteofrance.neige.state.option.0 = No special vigilance
channel-type.meteofrance.neige.state.option.1 = Be attentive
channel-type.meteofrance.neige.state.option.2 = Be very vigilant
channel-type.meteofrance.neige.state.option.3 = Absolute vigilance
channel-type.meteofrance.orage.label = Storm
channel-type.meteofrance.orage.description = Storm alert level
channel-type.meteofrance.orage.state.option.0 = No special vigilance
channel-type.meteofrance.orage.state.option.1 = Be attentive
channel-type.meteofrance.orage.state.option.2 = Be very vigilant
channel-type.meteofrance.orage.state.option.3 = Absolute vigilance
channel-type.meteofrance.pluie-inondation.label = Rain Flood
channel-type.meteofrance.pluie-inondation.description = Flood caused by rainfall alert level
channel-type.meteofrance.pluie-inondation.state.option.0 = No special vigilance
channel-type.meteofrance.pluie-inondation.state.option.1 = Be attentive
channel-type.meteofrance.pluie-inondation.state.option.2 = Be very vigilant
channel-type.meteofrance.pluie-inondation.state.option.3 = Absolute vigilance
channel-type.meteofrance.rain-intensity.label = Intensity
channel-type.meteofrance.rain-intensity.description = Rain intensity level
channel-type.meteofrance.rain-intensity.state.option.0 = Dry Weather
channel-type.meteofrance.rain-intensity.state.option.1 = Light Rain
channel-type.meteofrance.rain-intensity.state.option.2 = Moderate Rain
channel-type.meteofrance.rain-intensity.state.option.3 = Heavy Rain
channel-type.meteofrance.timestamp.label = Observation Timestamp
channel-type.meteofrance.vague-submersion.label = Wave Submersion
channel-type.meteofrance.vague-submersion.description = Submersion wave alert level
channel-type.meteofrance.vague-submersion.state.option.0 = No special vigilance
channel-type.meteofrance.vague-submersion.state.option.1 = Be attentive
channel-type.meteofrance.vague-submersion.state.option.2 = Be very vigilant
channel-type.meteofrance.vague-submersion.state.option.3 = Absolute vigilance
channel-type.meteofrance.vent.label = Wind
channel-type.meteofrance.vent.description = Wind event alert level
channel-type.meteofrance.vent.state.option.0 = No special vigilance
channel-type.meteofrance.vent.state.option.1 = Be attentive
channel-type.meteofrance.vent.state.option.2 = Be very vigilant
channel-type.meteofrance.vent.state.option.3 = Absolute vigilance
# iconprovider
iconset.label = Météo France Icons
iconset.description = Icons illustrating weather alerts provided by Météo France
# discovery result
discovery.meteofrance.rain-forecast.local.label = Prévisions de Précipitations
# thing status descriptions
offline.config-error-unknown-apikey = Parameter 'apikey' must be configured.

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="meteofrance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="api">
<label>Météo-France API Portal</label>
<description>
Bridge to the Météo-France API Portal. In order to receive the data, you must register an account on
https://portail-api.meteofrance.fr/devportal/apis and get your API token.
</description>
<config-description>
<parameter name="apikey" type="text" required="true">
<label>API Key</label>
<description>Data-platform token to access the service</description>
<context>password</context>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="meteofrance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="rain-intensity">
<item-type>Number</item-type>
<label>Intensity</label>
<description>Rain intensity level</description>
<category>oh:meteofrance:intensity</category>
<state readOnly="true">
<options>
<option value="0">Dry Weather</option>
<option value="1">Light Rain</option>
<option value="2">Moderate Rain</option>
<option value="3">Heavy Rain</option>
</options>
</state>
</channel-type>
<channel-type id="vent">
<item-type>Number</item-type>
<label>Wind</label>
<description>Wind event alert level</description>
<category>oh:meteofrance:vent</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="orage">
<item-type>Number</item-type>
<label>Storm</label>
<description>Storm alert level</description>
<category>oh:meteofrance:orage</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="inondation">
<item-type>Number</item-type>
<label>Flood</label>
<description>Flood alert level</description>
<category>oh:meteofrance:inondation</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="neige">
<item-type>Number</item-type>
<label>Snow</label>
<description>Snow event alert level</description>
<category>oh:meteofrance:neige</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="canicule">
<item-type>Number</item-type>
<label>Heat Wave</label>
<description>High temperature alert level</description>
<category>oh:meteofrance:canicule</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="grand-froid">
<item-type>Number</item-type>
<label>Extreme Cold</label>
<description>Negative temperature alert level</description>
<category>oh:meteofrance:grand-froid</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="avalanches">
<item-type>Number</item-type>
<label>Avalanches</label>
<description>Avalanche alert level</description>
<category>oh:meteofrance:avalanches</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="vague-submersion">
<item-type>Number</item-type>
<label>Wave Submersion</label>
<description>Submersion wave alert level</description>
<category>oh:meteofrance:vague-submersion</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="pluie-inondation">
<item-type>Number</item-type>
<label>Rain Flood</label>
<description>Flood caused by rainfall alert level</description>
<category>oh:meteofrance:pluie-inondation</category>
<state readOnly="true">
<options>
<option value="0">No special vigilance</option>
<option value="1">Be attentive</option>
<option value="2">Be very vigilant</option>
<option value="3">Absolute vigilance</option>
</options>
</state>
</channel-type>
<channel-type id="comment">
<item-type>String</item-type>
<label>Comment</label>
<category>text</category>
<state readOnly="true" pattern="%s"/>
</channel-type>
<channel-type id="timestamp" advanced="true">
<item-type>DateTime</item-type>
<label>Observation Timestamp</label>
<category>time</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="condition-icon">
<item-type>Image</item-type>
<label>Icon</label>
<description>Pictogram associated with the alert level.</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="meteofrance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="rain-forecast">
<supported-bridge-type-refs>
<bridge-type-ref id="api"/>
</supported-bridge-type-refs>
<label>Precipitation Forecast</label>
<description>Provides precipitation forecast for the next hour</description>
<channels>
<channel id="update-time" typeId="timestamp"/>
<channel id="intensity" typeId="rain-intensity"/>
</channels>
<representation-property>location</representation-property>
<config-description>
<parameter name="location" type="text" required="true">
<label>Location</label>
<context>location</context>
<description>Your geo coordinates separated with comma (e.g. "37.8,-122.4,177").</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="meteofrance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="vigilance">
<supported-bridge-type-refs>
<bridge-type-ref id="api"/>
</supported-bridge-type-refs>
<label>Department Awareness</label>
<description>Provides weather alert levels for a department.</description>
<channels>
<channel id="vague-submersion" typeId="vague-submersion"/>
<channel id="vent" typeId="vent"/>
<channel id="pluie-inondation" typeId="pluie-inondation"/>
<channel id="orage" typeId="orage"/>
<channel id="inondation" typeId="inondation"/>
<channel id="neige" typeId="neige"/>
<channel id="canicule" typeId="canicule"/>
<channel id="grand-froid" typeId="grand-froid"/>
<channel id="avalanches" typeId="avalanches"/>
<channel id="vague-submersion-icon" typeId="condition-icon">
<label>Wave Submersion Icon</label>
</channel>
<channel id="vent-icon" typeId="condition-icon">
<label>Wind Icon</label>
</channel>
<channel id="pluie-inondation-icon" typeId="condition-icon">
<label>Rain Flood Icon</label>
</channel>
<channel id="orage-icon" typeId="condition-icon">
<label>Storm Icon</label>
</channel>
<channel id="inondation-icon" typeId="condition-icon">
<label>Flood Icon</label>
</channel>
<channel id="neige-icon" typeId="condition-icon">
<label>Snow Icon</label>
</channel>
<channel id="canicule-icon" typeId="condition-icon">
<label>Heat Wave Icon</label>
</channel>
<channel id="grand-froid-icon" typeId="condition-icon">
<label>Extreme Cold Icon</label>
</channel>
<channel id="avalanches-icon" typeId="condition-icon">
<label>Avalanche Icon</label>
</channel>
<channel id="comment" typeId="comment"/>
<channel id="observation-time" typeId="timestamp">
<label>Start of Validity</label>
</channel>
<channel id="end-time" typeId="timestamp">
<label>End of Validity</label>
</channel>
</channels>
<representation-property>department</representation-property>
<config-description>
<parameter name="department" type="text" required="true">
<label>Department</label>
<description>Name of the department</description>
<options>
<option value="01">Ain</option>
<option value="02">Aisne</option>
<option value="03">Allier</option>
<option value="04">Alpes de Haute Provence</option>
<option value="06">Alpes Maritimes</option>
<option value="07">Ardèche</option>
<option value="08">Ardennes</option>
<option value="09">Ariège</option>
<option value="10">Aube</option>
<option value="11">Aude</option>
<option value="12">Aveyron</option>
<option value="67">Bas-Rhin</option>
<option value="13">Bouches du Rhône</option>
<option value="14">Calvados</option>
<option value="15">Cantal</option>
<option value="16">Charente</option>
<option value="17">Charente Maritime</option>
<option value="18">Cher</option>
<option value="19">Corrèze</option>
<option value="2A">Corse du Sud</option>
<option value="21">Côte D'Or</option>
<option value="22">Côtes D'Armor</option>
<option value="23">Creuse</option>
<option value="79">Deux Sèvres</option>
<option value="24">Dordogne</option>
<option value="25">Doubs</option>
<option value="26">Drôme</option>
<option value="91">Essonne</option>
<option value="27">Eure</option>
<option value="28">Eure et Loir</option>
<option value="29">Finistère</option>
<option value="30">Gard</option>
<option value="32">Gers</option>
<option value="33">Gironde</option>
<option value="68">Haut-Rhin</option>
<option value="2B">Haute Corse</option>
<option value="31">Haute Garonne</option>
<option value="43">Haute Loire</option>
<option value="52">Haute Marne</option>
<option value="70">Haute Saône</option>
<option value="74">Haute Savoie</option>
<option value="87">Haute Vienne</option>
<option value="05">Hautes Alpes</option>
<option value="65">Hautes Pyrénées</option>
<option value="92">Hauts de Seine</option>
<option value="34">Hérault</option>
<option value="35">Ille et Vilaine</option>
<option value="36">Indre</option>
<option value="37">Indre et Loire</option>
<option value="38">Isère</option>
<option value="39">Jura</option>
<option value="40">Landes</option>
<option value="41">Loir et Cher</option>
<option value="42">Loire</option>
<option value="44">Loire Atlantique</option>
<option value="45">Loiret</option>
<option value="46">Lot</option>
<option value="47">Lot et Garonne</option>
<option value="48">Lozère</option>
<option value="49">Maine et Loire</option>
<option value="50">Manche</option>
<option value="51">Marne</option>
<option value="53">Mayenne</option>
<option value="54">Meurthe et Moselle</option>
<option value="55">Meuse</option>
<option value="56">Morbihan</option>
<option value="57">Moselle</option>
<option value="58">Nièvre</option>
<option value="59">Nord</option>
<option value="60">Oise</option>
<option value="61">Orne</option>
<option value="75">Paris</option>
<option value="62">Pas de Calais</option>
<option value="63">Puy de Dôme</option>
<option value="64">Pyrénées Atlantiques</option>
<option value="66">Pyrénées Orientales</option>
<option value="69">Rhône</option>
<option value="71">Saône et Loire</option>
<option value="72">Sarthe</option>
<option value="73">Savoie</option>
<option value="77">Seine et Marne</option>
<option value="76">Seine Maritime</option>
<option value="93">Seine Saint Denis</option>
<option value="80">Somme</option>
<option value="81">Tarn</option>
<option value="82">Tarn et Garonne</option>
<option value="90">Territoire de Belfort</option>
<option value="95">Val D'Oise</option>
<option value="94">Val de Marne</option>
<option value="83">Var</option>
<option value="84">Vaucluse</option>
<option value="85">Vendée</option>
<option value="86">Vienne</option>
<option value="88">Vosges</option>
<option value="89">Yonne</option>
<option value="78">Yvelines</option>
</options>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="refresh" type="integer" min="1" required="true" unit="min">
<label>Refresh Rate</label>
<description>Data refresh period in minutes.</description>
<default>60</default>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,770 @@
[
{
"id": "01",
"name": "Ain",
"northest_lat": 46.517199374492,
"southest_lat": 45.611235124481,
"eastest_lon": 6.1697363568789,
"westest_lon": 4.729097034294
},
{
"id": "02",
"name": "Aisne",
"northest_lat": 50.069271974234,
"southest_lat": 48.837795469902,
"eastest_lon": 4.2540638814402,
"westest_lon": 2.9624508927558
},
{
"id": "03",
"name": "Allier",
"northest_lat": 46.803872076205,
"southest_lat": 45.930727869968,
"eastest_lon": 4.0055701432229,
"westest_lon": 2.2804029533754
},
{
"id": "04",
"name": "Alpes-de-Haute-Provence",
"northest_lat": 44.659501345682,
"southest_lat": 43.668282105491,
"eastest_lon": 6.9668199032047,
"westest_lon": 5.4980104773442
},
{
"id": "05",
"name": "Hautes-Alpes",
"northest_lat": 45.12684420383,
"southest_lat": 44.186478874057,
"eastest_lon": 7.0771048243018,
"westest_lon": 5.4185330627929
},
{
"id": "06",
"name": "Alpes-Maritimes",
"northest_lat": 44.361051257141,
"southest_lat": 43.480065494401,
"eastest_lon": 7.7169378581589,
"westest_lon": 6.6363906079569
},
{
"id": "07",
"name": "Ardèche",
"northest_lat": 45.365674921417,
"southest_lat": 44.264783733394,
"eastest_lon": 4.8865943991285,
"westest_lon": 3.8615128126047
},
{
"id": "08",
"name": "Ardennes",
"northest_lat": 50.168317417174,
"southest_lat": 49.228510768771,
"eastest_lon": 5.3935393812988,
"westest_lon": 4.0252899216328
},
{
"id": "09",
"name": "Ariège",
"northest_lat": 43.315601482419,
"southest_lat": 42.572397819345,
"eastest_lon": 2.1758766074869,
"westest_lon": 0.82612266137771
},
{
"id": "10",
"name": "Aube",
"northest_lat": 48.716672523486,
"southest_lat": 47.924112631788,
"eastest_lon": 4.863174195777,
"westest_lon": 3.3883584814447
},
{
"id": "11",
"name": "Aude",
"northest_lat": 43.459927734352,
"southest_lat": 42.64890098195,
"eastest_lon": 3.2405623482295,
"westest_lon": 1.6884233932357
},
{
"id": "12",
"name": "Aveyron",
"northest_lat": 44.941219492647,
"southest_lat": 43.692056102371,
"eastest_lon": 3.4507554815828,
"westest_lon": 1.8396044963184
},
{
"id": "13",
"name": "Bouches-du-Rhône",
"northest_lat": 43.92406219253,
"southest_lat": 43.162545513212,
"eastest_lon": 5.8132476569219,
"westest_lon": 4.2302808850321
},
{
"id": "14",
"name": "Calvados",
"northest_lat": 49.429859840723,
"southest_lat": 48.752223582274,
"eastest_lon": 0.44627431224134,
"westest_lon": -1.1595014604014
},
{
"id": "15",
"name": "Cantal",
"northest_lat": 45.480702895801,
"southest_lat": 44.61552895784,
"eastest_lon": 3.3699091459722,
"westest_lon": 2.0629079799591
},
{
"id": "16",
"name": "Charente",
"northest_lat": 46.139591492141,
"southest_lat": 45.191628193392,
"eastest_lon": 0.9456207917489,
"westest_lon": -0.46177341555077
},
{
"id": "17",
"name": "Charente-Maritime",
"northest_lat": 46.371051399922,
"southest_lat": 45.088810634907,
"eastest_lon": 0.005823224821197,
"westest_lon": -1.5614800452621
},
{
"id": "18",
"name": "Cher",
"northest_lat": 47.628965936616,
"southest_lat": 46.420403547753,
"eastest_lon": 3.0793324170792,
"westest_lon": 1.7745852665449
},
{
"id": "19",
"name": "Corrèze",
"northest_lat": 45.763895555756,
"southest_lat": 44.923721627249,
"eastest_lon": 2.5283596411119,
"westest_lon": 1.2271245972559
},
{
"id": "21",
"name": "Côte-d'Or",
"northest_lat": 48.030241950581,
"southest_lat": 46.900857518168,
"eastest_lon": 5.5185372800929,
"westest_lon": 4.0660574486622
},
{
"id": "22",
"name": "Côtes-d'Armor",
"northest_lat": 48.867411825697,
"southest_lat": 48.035478415172,
"eastest_lon": -1.9089921410274,
"westest_lon": -3.663669588163
},
{
"id": "23",
"name": "Creuse",
"northest_lat": 46.45481310551,
"southest_lat": 45.664008285608,
"eastest_lon": 2.6107853057918,
"westest_lon": 1.3748978470741
},
{
"id": "24",
"name": "Dordogne",
"northest_lat": 45.714569962764,
"southest_lat": 44.57129825478,
"eastest_lon": 1.4482602497483,
"westest_lon": -0.041998525054412
},
{
"id": "25",
"name": "Doubs",
"northest_lat": 47.579897594928,
"southest_lat": 46.553996454277,
"eastest_lon": 7.0622006908671,
"westest_lon": 5.6987272452696
},
{
"id": "26",
"name": "Drôme",
"northest_lat": 45.344042230781,
"southest_lat": 44.115716677493,
"eastest_lon": 5.8294720463131,
"westest_lon": 4.6477668446587
},
{
"id": "27",
"name": "Eure",
"northest_lat": 49.485111873749,
"southest_lat": 48.666521479531,
"eastest_lon": 1.8026740663848,
"westest_lon": 0.29722451460974
},
{
"id": "28",
"name": "Eure-et-Loir",
"northest_lat": 48.941051842112,
"southest_lat": 47.953852019595,
"eastest_lon": 1.9940901445311,
"westest_lon": 0.76023175104941
},
{
"id": "29",
"name": "Finistère",
"northest_lat": 48.75230874743,
"southest_lat": 47.762638930067,
"eastest_lon": -3.3880788564101,
"westest_lon": -5.138001239929
},
{
"id": "30",
"name": "Gard",
"northest_lat": 44.459798467391,
"southest_lat": 43.460183661653,
"eastest_lon": 4.8455501032842,
"westest_lon": 3.2628340569911
},
{
"id": "31",
"name": "Haute-Garonne",
"northest_lat": 43.920240096152,
"southest_lat": 42.68989270234,
"eastest_lon": 2.0478554672695,
"westest_lon": 0.44199364903152
},
{
"id": "32",
"name": "Gers",
"northest_lat": 44.078224550392,
"southest_lat": 43.310884111508,
"eastest_lon": 1.2013345895525,
"westest_lon": -0.28211623210758
},
{
"id": "33",
"name": "Gironde",
"northest_lat": 45.574691325999,
"southest_lat": 44.193811119459,
"eastest_lon": 0.31506020240148,
"westest_lon": -1.2617334302552
},
{
"id": "34",
"name": "Hérault",
"northest_lat": 43.969527164685,
"southest_lat": 43.212804132866,
"eastest_lon": 4.1944474773799,
"westest_lon": 2.5399656073586
},
{
"id": "35",
"name": "Ille-et-Vilaine",
"northest_lat": 48.704854504824,
"southest_lat": 47.631356309182,
"eastest_lon": -1.0168893967587,
"westest_lon": -2.289084836122
},
{
"id": "36",
"name": "Indre",
"northest_lat": 47.276819032313,
"southest_lat": 46.347214822447,
"eastest_lon": 2.2043920861378,
"westest_lon": 0.86746898682573
},
{
"id": "37",
"name": "Indre-et-Loire",
"northest_lat": 47.709346222795,
"southest_lat": 46.737086924375,
"eastest_lon": 1.3653663291974,
"westest_lon": 0.053277684947378
},
{
"id": "38",
"name": "Isère",
"northest_lat": 45.883269928025,
"southest_lat": 44.696067584965,
"eastest_lon": 6.3588423781754,
"westest_lon": 4.7441167394752
},
{
"id": "39",
"name": "Jura",
"northest_lat": 47.305475313171,
"southest_lat": 46.260731935709,
"eastest_lon": 6.2033299339615,
"westest_lon": 5.2548827302617
},
{
"id": "40",
"name": "Landes",
"northest_lat": 44.532195517275,
"southest_lat": 43.487949116697,
"eastest_lon": 0.13672631290526,
"westest_lon": -1.524870110434
},
{
"id": "41",
"name": "Loir-et-Cher",
"northest_lat": 48.132548568904,
"southest_lat": 47.18622172903,
"eastest_lon": 2.2478931361182,
"westest_lon": 0.58052041667909
},
{
"id": "42",
"name": "Loire",
"northest_lat": 46.275936357353,
"southest_lat": 45.232177394436,
"eastest_lon": 4.7604638818845,
"westest_lon": 3.6906909501902
},
{
"id": "43",
"name": "Haute-Loire",
"northest_lat": 45.427582294546,
"southest_lat": 44.743866105932,
"eastest_lon": 4.489606977621,
"westest_lon": 3.0822533822787
},
{
"id": "44",
"name": "Loire-Atlantique",
"northest_lat": 47.833557723029,
"southest_lat": 46.860078088448,
"eastest_lon": -0.94643916329696,
"westest_lon": -2.5589448655806
},
{
"id": "45",
"name": "Loiret",
"northest_lat": 48.344598562828,
"southest_lat": 47.482968445146,
"eastest_lon": 3.1284487900515,
"westest_lon": 1.5129691249084
},
{
"id": "46",
"name": "Lot",
"northest_lat": 45.046275852749,
"southest_lat": 44.204018679795,
"eastest_lon": 2.2108934010391,
"westest_lon": 0.98177646477517
},
{
"id": "47",
"name": "Lot-et-Garonne",
"northest_lat": 44.764390535693,
"southest_lat": 43.973860568873,
"eastest_lon": 1.0779367166615,
"westest_lon": -0.14068987994571
},
{
"id": "48",
"name": "Lozère",
"northest_lat": 44.971408091786,
"southest_lat": 44.113818175271,
"eastest_lon": 3.9981617468281,
"westest_lon": 2.981675726654
},
{
"id": "49",
"name": "Maine-et-Loire",
"northest_lat": 47.809992506553,
"southest_lat": 46.969397597368,
"eastest_lon": 0.23453049018557,
"westest_lon": -1.3541992398083
},
{
"id": "50",
"name": "Manche",
"northest_lat": 49.725557927402,
"southest_lat": 48.458282754255,
"eastest_lon": -0.73732101904671,
"westest_lon": -1.9472733176655
},
{
"id": "51",
"name": "Marne",
"northest_lat": 49.406179032892,
"southest_lat": 48.516108006569,
"eastest_lon": 5.0379027924329,
"westest_lon": 3.398657955437
},
{
"id": "52",
"name": "Haute-Marne",
"northest_lat": 48.688711618117,
"southest_lat": 47.576950536437,
"eastest_lon": 5.8908642780035,
"westest_lon": 4.6268310932286
},
{
"id": "53",
"name": "Mayenne",
"northest_lat": 48.567994064435,
"southest_lat": 47.733379704738,
"eastest_lon": -0.049909790963035,
"westest_lon": -1.238247803597
},
{
"id": "54",
"name": "Meurthe-et-Moselle",
"northest_lat": 49.562644003065,
"southest_lat": 48.349889737943,
"eastest_lon": 7.1231636635608,
"westest_lon": 5.429907860027
},
{
"id": "55",
"name": "Meuse",
"northest_lat": 49.617086785829,
"southest_lat": 48.410687855212,
"eastest_lon": 5.8541770017029,
"westest_lon": 4.8885820531146
},
{
"id": "56",
"name": "Morbihan",
"northest_lat": 48.210884763611,
"southest_lat": 47.283069445657,
"eastest_lon": -2.0357552590146,
"westest_lon": -3.7321436369252
},
{
"id": "57",
"name": "Moselle",
"northest_lat": 49.510019040716,
"southest_lat": 48.52694525177,
"eastest_lon": 7.6352815933424,
"westest_lon": 5.8934039932125
},
{
"id": "58",
"name": "Nièvre",
"northest_lat": 47.587958865747,
"southest_lat": 46.651760006926,
"eastest_lon": 4.2306617272065,
"westest_lon": 2.8451871650071
},
{
"id": "59",
"name": "Nord",
"northest_lat": 51.08854370897,
"southest_lat": 49.969186662527,
"eastest_lon": 4.2279959931456,
"westest_lon": 2.0677049871716
},
{
"id": "60",
"name": "Oise",
"northest_lat": 49.758309270134,
"southest_lat": 49.060452516659,
"eastest_lon": 3.162641421643,
"westest_lon": 1.6895744511517
},
{
"id": "61",
"name": "Orne",
"northest_lat": 48.972557592954,
"southest_lat": 48.181599665308,
"eastest_lon": 0.9762713097259,
"westest_lon": -0.86036021134895
},
{
"id": "62",
"name": "Pas-de-Calais",
"northest_lat": 51.006501514321,
"southest_lat": 50.020975633738,
"eastest_lon": 3.1883563131291,
"westest_lon": 1.5577948179294
},
{
"id": "63",
"name": "Puy-de-Dôme",
"northest_lat": 46.255486133208,
"southest_lat": 45.287121578381,
"eastest_lon": 3.9844000097893,
"westest_lon": 2.388014020679
},
{
"id": "64",
"name": "Pyrénées-Atlantiques",
"northest_lat": 43.596401195938,
"southest_lat": 42.777515930774,
"eastest_lon": 0.02629551293813,
"westest_lon": -1.7908870919282
},
{
"id": "65",
"name": "Hautes-Pyrénées",
"northest_lat": 43.609311711216,
"southest_lat": 42.674921018438,
"eastest_lon": 0.64553925526757,
"westest_lon": -0.3270823405503
},
{
"id": "66",
"name": "Pyrénées-Orientales",
"northest_lat": 42.918339638726,
"southest_lat": 42.33364688988,
"eastest_lon": 3.1747892794105,
"westest_lon": 1.7256472450279
},
{
"id": "67",
"name": "Bas-Rhin",
"northest_lat": 49.077884925649,
"southest_lat": 48.120371112534,
"eastest_lon": 8.2303986615424,
"westest_lon": 6.9403717864006
},
{
"id": "68",
"name": "Haut-Rhin",
"northest_lat": 48.310471263573,
"southest_lat": 47.422198455938,
"eastest_lon": 7.6220859200517,
"westest_lon": 6.8428287756472
},
{
"id": "69",
"name": "Rhône",
"northest_lat": 46.303994122044,
"southest_lat": 45.45503324486,
"eastest_lon": 5.1592030475156,
"westest_lon": 4.243469905983
},
{
"id": "70",
"name": "Haute-Saône",
"northest_lat": 48.023714993889,
"southest_lat": 47.253139353829,
"eastest_lon": 6.8235333222471,
"westest_lon": 5.3727580571009
},
{
"id": "71",
"name": "Saône-et-Loire",
"northest_lat": 47.155410810959,
"southest_lat": 46.156946471815,
"eastest_lon": 5.4622983197648,
"westest_lon": 3.6225898833129
},
{
"id": "72",
"name": "Sarthe",
"northest_lat": 48.482954540393,
"southest_lat": 47.569104805534,
"eastest_lon": 0.91379809767445,
"westest_lon": -0.44786007819229
},
{
"id": "73",
"name": "Savoie",
"northest_lat": 45.93845768164,
"southest_lat": 45.051837207667,
"eastest_lon": 7.1842712160815,
"westest_lon": 5.6230208703548
},
{
"id": "74",
"name": "Haute-Savoie",
"northest_lat": 46.408081546332,
"southest_lat": 45.682198985672,
"eastest_lon": 7.0438913499404,
"westest_lon": 5.8074048290847
},
{
"id": "75",
"name": "Paris",
"northest_lat": 48.902007785215,
"southest_lat": 48.816314210034,
"eastest_lon": 2.4675819883673,
"westest_lon": 2.2242191058804
},
{
"id": "76",
"name": "Seine-Maritime",
"northest_lat": 50.070851596042,
"southest_lat": 49.252262168305,
"eastest_lon": 1.79022549105,
"westest_lon": 0.065609431053556
},
{
"id": "77",
"name": "Seine-et-Marne",
"northest_lat": 49.11755332218,
"southest_lat": 48.122204261229,
"eastest_lon": 3.555613758785,
"westest_lon": 2.3931765378081
},
{
"id": "78",
"name": "Yvelines",
"northest_lat": 49.08303659502,
"southest_lat": 48.440146719021,
"eastest_lon": 2.2265538842831,
"westest_lon": 1.4472851104304
},
{
"id": "79",
"name": "Deux-Sèvres",
"northest_lat": 47.108333434925,
"southest_lat": 45.969661749473,
"eastest_lon": 0.22035828616308,
"westest_lon": -0.89196408624284
},
{
"id": "80",
"name": "Somme",
"northest_lat": 50.366290636763,
"southest_lat": 49.571762327,
"eastest_lon": 3.2030417908111,
"westest_lon": 1.3796981484469
},
{
"id": "81",
"name": "Tarn",
"northest_lat": 44.200834436147,
"southest_lat": 43.383508824887,
"eastest_lon": 2.93545676901,
"westest_lon": 1.5439759556659
},
{
"id": "82",
"name": "Tarn-et-Garonne",
"northest_lat": 44.393331095059,
"southest_lat": 43.770780304363,
"eastest_lon": 1.9963637896774,
"westest_lon": 0.73810974125492
},
{
"id": "83",
"name": "Var",
"northest_lat": 43.806770970879,
"southest_lat": 42.982043008785,
"eastest_lon": 6.9337211159516,
"westest_lon": 5.6559638228901
},
{
"id": "84",
"name": "Vaucluse",
"northest_lat": 44.431367227183,
"southest_lat": 43.658685905188,
"eastest_lon": 5.7573377215236,
"westest_lon": 4.649227423465
},
{
"id": "85",
"name": "Vendée",
"northest_lat": 47.083893903306,
"southest_lat": 46.266566974958,
"eastest_lon": -0.53779518169029,
"westest_lon": -2.3987614706025
},
{
"id": "86",
"name": "Vienne",
"northest_lat": 47.175754285742,
"southest_lat": 46.049008552161,
"eastest_lon": 1.2126877519811,
"westest_lon": -0.1021158452812
},
{
"id": "87",
"name": "Haute-Vienne",
"northest_lat": 46.401546863371,
"southest_lat": 45.437356928582,
"eastest_lon": 1.9094484810021,
"westest_lon": 0.62974117909144
},
{
"id": "88",
"name": "Vosges",
"northest_lat": 48.513587820739,
"southest_lat": 47.813051201983,
"eastest_lon": 7.1982872111206,
"westest_lon": 5.3944755711529
},
{
"id": "89",
"name": "Yonne",
"northest_lat": 48.39970411104,
"southest_lat": 47.312769315752,
"eastest_lon": 4.3403007872795,
"westest_lon": 2.8487899744432
},
{
"id": "90",
"name": "Territoire de Belfort",
"northest_lat": 47.824784354742,
"southest_lat": 47.433371743667,
"eastest_lon": 7.1398015507652,
"westest_lon": 6.7576409592057
},
{
"id": "91",
"name": "Essonne",
"northest_lat": 48.776101996393,
"southest_lat": 48.284688606385,
"eastest_lon": 2.5853737107586,
"westest_lon": 1.9149199821626
},
{
"id": "92",
"name": "Hauts-de-Seine",
"northest_lat": 48.950965864655,
"southest_lat": 48.72948996497,
"eastest_lon": 2.3363529889891,
"westest_lon": 2.1458760215967
},
{
"id": "93",
"name": "Seine-Saint-Denis",
"northest_lat": 49.012397786393,
"southest_lat": 48.807437551952,
"eastest_lon": 2.6025997962059,
"westest_lon": 2.2882536989787
},
{
"id": "94",
"name": "Val-de-Marne",
"northest_lat": 48.861405371284,
"southest_lat": 48.688326690701,
"eastest_lon": 2.6136517425679,
"westest_lon": 2.3102224901101
},
{
"id": "95",
"name": "Val-d'Oise",
"northest_lat": 49.232197221792,
"southest_lat": 48.908679329899,
"eastest_lon": 2.5905283926735,
"westest_lon": 1.608798807603
},
{
"id": "2A",
"name": "Corse-du-Sud",
"northest_lat": 42.381404942274,
"southest_lat": 41.362164776515,
"eastest_lon": 9.4073217319481,
"westest_lon": 8.5401025407511
},
{
"id": "2B",
"name": "Haute-Corse",
"northest_lat": 43.011724041684,
"southest_lat": 41.832143660252,
"eastest_lon": 9.5592262719626,
"westest_lon": 8.5734085639674
}
]

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30"/>
<g transform="matrix(0.095654, 0, 0, 0.103801, 7.65905, -0.7645)" style="">
<g>
<path style="fill:#9B5C5C;" d="M277.259,58.637c-11.695-20.344-30.835-20.344-42.53,0L4.978,432.587 c-11.695,20.342-2.07,36.966,21.398,36.966h459.235c23.468,0,33.093-16.624,21.405-36.966L277.259,58.637z"/>
<path style="fill:#9B5C5C;" d="M277.259,57.683c-11.695-20.328-30.835-20.328-42.53,0l-23.538,59.732l-32.538,31.78L4.978,432.587 c-11.695,20.342-2.07,36.966,21.398,36.966h459.235c23.468,0,33.093-16.624,21.405-36.966l-56.124-91.592l-84.06-104.052 L277.259,57.683z"/>
</g>
<g>
<path style="fill:#CBD0D8;" d="M426.629,226.272c-23.562,0-42.671,19.108-42.671,42.671c0,23.554,19.109,42.646,42.671,42.646 c23.545,0,42.654-19.093,42.654-42.646C469.283,245.381,450.174,226.272,426.629,226.272L426.629,226.272z"/>
<path style="fill:#CBD0D8;" d="M394.63,226.272c0,5.89-4.766,10.671-10.672,10.671c-5.875,0-10.656-4.781-10.656-10.671 c0-5.875,4.781-10.656,10.656-10.656C389.864,215.616,394.63,220.397,394.63,226.272z"/>
<path style="fill:#CBD0D8;" d="M426.629,204.944c0,5.891-4.781,10.672-10.671,10.672c-5.891,0-10.656-4.781-10.656-10.672 s4.766-10.655,10.656-10.655C421.848,194.289,426.629,199.054,426.629,204.944z"/>
<path style="fill:#CBD0D8;" d="M383.958,183.617c0,5.891-4.766,10.672-10.656,10.672s-10.656-4.781-10.656-10.672 s4.766-10.656,10.656-10.656S383.958,177.726,383.958,183.617z"/>
<path style="fill:#CBD0D8;" d="M351.974,151.634c0,5.891-4.766,10.655-10.672,10.655c-5.874,0-10.655-4.765-10.655-10.655 s4.781-10.671,10.655-10.671C347.209,140.963,351.974,145.744,351.974,151.634z"/>
</g>
<path style="fill:#E6E9ED;" d="M 273.368 152.384 C 274.649 151.868 275.993 151.634 277.321 151.634 C 280.992 151.634 284.554 153.54 286.523 156.884 C 286.523 156.884 355.865 257.194 381.786 300.341 C 407.708 343.495 450.892 341.027 450.892 341.027 L 277.259 57.683 C 265.564 37.355 246.424 37.355 234.729 57.683 L 178.653 149.196 C 178.653 149.196 210.879 171.539 225.503 171.539 C 240.126 171.539 273.368 152.384 273.368 152.384 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30" />
<g fill="#dae1e4">
<circle cx="32" cy="55" r="9" />
<circle cx="32" cy="6" r="6" />
</g>
<circle cx="32" cy="6" r="5" fill="#d20014" />
<path d="m26 6h12v50h-12z" fill="#dae1e4" />
<g fill="#d20014">
<path d="m27 6h10v48.94h-10z" />
<circle cx="32" cy="55" r="8" />
</g>
<g fill="#fff">
<path d="m33 9h5v2h-5z" />
<path d="m33 17h5v2h-5z" />
<path d="m33 25h5v2h-5z" />
<path d="m33 33h5v2h-5z" />
<path d="m33 41h5v2h-5z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 759 B

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30" />
<circle cx="32" cy="6" r="6" fill="#dae1e4" />
<circle cx="32" cy="6" r="5" fill="#c4cacc" />
<path d="m26 6h12v50h-12z" fill="#dae1e4" />
<path d="m27 6h10v48.94h-10z" fill="#c4cacc" />
<circle cx="32" cy="55" r="9" fill="#dae1e4" />
<g fill="#2e7de5">
<circle cx="32" cy="55" r="8" />
<path d="m27 39h10v12h-10z" />
</g>
<g fill="#fff">
<path d="m33 9h5v2h-5z" />
<path d="m33 17h5v2h-5z" />
<path d="m33 25h5v2h-5z" />
<path d="m33 33h5v2h-5z" />
<path d="m33 41h5v2h-5z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 802 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30"/>
<path fill="#F8EFE0" d="M 11.018 52.475 L 11.018 31.819 C 7.317 33.065 4.779 29.687 7.284 26.959 C 7.325 26.916 7.371 26.873 7.425 26.837 L 30.938 10.768 C 31.242 10.522 31.712 10.497 32.05 10.727 L 55.617 26.768 C 55.654 26.793 55.688 26.819 55.72 26.851 C 56.812 27.879 57.084 29.019 56.82 29.983 C 56.692 30.449 56.434 30.88 56.071 31.238 C 55.708 31.596 55.249 31.867 54.735 32.029 C 53.911 32.288 52.931 32.281 51.937 31.869 L 51.937 51.358 C 43.944 54.785 42.3 46.755 42.28 46.918 C 41.461 53.613 28.847 53.227 26.809 48.767 C 26.439 47.958 26.67 47.804 26.141 47.766 C 23.045 47.544 22.868 55.347 11.018 52.475 Z M 51.987 12.422 L 51.987 20.469 L 44.911 16.382 L 44.911 12.422 L 51.987 12.422 Z" style=""/>
<path fill="#D93133" d="M 11.018 31.819 C 10.054 32.144 9.151 32.151 8.401 31.927 C 7.816 31.754 7.322 31.444 6.959 31.043 C 6.596 30.643 6.366 30.156 6.295 29.618 C 6.188 28.785 6.472 27.843 7.284 26.959 C 7.325 26.916 7.371 26.873 7.425 26.837 L 30.938 10.768 C 31.242 10.522 31.712 10.497 32.05 10.727 L 55.617 26.768 C 55.654 26.793 55.688 26.819 55.72 26.851 C 56.812 27.879 57.084 29.019 56.82 29.983 C 56.692 30.449 56.434 30.88 56.071 31.238 C 55.708 31.596 55.249 31.867 54.735 32.029 C 53.911 32.288 52.931 32.281 51.937 31.869 L 49.63 30.982 C 49.63 30.618 33.464 19.43 31.679 18.213 C 29.788 19.473 13.326 30.571 13.326 31.04 L 11.018 31.819 Z M 51.987 12.422 L 51.987 20.469 L 44.911 16.382 L 44.911 12.422 L 51.987 12.422 Z" style=""/>
<path fill="#313A58" d="M 31.595 25.496 C 33.228 25.496 34.711 26.081 35.784 27.021 C 36.861 27.963 37.526 29.076 37.526 30.503 L 37.526 35.325 L 25.662 35.325 L 25.662 30.503 C 25.662 29.073 26.33 27.961 27.404 27.021 C 28.479 26.081 29.963 25.496 31.595 25.496 Z" style=""/>
<path fill="#ABB9BC" d="M 32.111 31.421 L 32.111 34.426 L 36.496 34.426 L 36.496 31.421 L 32.111 31.421 Z M 32.111 26.424 L 32.111 30.517 L 36.496 30.517 L 36.496 30.505 C 36.496 29.326 35.945 28.439 35.056 27.661 C 34.282 26.983 33.252 26.529 32.111 26.424 Z M 31.079 30.517 L 31.079 26.424 C 29.936 26.531 28.909 26.983 28.134 27.661 C 27.245 28.439 26.694 29.326 26.694 30.505 L 26.694 30.517 L 31.079 30.517 Z M 31.079 34.423 L 31.079 31.421 L 26.694 31.421 L 26.694 34.426 L 31.079 34.426 L 31.079 34.423 Z" style=""/>
<path fill="#313A58" d="M 51.92 31.732 L 49.64 30.959 L 49.64 52.389 L 51.92 52.389 L 51.92 31.732 Z M 10.908 31.819 L 13.189 31.046 L 13.189 52.476 L 10.908 52.476 L 10.908 31.819 Z" style=""/>
<path d="M 11.097 51.603 C 13.269 52.586 15.988 52.276 18.451 51.315 C 21.367 50.176 23.866 48.168 24.668 46.318 C 24.9 45.781 25.509 45.54 26.029 45.779 C 26.259 45.885 26.444 46.075 26.547 46.312 C 27.432 48.152 28.886 49.487 30.554 50.246 C 31.623 50.733 32.775 50.983 33.918 50.978 C 35.051 50.973 36.18 50.717 37.211 50.195 C 38.688 49.446 39.974 48.13 40.802 46.195 C 41.031 45.656 41.641 45.412 42.163 45.649 C 42.416 45.764 42.614 45.978 42.711 46.245 C 43.106 47.121 43.54 47.869 44.009 48.496 C 45.11 49.969 46.402 50.756 47.805 50.981 C 49.259 51.215 50.874 50.868 52.56 50.07 C 53.763 49.501 55 48.701 56.239 47.714 L 57.499 49.397 C 56.142 50.478 54.77 51.363 53.416 52.004 C 51.373 52.971 49.365 53.383 47.49 53.082 C 45.567 52.773 43.824 51.731 42.377 49.796 C 42.148 49.488 41.933 49.17 41.734 48.842 C 40.756 50.32 39.505 51.399 38.116 52.103 C 36.799 52.772 35.361 53.098 33.918 53.104 C 32.484 53.109 31.046 52.799 29.722 52.197 C 28.122 51.468 26.677 50.311 25.588 48.764 C 24.183 50.57 21.811 52.279 19.178 53.307 C 16.265 54.444 12.989 54.782 10.273 53.553 L 11.097 51.603 Z" style="fill: rgb(30, 130, 255);"/>
<path d="M 10.92 57.018 C 13.092 58.001 15.811 57.691 18.274 56.73 C 21.19 55.591 23.689 53.583 24.491 51.733 C 24.723 51.196 25.332 50.955 25.852 51.194 C 26.082 51.3 26.267 51.49 26.37 51.727 C 27.255 53.567 28.709 54.902 30.377 55.661 C 31.446 56.148 32.598 56.398 33.741 56.393 C 34.874 56.388 36.003 56.132 37.034 55.61 C 38.511 54.861 39.797 53.545 40.625 51.61 C 40.854 51.071 41.464 50.827 41.986 51.064 C 42.239 51.179 42.437 51.393 42.534 51.66 C 42.929 52.536 43.363 53.284 43.832 53.911 C 44.933 55.384 46.225 56.171 47.628 56.396 C 49.082 56.63 50.697 56.283 52.383 55.485 C 53.586 54.916 54.823 54.116 56.062 53.129 L 57.322 54.812 C 55.965 55.893 54.593 56.778 53.239 57.419 C 51.196 58.386 49.188 58.798 47.313 58.497 C 45.39 58.188 43.647 57.146 42.2 55.211 C 41.971 54.903 41.756 54.585 41.557 54.257 C 40.579 55.735 39.328 56.814 37.939 57.518 C 36.622 58.187 35.184 58.513 33.741 58.519 C 32.307 58.524 30.869 58.214 29.545 57.612 C 27.945 56.883 26.5 55.726 25.411 54.179 C 24.006 55.985 21.634 57.694 19.001 58.722 C 16.088 59.859 12.812 60.197 10.096 58.968 L 10.92 57.018 Z" style="fill: rgb(30, 130, 255);"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,81 @@
<svg width="55" height="63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__k">
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
width="27.056" height="36.375">
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
<use xlink:href="#prefix__c" />
</mask>
<mask id="prefix__l" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="67"
height="4" fill="#fff">
<use xlink:href="#prefix__d" />
</mask>
<path
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
id="prefix__a" />
<path
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
id="prefix__b" />
<path
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(2 2)">
<use fill="#000" filter="url(#prefix__e)" xlink:href="#prefix__a" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__a" />
<use stroke="#FFF" mask="url(#prefix__f)" stroke-width="4" xlink:href="#prefix__a" />
</g>
<g transform="translate(2 2)">
<use fill="#000" filter="url(#prefix__g)" xlink:href="#prefix__b" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__b" />
<use stroke="#FFF" mask="url(#prefix__h)" stroke-width="2.16" xlink:href="#prefix__b" />
</g>
<g transform="translate(2 2)">
<use fill="#000" filter="url(#prefix__i)" xlink:href="#prefix__c" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__c" />
<use stroke="#FFF" mask="url(#prefix__j)" stroke-width="2.88" xlink:href="#prefix__c" />
</g>
<g transform="rotate(-45 28.81 28.981)">
<use fill="#000" filter="url(#prefix__k)" xlink:href="#prefix__d" />
<use stroke="#FFF" mask="url(#prefix__l)" stroke-width="2" fill="#464646" xlink:href="#prefix__d" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,66 @@
<svg width="46" height="63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__d">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__f">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__h">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<path
d="M18.528 23C16.887 23 7 38.241 7 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C30.056 38.158 20.169 23 18.528 23z"
id="prefix__a" />
<path
d="M6.125 5C5.253 5 0 13.238 0 16.785 0 19.582 1.587 22.5 6.125 22.5c4.155 0 6.125-2.89 6.125-5.715C12.25 13.193 6.997 5 6.125 5z"
id="prefix__b" />
<path
d="M31.313 0C30.128 0 23 11.122 23 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<mask id="prefix__e" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
width="27.056" height="36.375">
<path fill="#fff" d="M5 21h27.056v36.375H5z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__g" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M-1.08 3.92h14.41v19.66H-1.08z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__i" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M21.56-1.44h19.505v26.505H21.56z" />
<use xlink:href="#prefix__c" />
</mask>
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__d)" xlink:href="#prefix__a" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__a" />
<use stroke="#FFF" mask="url(#prefix__e)" stroke-width="4" xlink:href="#prefix__a" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__f)" xlink:href="#prefix__b" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__b" />
<use stroke="#FFF" mask="url(#prefix__g)" stroke-width="2.16" xlink:href="#prefix__b" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__h)" xlink:href="#prefix__c" />
<use fill="#55A4FF" xlink:href="#prefix__c" />
<use stroke="#FFF" mask="url(#prefix__i)" stroke-width="2.88" xlink:href="#prefix__c" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,66 @@
<svg width="46" height="63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__d">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__f">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__h">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<path
d="M18.528 23C16.887 23 7 38.241 7 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C30.056 38.158 20.169 23 18.528 23z"
id="prefix__a" />
<path
d="M6.125 5C5.253 5 0 13.238 0 16.785 0 19.582 1.587 22.5 6.125 22.5c4.155 0 6.125-2.89 6.125-5.715C12.25 13.193 6.997 5 6.125 5z"
id="prefix__b" />
<path
d="M31.313 0C30.128 0 23 11.122 23 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<mask id="prefix__e" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
width="27.056" height="36.375">
<path fill="#fff" d="M5 21h27.056v36.375H5z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__g" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M-1.08 3.92h14.41v19.66H-1.08z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__i" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M21.56-1.44h19.505v26.505H21.56z" />
<use xlink:href="#prefix__c" />
</mask>
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__d)" xlink:href="#prefix__a" />
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__a" />
<use stroke="#FFF" mask="url(#prefix__e)" stroke-width="4" xlink:href="#prefix__a" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__f)" xlink:href="#prefix__b" />
<use fill="#55A4FF" xlink:href="#prefix__b" />
<use stroke="#FFF" mask="url(#prefix__g)" stroke-width="2.16" xlink:href="#prefix__b" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__h)" xlink:href="#prefix__c" />
<use fill="#55A4FF" xlink:href="#prefix__c" />
<use stroke="#FFF" mask="url(#prefix__i)" stroke-width="2.88" xlink:href="#prefix__c" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,66 @@
<svg width="46" height="63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__d">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__f">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__h">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<path
d="M18.528 23C16.887 23 7 38.241 7 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C30.056 38.158 20.169 23 18.528 23z"
id="prefix__a" />
<path
d="M6.125 5C5.253 5 0 13.238 0 16.785 0 19.582 1.587 22.5 6.125 22.5c4.155 0 6.125-2.89 6.125-5.715C12.25 13.193 6.997 5 6.125 5z"
id="prefix__b" />
<path
d="M31.313 0C30.128 0 23 11.122 23 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<mask id="prefix__e" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
width="27.056" height="36.375">
<path fill="#fff" d="M5 21h27.056v36.375H5z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__g" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M-1.08 3.92h14.41v19.66H-1.08z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__i" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M21.56-1.44h19.505v26.505H21.56z" />
<use xlink:href="#prefix__c" />
</mask>
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__d)" xlink:href="#prefix__a" />
<use fill="#55A4FF" xlink:href="#prefix__a" />
<use stroke="#FFF" mask="url(#prefix__e)" stroke-width="4" xlink:href="#prefix__a" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__f)" xlink:href="#prefix__b" />
<use fill="#55A4FF" xlink:href="#prefix__b" />
<use stroke="#FFF" mask="url(#prefix__g)" stroke-width="2.16" xlink:href="#prefix__b" />
</g>
<g transform="translate(3 2)">
<use fill="#000" filter="url(#prefix__h)" xlink:href="#prefix__c" />
<use fill="#55A4FF" xlink:href="#prefix__c" />
<use stroke="#FFF" mask="url(#prefix__i)" stroke-width="2.88" xlink:href="#prefix__c" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,67 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="55" height="63">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
</filter>
<path
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
id="prefix__a" />
<path
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
id="prefix__b" />
<path
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
id="prefix__c" />
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2" width="27.056"
height="36.375">
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
<use xlink:href="#prefix__a" />
</mask>
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
width="14.41" height="19.66">
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
<use xlink:href="#prefix__b" />
</mask>
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
width="19.505" height="26.505">
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
<use xlink:href="#prefix__c" />
</mask>
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
</defs>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#000" filter="url(#prefix__e)" />
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__a" mask="url(#prefix__f)" stroke="#fff" stroke-width="4" />
</g>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#000" filter="url(#prefix__g)" />
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__b" mask="url(#prefix__h)" stroke="#fff"
stroke-width="2.16" />
</g>
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#000" filter="url(#prefix__i)" />
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#55a4ff" fill-opacity=".3" />
<use height="100%" width="100%" xlink:href="#prefix__c" mask="url(#prefix__j)" stroke="#fff"
stroke-width="2.88" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" height="600" width="600" data-name="Calque 1">
<path fill="#005892" d="M0 0h600v600H0z" />
<rect x="220.481" y="111.57" width="159.038" height="159.038" rx="12.759" ry="12.759" fill="#fff" />
<path fill="#005891" d="M334.557 134.05l17.013 17.697-24.152 6.152z" />
<path
d="M357.95 191.089a57.646 57.646 0 00-10.178-32.81l-5.164 1.974a56.05 56.05 0 0110.253 30.836 53.165 53.165 0 01-45.57 52.329 210.152 210.152 0 01-1.747-104.81 55.443 55.443 0 0119.747 6l1.291-4.861a57.57 57.57 0 00-19.747-6.152 150.684 150.684 0 018.127-22.025h-7.595a164.05 164.05 0 00-7.595 21.645 57.95 57.95 0 000 115.823h1.975c1.975 7.063 4.253 14.278 7.063 21.646h7.063c-2.886-7.595-5.392-15.19-7.595-22.254a57.95 57.95 0 0049.671-57.341z"
fill="#005891" />
<path d="M298.633 130.405h-7.063" fill="#fff" />
<g fill="#fff">
<path
d="M123.418 315.342h22.025l14.582 44.126 14.05-44.126h22.102l7.975 76.48H182.05l-3.266-38.278-11.317 38.279h-15.873l-11.317-38.279-4.48 38.279H114zM268.405 372.608h-34.709v-10.254h26.279V346.33H233.24v-11.544h35.164v-19.443h-56.278v76.48h56.278zM403.975 372.608H369.19v-10.254h26.278V346.33h-26.734v-11.544h35.24v-19.443H347.62v76.48h56.355zM297.038 391.823h21.19v-57.038h18.76v-19.443h-59.165v19.443h19.215zM448.101 314.658a37.975 37.975 0 1037.975 37.975 37.975 37.975 0 00-37.975-37.975zm0 57.494a19.443 19.443 0 1119.443-19.443 19.443 19.443 0 01-19.443 19.443zM134.962 464.127h23.696v-14.81h-23.696v-14.963h30.684v-18.607H114v72.607h20.962zM213.57 458.81s10.936-1.215 10.936-21.19a19.519 19.519 0 00-18.835-21.873h-34.86v72.607h17.695V461.62h6.532l11.165 26.734h20.886zm-11.848-11.544h-13.52v-17.469h13.52a9.494 9.494 0 010 17.469zM274.025 415.747h-16.253l-27.418 72.607h20.735l5.392-13.519h19.975l4.557 13.52h19.822zm-15.19 45.114l6.988-19.52 6.987 19.52zM321.494 449.316l26.05 39.038h16.785v-72.607h-18.911v42.456l-24.152-42.456h-18.38v72.607h18.608zM418.253 465.038a18.38 18.38 0 11-.987-26.43l11.848-13.292a36.076 36.076 0 100 53.773zM486 470.127h-33.038v-9.722h24.987V445.14h-25.443v-10.936H486V415.67h-53.544v72.76H486z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30" />
<path
d="m32 2.5c.98 0 1.76.79 1.76 1.76v8.25l2.82-4.09c.35-.5.9-.77 1.47-.76.34 0 .68.11.98.31.8.55 1.01 1.65.45 2.45l-5.73 8.31v3.56 6.66l8.85-5.11 4.33-9.11c.42-.88 1.47-1.25 2.35-.84.33.16.59.4.76.7.29.49.33 1.1.07 1.65l-2.13 4.49 7.15-4.13c.85-.49 1.92-.2 2.41.65.49.85.2 1.92-.65 2.41l-7.15 4.13 4.96.4c.61.05 1.12.4 1.4.89.17.3.25.64.22 1.01-.08.97-.92 1.69-1.9 1.62l-10.06-.8-8.84 5.09 5.77 3.33 3.08 1.78 10.06-.8c.97-.08 1.82.64 1.9 1.62.03.36-.05.71-.22 1.01-.28.49-.79.84-1.4.89l-4.96.4 7.15 4.13c.85.49 1.13 1.56.65 2.41-.49.85-1.56 1.13-2.41.64l-7.15-4.13 2.13 4.49c.26.55.22 1.17-.07 1.65-.17.29-.43.54-.76.69-.88.42-1.93.05-2.35-.83l-4.33-9.11-8.85-5.11v6.66 3.56l5.73 8.31c.55.8.35 1.9-.45 2.45-.3.21-.64.31-.98.31-.57.01-1.12-.26-1.47-.76l-2.82-4.09v8.25c0 .98-.79 1.76-1.76 1.76-.98 0-1.76-.79-1.76-1.76v-8.25l-2.82 4.09c-.35.5-.9.77-1.47.76-.34 0-.68-.11-.98-.31-.8-.55-1.01-1.65-.45-2.45l5.73-8.31v-3.56-6.66l-5.77 3.33-3.08 1.78-4.33 9.11c-.42.88-1.47 1.25-2.35.83-.33-.16-.59-.4-.76-.69-.29-.49-.33-1.1-.07-1.65l2.14-4.49-7.15 4.13c-.85.49-1.92.2-2.41-.64-.49-.85-.2-1.92.64-2.41l7.15-4.13-4.96-.4c-.61-.05-1.12-.4-1.4-.89-.17-.3-.25-.64-.22-1.01.08-.97.92-1.69 1.9-1.62l10.06.8 3.08-1.78 5.75-3.33-8.85-5.11-10.06.8c-.97.08-1.82-.64-1.9-1.62-.03-.36.05-.71.22-1.01.28-.49.79-.84 1.4-.89l4.96-.4-7.15-4.11c-.85-.49-1.13-1.56-.64-2.41.49-.85 1.56-1.13 2.41-.65l7.15 4.13-2.14-4.49c-.26-.55-.22-1.17.07-1.65.17-.29.43-.54.76-.7.88-.42 1.93-.05 2.35.84l4.33 9.11 8.85 5.11v-6.66-3.56l-5.73-8.31c-.55-.8-.35-1.9.45-2.45.3-.21.64-.31.98-.31.57-.01 1.12.26 1.47.76l2.82 4.09v-8.25c.01-.97.79-1.76 1.77-1.76"
fill="#dae1e4" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30"/>
<g transform="matrix(0.109429, 0, 0, 0.095795, 3.683853, 5.87504)" style="">
<polygon style="fill:#FFDA44;" points="225.584 244.867 174.304 380.884 268.543 380.884 209.837 509.421 367.316 333.616 271.782 333.616 303.985 244.867"/>
<polygon style="fill:#CCA400;" points="304.519 244.867 226.118 244.867 207.482 294.3 286.581 294.3"/>
<path style="fill:#B8D6FD;" d="M 459.559 266.555 C 488.522 266.555 512.001 243.077 512.001 214.113 C 512.001 185.149 488.523 161.671 459.559 161.671 C 450.829 161.671 442.605 163.816 435.364 167.589 C 428.157 153.316 416.099 141.917 401.341 135.574 C 401.341 135.443 401.351 135.316 401.351 135.185 C 401.351 62.632 342.535 3.817 269.982 3.817 C 209.899 3.817 159.273 44.169 143.634 99.239 C 109.041 105.309 81.724 132.452 75.398 166.956 C 68.464 163.574 60.675 161.671 52.44 161.671 C 23.479 161.672 0 185.15 0 214.114 C 0 243.078 23.479 266.556 52.442 266.556 L 459.559 266.556 L 459.559 266.555 Z"/>
<path style="opacity:0.41;fill:#6AB3EA;enable-background:new ;" d="M 458.404 160.434 C 449.674 160.434 441.45 162.579 434.209 166.352 C 427.002 152.079 414.944 140.68 400.186 134.337 C 400.186 134.206 400.196 134.079 400.196 133.948 C 400.196 61.395 341.38 2.58 268.827 2.58 C 257.171 2.58 245.875 4.105 235.117 6.955 C 291.333 21.84 332.772 73.052 332.772 133.95 C 332.772 134.081 332.763 134.208 332.762 134.339 C 347.519 140.681 359.578 152.081 366.785 166.354 C 374.026 162.58 382.25 160.436 390.98 160.436 C 419.943 160.436 443.422 183.914 443.422 212.878 C 443.422 241.841 419.944 265.32 390.98 265.32 L 458.404 265.32 C 487.367 265.32 510.846 241.841 510.846 212.878 C 510.846 183.912 487.367 160.434 458.404 160.434 Z"/>
<g style="opacity:0.41;">
<path style="fill:#6AB3EA;" d="M258.252,34.812c0,3.364-2.216,6.456-5.612,7.421c-40.109,11.389-68.121,48.482-68.121,90.201 c0,4.264-3.455,7.718-7.718,7.718s-7.718-3.455-7.718-7.718c0-48.587,32.626-91.785,79.341-105.052 c4.1-1.165,8.369,1.216,9.534,5.316C258.157,33.404,258.252,34.114,258.252,34.812z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30"/>
<path fill="#F8EFE0" d="M 11.018 52.475 L 11.018 31.819 C 7.317 33.065 4.779 29.687 7.284 26.959 C 7.325 26.916 7.371 26.873 7.425 26.837 L 30.938 10.768 C 31.242 10.522 31.712 10.497 32.05 10.727 L 55.617 26.768 C 55.654 26.793 55.688 26.819 55.72 26.851 C 56.812 27.879 57.084 29.019 56.82 29.983 C 56.692 30.449 56.434 30.88 56.071 31.238 C 55.708 31.596 55.249 31.867 54.735 32.029 C 53.911 32.288 52.931 32.281 51.937 31.869 L 51.937 51.358 C 43.944 54.785 42.3 46.755 42.28 46.918 C 41.461 53.613 28.847 53.227 26.809 48.767 C 26.439 47.958 26.67 47.804 26.141 47.766 C 23.045 47.544 22.868 55.347 11.018 52.475 Z M 51.987 12.422 L 51.987 20.469 L 44.911 16.382 L 44.911 12.422 L 51.987 12.422 Z" style=""/>
<path fill="#D93133" d="M 11.018 31.819 C 10.054 32.144 9.151 32.151 8.401 31.927 C 7.816 31.754 7.322 31.444 6.959 31.043 C 6.596 30.643 6.366 30.156 6.295 29.618 C 6.188 28.785 6.472 27.843 7.284 26.959 C 7.325 26.916 7.371 26.873 7.425 26.837 L 30.938 10.768 C 31.242 10.522 31.712 10.497 32.05 10.727 L 55.617 26.768 C 55.654 26.793 55.688 26.819 55.72 26.851 C 56.812 27.879 57.084 29.019 56.82 29.983 C 56.692 30.449 56.434 30.88 56.071 31.238 C 55.708 31.596 55.249 31.867 54.735 32.029 C 53.911 32.288 52.931 32.281 51.937 31.869 L 49.63 30.982 C 49.63 30.618 33.464 19.43 31.679 18.213 C 29.788 19.473 13.326 30.571 13.326 31.04 L 11.018 31.819 Z M 51.987 12.422 L 51.987 20.469 L 44.911 16.382 L 44.911 12.422 L 51.987 12.422 Z" style=""/>
<path fill="#313A58" d="M 31.595 25.496 C 33.228 25.496 34.711 26.081 35.784 27.021 C 36.861 27.963 37.526 29.076 37.526 30.503 L 37.526 35.325 L 25.662 35.325 L 25.662 30.503 C 25.662 29.073 26.33 27.961 27.404 27.021 C 28.479 26.081 29.963 25.496 31.595 25.496 Z" style=""/>
<path fill="#ABB9BC" d="M 32.111 31.421 L 32.111 34.426 L 36.496 34.426 L 36.496 31.421 L 32.111 31.421 Z M 32.111 26.424 L 32.111 30.517 L 36.496 30.517 L 36.496 30.505 C 36.496 29.326 35.945 28.439 35.056 27.661 C 34.282 26.983 33.252 26.529 32.111 26.424 Z M 31.079 30.517 L 31.079 26.424 C 29.936 26.531 28.909 26.983 28.134 27.661 C 27.245 28.439 26.694 29.326 26.694 30.505 L 26.694 30.517 L 31.079 30.517 Z M 31.079 34.423 L 31.079 31.421 L 26.694 31.421 L 26.694 34.426 L 31.079 34.426 L 31.079 34.423 Z" style=""/>
<path fill="#313A58" d="M 51.92 31.732 L 49.64 30.959 L 49.64 52.389 L 51.92 52.389 L 51.92 31.732 Z M 10.908 31.819 L 13.189 31.046 L 13.189 52.476 L 10.908 52.476 L 10.908 31.819 Z" style=""/>
<path d="M 11.097 51.603 C 13.269 52.586 15.988 52.276 18.451 51.315 C 21.367 50.176 23.866 48.168 24.668 46.318 C 24.9 45.781 25.509 45.54 26.029 45.779 C 26.259 45.885 26.444 46.075 26.547 46.312 C 27.432 48.152 28.886 49.487 30.554 50.246 C 31.623 50.733 32.775 50.983 33.918 50.978 C 35.051 50.973 36.18 50.717 37.211 50.195 C 38.688 49.446 39.974 48.13 40.802 46.195 C 41.031 45.656 41.641 45.412 42.163 45.649 C 42.416 45.764 42.614 45.978 42.711 46.245 C 43.106 47.121 43.54 47.869 44.009 48.496 C 45.11 49.969 46.402 50.756 47.805 50.981 C 49.259 51.215 50.874 50.868 52.56 50.07 C 53.763 49.501 55 48.701 56.239 47.714 L 57.499 49.397 C 56.142 50.478 54.77 51.363 53.416 52.004 C 51.373 52.971 49.365 53.383 47.49 53.082 C 45.567 52.773 43.824 51.731 42.377 49.796 C 42.148 49.488 41.933 49.17 41.734 48.842 C 40.756 50.32 39.505 51.399 38.116 52.103 C 36.799 52.772 35.361 53.098 33.918 53.104 C 32.484 53.109 31.046 52.799 29.722 52.197 C 28.122 51.468 26.677 50.311 25.588 48.764 C 24.183 50.57 21.811 52.279 19.178 53.307 C 16.265 54.444 12.989 54.782 10.273 53.553 L 11.097 51.603 Z" style="fill: rgb(30, 130, 255);"/>
<path d="M 8.218 34.7 C 8.218 34.7 8.173 34.7 8.13 34.7 C 8.041 34.7 7.997 34.7 7.953 34.64 L 7.908 34.64 C 6.888 34.399 6.046 33.017 5.958 31.515 L 5.958 31.034 C 6.003 30.794 6.046 30.494 6.091 30.192 C 6.18 30.013 6.268 29.772 6.357 29.532 L 6.667 28.93 C 7.376 27.789 7.953 26.648 8.351 25.445 C 8.794 26.888 9.504 28.15 10.08 29.111 C 10.124 29.231 10.213 29.411 10.346 29.592 C 10.434 29.832 10.567 30.133 10.701 30.373 C 10.745 30.553 10.789 30.854 10.834 31.094 L 10.834 31.455 C 10.834 31.515 10.834 31.575 10.834 31.635 C 10.834 31.635 10.834 31.695 10.834 31.695 L 10.834 31.756 L 10.834 31.815 C 10.701 33.138 9.903 34.399 8.928 34.64 L 8.883 34.64 C 8.839 34.64 8.794 34.7 8.706 34.7 C 8.661 34.7 8.618 34.7 8.618 34.7 L 8.218 34.7" fill="#1e82ff" style=""/>
<path d="M 17.152 21.924 C 17.152 21.924 17.107 21.924 17.064 21.924 C 16.975 21.924 16.931 21.924 16.887 21.864 L 16.842 21.864 C 15.822 21.623 14.98 20.241 14.892 18.739 L 14.892 18.258 C 14.937 18.018 14.98 17.718 15.025 17.416 C 15.114 17.237 15.202 16.996 15.291 16.756 L 15.601 16.154 C 16.31 15.013 16.887 13.872 17.285 12.669 C 17.728 14.112 18.438 15.374 19.014 16.335 C 19.058 16.455 19.147 16.635 19.28 16.816 C 19.368 17.056 19.501 17.357 19.635 17.597 C 19.679 17.777 19.723 18.078 19.768 18.318 L 19.768 18.679 C 19.768 18.739 19.768 18.799 19.768 18.859 C 19.768 18.859 19.768 18.919 19.768 18.919 L 19.768 18.98 L 19.768 19.039 C 19.635 20.362 18.837 21.623 17.862 21.864 L 17.817 21.864 C 17.773 21.864 17.728 21.924 17.64 21.924 C 17.595 21.924 17.552 21.924 17.552 21.924 L 17.152 21.924" fill="#1e82ff" style=""/>
<path d="M 42.51 27.949 C 42.51 27.949 42.465 27.949 42.422 27.949 C 42.333 27.949 42.289 27.949 42.245 27.889 L 42.2 27.889 C 41.18 27.648 40.338 26.266 40.25 24.764 L 40.25 24.283 C 40.295 24.043 40.338 23.743 40.383 23.441 C 40.472 23.262 40.56 23.021 40.649 22.781 L 40.959 22.179 C 41.668 21.038 42.245 19.897 42.643 18.694 C 43.086 20.137 43.796 21.399 44.372 22.36 C 44.416 22.48 44.505 22.66 44.638 22.841 C 44.726 23.081 44.859 23.382 44.993 23.622 C 45.037 23.802 45.081 24.103 45.126 24.343 L 45.126 24.704 C 45.126 24.764 45.126 24.824 45.126 24.884 C 45.126 24.884 45.126 24.944 45.126 24.944 L 45.126 25.005 L 45.126 25.064 C 44.993 26.387 44.195 27.648 43.22 27.889 L 43.175 27.889 C 43.131 27.889 43.086 27.949 42.998 27.949 C 42.953 27.949 42.91 27.949 42.91 27.949 L 42.51 27.949" fill="#1e82ff" style=""/>
<path d="M 30.168 12.518 C 30.168 12.518 30.123 12.518 30.08 12.518 C 29.991 12.518 29.947 12.518 29.903 12.458 L 29.858 12.458 C 28.838 12.217 27.996 10.835 27.908 9.333 L 27.908 8.852 C 27.953 8.612 27.996 8.312 28.041 8.01 C 28.13 7.831 28.218 7.59 28.307 7.35 L 28.617 6.748 C 29.326 5.607 29.903 4.466 30.301 3.263 C 30.744 4.706 31.454 5.968 32.03 6.929 C 32.074 7.049 32.163 7.229 32.296 7.41 C 32.384 7.65 32.517 7.951 32.651 8.191 C 32.695 8.371 32.739 8.672 32.784 8.912 L 32.784 9.273 C 32.784 9.333 32.784 9.393 32.784 9.453 C 32.784 9.453 32.784 9.513 32.784 9.513 L 32.784 9.574 L 32.784 9.633 C 32.651 10.956 31.853 12.217 30.878 12.458 L 30.833 12.458 C 30.789 12.458 30.744 12.518 30.656 12.518 C 30.611 12.518 30.568 12.518 30.568 12.518 L 30.168 12.518" fill="#1e82ff" style=""/>
<path d="M 54.328 36.78 C 54.328 36.78 54.283 36.78 54.24 36.78 C 54.151 36.78 54.107 36.78 54.063 36.72 L 54.018 36.72 C 52.998 36.479 52.156 35.097 52.068 33.595 L 52.068 33.114 C 52.113 32.874 52.156 32.574 52.201 32.272 C 52.29 32.093 52.378 31.852 52.467 31.612 L 52.777 31.01 C 53.486 29.869 54.063 28.728 54.461 27.525 C 54.904 28.968 55.614 30.23 56.19 31.191 C 56.234 31.311 56.323 31.491 56.456 31.672 C 56.544 31.912 56.677 32.213 56.811 32.453 C 56.855 32.633 56.899 32.934 56.944 33.174 L 56.944 33.535 C 56.944 33.595 56.944 33.655 56.944 33.715 C 56.944 33.715 56.944 33.775 56.944 33.775 L 56.944 33.836 L 56.944 33.895 C 56.811 35.218 56.013 36.479 55.038 36.72 L 54.993 36.72 C 54.949 36.72 54.904 36.78 54.816 36.78 C 54.771 36.78 54.728 36.78 54.728 36.78 L 54.328 36.78" fill="#1e82ff" style=""/>
<path d="M 10.92 57.018 C 13.092 58.001 15.811 57.691 18.274 56.73 C 21.19 55.591 23.689 53.583 24.491 51.733 C 24.723 51.196 25.332 50.955 25.852 51.194 C 26.082 51.3 26.267 51.49 26.37 51.727 C 27.255 53.567 28.709 54.902 30.377 55.661 C 31.446 56.148 32.598 56.398 33.741 56.393 C 34.874 56.388 36.003 56.132 37.034 55.61 C 38.511 54.861 39.797 53.545 40.625 51.61 C 40.854 51.071 41.464 50.827 41.986 51.064 C 42.239 51.179 42.437 51.393 42.534 51.66 C 42.929 52.536 43.363 53.284 43.832 53.911 C 44.933 55.384 46.225 56.171 47.628 56.396 C 49.082 56.63 50.697 56.283 52.383 55.485 C 53.586 54.916 54.823 54.116 56.062 53.129 L 57.322 54.812 C 55.965 55.893 54.593 56.778 53.239 57.419 C 51.196 58.386 49.188 58.798 47.313 58.497 C 45.39 58.188 43.647 57.146 42.2 55.211 C 41.971 54.903 41.756 54.585 41.557 54.257 C 40.579 55.735 39.328 56.814 37.939 57.518 C 36.622 58.187 35.184 58.513 33.741 58.519 C 32.307 58.524 30.869 58.214 29.545 57.612 C 27.945 56.883 26.5 55.726 25.411 54.179 C 24.006 55.985 21.634 57.694 19.001 58.722 C 16.088 59.859 12.812 60.197 10.096 58.968 L 10.92 57.018 Z" style="fill: rgb(30, 130, 255);"/>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30"/>
<path d="M 10.382 37.307 C 12.554 38.29 15.273 37.98 17.736 37.019 C 20.652 35.88 23.151 33.872 23.953 32.022 C 24.185 31.485 24.794 31.244 25.314 31.483 C 25.544 31.589 25.729 31.779 25.832 32.016 C 26.717 33.856 28.171 35.191 29.839 35.95 C 30.908 36.437 32.06 36.687 33.203 36.682 C 34.336 36.677 35.465 36.421 36.496 35.899 C 37.973 35.15 39.259 33.834 40.087 31.899 C 40.316 31.36 40.926 31.116 41.448 31.353 C 41.701 31.468 41.899 31.682 41.996 31.949 C 42.391 32.825 42.825 33.573 43.294 34.2 C 44.395 35.673 45.687 36.46 47.09 36.685 C 48.544 36.919 50.159 36.572 51.845 35.774 C 53.048 35.205 54.285 34.405 55.524 33.418 L 56.784 35.101 C 55.427 36.182 54.055 37.067 52.701 37.708 C 50.658 38.675 48.65 39.087 46.775 38.786 C 44.852 38.477 43.109 37.435 41.662 35.5 C 41.433 35.192 41.218 34.874 41.019 34.546 C 40.041 36.024 38.79 37.103 37.401 37.807 C 36.084 38.476 34.646 38.802 33.203 38.808 C 31.769 38.813 30.331 38.503 29.007 37.901 C 27.407 37.172 25.962 36.015 24.873 34.468 C 23.468 36.274 21.096 37.983 18.463 39.011 C 15.55 40.148 12.274 40.486 9.558 39.257 L 10.382 37.307 Z" style="fill: rgb(30, 130, 255);"/>
<path d="M 10.205 42.722 C 12.377 43.705 15.096 43.395 17.559 42.434 C 20.475 41.295 22.974 39.287 23.776 37.437 C 24.008 36.9 24.617 36.659 25.137 36.898 C 25.367 37.004 25.552 37.194 25.655 37.431 C 26.54 39.271 27.994 40.606 29.662 41.365 C 30.731 41.852 31.883 42.102 33.026 42.097 C 34.159 42.092 35.288 41.836 36.319 41.314 C 37.796 40.565 39.082 39.249 39.91 37.314 C 40.139 36.775 40.749 36.531 41.271 36.768 C 41.524 36.883 41.722 37.097 41.819 37.364 C 42.214 38.24 42.648 38.988 43.117 39.615 C 44.218 41.088 45.51 41.875 46.913 42.1 C 48.367 42.334 49.982 41.987 51.668 41.189 C 52.871 40.62 54.108 39.82 55.347 38.833 L 56.607 40.516 C 55.25 41.597 53.878 42.482 52.524 43.123 C 50.481 44.09 48.473 44.502 46.598 44.201 C 44.675 43.892 42.932 42.85 41.485 40.915 C 41.256 40.607 41.041 40.289 40.842 39.961 C 39.864 41.439 38.613 42.518 37.224 43.222 C 35.907 43.891 34.469 44.217 33.026 44.223 C 31.592 44.228 30.154 43.918 28.83 43.316 C 27.23 42.587 25.785 41.43 24.696 39.883 C 23.291 41.689 20.919 43.398 18.286 44.426 C 15.373 45.563 12.097 45.901 9.381 44.672 L 10.205 42.722 Z" style="fill: rgb(30, 130, 255);"/>
<path d="M 32.307 33.157 C 30.008 33.038 27.346 31.638 25.776 29.76 C 25.48 29.421 25.206 29.064 24.956 28.688 C 23.585 30.561 21.153 32.357 18.438 33.424 C 15.546 34.563 12.297 34.903 9.61 33.677 L 10.301 32.03 C 12.342 32.962 14.845 32.775 17.183 31.979 C 17.253 28.669 18.202 25.534 19.838 22.875 C 21.336 20.442 23.646 18.223 26.281 16.786 C 29.429 15.07 33.861 14.165 36.905 16.582 C 40.064 19.088 39.596 23.333 36.462 25.669 C 35.744 26.203 34.894 26.543 34.027 26.631 C 33.319 26.702 32.602 26.606 31.934 26.309 C 31.263 26.011 30.648 25.518 30.154 24.801 C 29.581 23.968 29.17 22.827 29.031 21.336 C 28.992 20.904 29.296 20.523 29.711 20.48 C 30.125 20.44 30.493 20.757 30.532 21.188 C 30.643 22.376 30.953 23.26 31.383 23.885 C 31.708 24.357 32.102 24.676 32.525 24.866 C 32.954 25.056 33.42 25.116 33.885 25.069 C 34.48 25.009 35.072 24.769 35.582 24.39 C 37.883 22.674 38.346 19.7 35.99 17.83 C 26.886 12.683 22.711 27.316 31.469 31.199 C 32.079 31.342 32.703 31.412 33.329 31.411 C 34.495 31.407 35.656 31.142 36.717 30.6 C 38.239 29.821 39.562 28.459 40.413 26.455 C 40.605 26 41.116 25.793 41.554 25.994 C 41.766 26.091 41.931 26.272 42.013 26.498 C 42.418 27.404 42.863 28.177 43.343 28.823 C 44.48 30.357 45.818 31.177 47.274 31.413 C 48.771 31.655 50.429 31.298 52.154 30.475 C 53.379 29.893 54.635 29.073 55.89 28.065 L 56.947 29.488 C 55.592 30.576 54.222 31.465 52.872 32.109 C 50.847 33.075 48.86 33.487 47.01 33.188 C 45.117 32.881 43.402 31.847 41.976 29.924 C 41.694 29.542 41.434 29.145 41.197 28.733 C 40.211 30.324 38.919 31.474 37.476 32.212 C 36.175 32.876 34.753 33.201 33.329 33.207 C 32.988 33.209 32.647 33.192 32.307 33.157 Z" style="fill: rgb(30, 130, 255);"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<svg enable-background="new 0 0 64 64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<ellipse style="stroke-width: 0px; stroke: rgb(0, 0, 0); fill:#3d3c3c;" cx="32" cy="32" rx="30" ry="30" />
<path
d="m61.55 22.14c-3.06-.84-7.3-2.16-9.33-3.51-3.39-2.25-4.77-2.26-7.12-3.02-2.35-.76-4.78-2.08-4.78-2.08s-2.57-2.17-6.5-2.77c-3.92-.6-5.65-2.61-5.65-2.61s-.6-1.38-2.2-1.96c-2.54-1.01-6.25 2.29-8.28 7.38-2.03 5.09-1.61 10.04.94 11.05 0 0 6.32 2.52 10.66 2.08 4.34-.44 7.8.39 7.8.39s3.64.85 7.37.58c3.72-.27 6.09.62 6.09.62s2.41.67 10.18.27c.31-.43.57-.91.77-1.43.67-1.69.64-3.47.05-4.99z"
fill="#db5336" stroke="#db5336" stroke-miterlimit="10" />
<ellipse transform="matrix(-.929-.37.37-.929 37.3113 37.9507)" cx="22.3" cy="15.4" rx="4.96" ry="9.92"
fill="#ca2a14" />
<g fill="#fff">
<path
d="m61.55 22.14c-1.77-.48-3.92-1.13-5.82-1.84.77 1.74.88 3.69.16 5.5-.46 1.15-1.2 2.1-2.12 2.84 1.58.07 3.84.09 6.97-.07.31-.43.57-.91.77-1.43.66-1.7.63-3.48.04-5" />
<path
d="m50.1 17.36c-1.98-1.03-3.21-1.17-5-1.75-.5-.16-1.01-.35-1.49-.54 1.27 2.47 1.49 5.53.35 8.41-.67 1.68-1.73 3.09-3.01 4.15 1.08.08 2.29.12 3.51.03 1.07-.08 2.02-.06 2.84.01 1.21-.93 2.19-2.2 2.8-3.73.89-2.21.82-4.56 0-6.58" />
<path
d="m37.81 12c-1.06-.5-2.42-1-3.99-1.24-.84-.13-1.57-.32-2.22-.54 1.6 2.52 1.73 6.77.05 10.99-.89 2.24-2.16 4.14-3.59 5.53.42 0 .83-.01 1.23-.05 1.8-.18 3.45-.15 4.76-.04 1.75-1.09 3.32-2.98 4.29-5.4 1.4-3.53 1.1-7.14-.53-9.25" />
<path
d="m24.53 15.02c-1.09 2.73-1.32 5.44-.8 7.43 1.21-1.31 2.34-3.11 3.18-5.22 1.08-2.72 1.47-5.4 1.2-7.46-1.4 1.19-2.7 3.02-3.58 5.25" />
</g>
<g fill="none" stroke="#1a171b" stroke-miterlimit="10">
<path d="m13.55 10.5 4.7 2.19" />
<path d="m13.55 10.5 13.28-3.87" />
<path d="m13.55 10.5 8.74 13.45" />
<path d="M 13.55,10.5 7,9" />
</g>
<path d="M4.5,59.5V9c0-1.38,1.12-2.5,2.5-2.5S9.5,7.62,9.5,9c0,1.21-0.86,2.22-2,2.45V59.5H4.5z" fill="#8b96ac" />
<path
d="m7 7c1.1 0 2 0.9 2 2s-0.9 2-2 2v48h-2v-50c0-1.1 0.9-2 2-2m0-1c-1.65 0-3 1.35-3 3v51h4v-48.17c1.16-0.41 2-1.52 2-2.83 0-1.65-1.35-3-3-3z"
fill="#676b72" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -246,8 +246,8 @@
<module>org.openhab.binding.mecmeter</module>
<module>org.openhab.binding.melcloud</module>
<module>org.openhab.binding.mercedesme</module>
<module>org.openhab.binding.meteoalerte</module>
<module>org.openhab.binding.meteoblue</module>
<module>org.openhab.binding.meteofrance</module>
<module>org.openhab.binding.meteostick</module>
<module>org.openhab.binding.mffan</module>
<module>org.openhab.binding.miele</module>