Progress done

Signed-off-by: Gaël L'hopital <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2024-10-24 17:38:04 +02:00
parent 1265419834
commit 4658292366
40 changed files with 1215 additions and 337 deletions

View File

@ -11,3 +11,17 @@ https://www.eclipse.org/legal/epl-2.0/.
== Source Code == Source Code
https://github.com/openhab/openhab-addons https://github.com/openhab/openhab-addons
== Credits
Icon set coming from the noon project:
Hazel, Ash : Imogen Oh
Birch, Oak : monkik
Cypress, Alder : Levi
Poplar, Rumex : Laymik
Willow : PizzaOter
Hornbeam, Linden : Cannavale
Olive : BnB Studio
Chestnut : Muhammad Fadli Rusady
Plantain : Lars Meiertoberens
Grasses : Neneng Yuliani Lestari
Ragweed, Wormwood : bsd studio

View File

@ -33,8 +33,18 @@ public class AirParifBindingConstants {
// List of Things Type UIDs // List of Things Type UIDs
public static final ThingTypeUID LOCATION_THING_TYPE = new ThingTypeUID(BINDING_ID, "location"); public static final ThingTypeUID LOCATION_THING_TYPE = new ThingTypeUID(BINDING_ID, "location");
// Channel group ids
public static final String GROUP_POLLENS = "pollens";
public static final String GROUP_DAILY = "daily";
public static final String GROUP_AQ_BULLETIN = "aq-bulletin";
public static final String GROUP_AQ_BULLETIN_TOMORROW = GROUP_AQ_BULLETIN + "-tomorrow";
// List of all Channel ids // List of all Channel ids
public static final String CHANNEL_1 = "channel1"; public static final String CHANNEL_BEGIN_VALIDITY = "begin-validity";
public static final String CHANNEL_END_VALIDITY = "end-validity";
public static final String CHANNEL_COMMENT = "comment";
public static final String CHANNEL_MESSAGE = "message";
public static final String CHANNEL_TOMORROW = "tomorrow";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(APIBRIDGE_THING_TYPE, public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(APIBRIDGE_THING_TYPE,
LOCATION_THING_TYPE); LOCATION_THING_TYPE);

View File

@ -41,12 +41,14 @@ import org.osgi.service.component.annotations.Reference;
public class AirParifHandlerFactory extends BaseThingHandlerFactory { public class AirParifHandlerFactory extends BaseThingHandlerFactory {
private final AirParifDeserializer deserializer; private final AirParifDeserializer deserializer;
private final HttpClient httpClient; private final HttpClient httpClient;
private final AirParifIconProvider iconProvider;
@Activate @Activate
public AirParifHandlerFactory(final @Reference HttpClientFactory httpClientFactory, public AirParifHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference AirParifDeserializer deserializer) { final @Reference AirParifDeserializer deserializer, final @Reference AirParifIconProvider iconProvider) {
this.httpClient = httpClientFactory.getCommonHttpClient(); this.httpClient = httpClientFactory.getCommonHttpClient();
this.deserializer = deserializer; this.deserializer = deserializer;
this.iconProvider = iconProvider;
} }
@Override @Override
@ -59,7 +61,7 @@ public class AirParifHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
return APIBRIDGE_THING_TYPE.equals(thingTypeUID) return APIBRIDGE_THING_TYPE.equals(thingTypeUID)
? new AirParifBridgeHandler((Bridge) thing, httpClient, deserializer) ? new AirParifBridgeHandler((Bridge) thing, httpClient, deserializer, iconProvider)
: LOCATION_THING_TYPE.equals(thingTypeUID) ? new LocationHandler(thing) : null; : LOCATION_THING_TYPE.equals(thingTypeUID) ? new LocationHandler(thing) : null;
} }
} }

View File

@ -25,6 +25,9 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
import org.openhab.binding.airparif.internal.api.ColorMap;
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.IconProvider; import org.openhab.core.ui.icon.IconProvider;
import org.openhab.core.ui.icon.IconSet; import org.openhab.core.ui.icon.IconSet;
@ -45,13 +48,17 @@ import org.slf4j.LoggerFactory;
@Component(service = { IconProvider.class, AirParifIconProvider.class }) @Component(service = { IconProvider.class, AirParifIconProvider.class })
@NonNullByDefault @NonNullByDefault
public class AirParifIconProvider implements IconProvider { public class AirParifIconProvider implements IconProvider {
private static final String NEUTRAL_COLOR = "#3d3c3c";
private static final String DEFAULT_LABEL = "Air Parif Icons"; private static final String DEFAULT_LABEL = "Air Parif Icons";
private static final String DEFAULT_DESCRIPTION = "Icons illustrating air quality levels provided by AirParif"; private static final String DEFAULT_DESCRIPTION = "Icons illustrating air quality levels provided by AirParif";
private static final List<String> ICONS = List.of("average", "bad", "degrated", "extremely-bad", "good", "pollen"); private static final List<String> ICONS = List.of("average", "bad", "degrated", "extremely-bad", "good", "pollen");
private static final List<String> POLLEN_ICONS = Pollen.AS_SET.stream().map(Pollen::name).map(String::toLowerCase)
.toList();
private final Logger logger = LoggerFactory.getLogger(AirParifIconProvider.class); private final Logger logger = LoggerFactory.getLogger(AirParifIconProvider.class);
private final TranslationProvider i18nProvider; private final TranslationProvider i18nProvider;
private final Bundle bundle; private final Bundle bundle;
private @Nullable ColorMap colorMap;
@Activate @Activate
public AirParifIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) { public AirParifIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
@ -79,7 +86,8 @@ public class AirParifIconProvider implements IconProvider {
@Override @Override
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) { public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
return Format.SVG.equals(format) && iconSetId.equals(BINDING_ID) && ICONS.contains(category) ? 0 : null; return Format.SVG.equals(format) && iconSetId.equals(BINDING_ID)
&& (ICONS.contains(category) || POLLEN_ICONS.contains(category)) ? 0 : null;
} }
@Override @Override
@ -89,6 +97,16 @@ public class AirParifIconProvider implements IconProvider {
String result; String result;
try (InputStream stream = iconResource.openStream()) { try (InputStream stream = iconResource.openStream()) {
result = new String(stream.readAllBytes(), StandardCharsets.UTF_8); result = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
if (POLLEN_ICONS.contains(category) && state != null) {
try {
int ordinal = Integer.valueOf(state);
PollenAlertLevel alertLevel = PollenAlertLevel.AS_SET.stream()
.filter(pal -> pal.riskLevel == ordinal).findFirst().orElse(PollenAlertLevel.UNKNOWN);
result = result.replaceAll(NEUTRAL_COLOR, alertLevel.color);
} catch (NumberFormatException ignore) {
}
}
} catch (IOException e) { } catch (IOException e) {
logger.warn("Unable to load ressource '{}': {}", iconResource.getPath(), e.getMessage()); logger.warn("Unable to load ressource '{}': {}", iconResource.getPath(), e.getMessage());
result = ""; result = "";
@ -96,4 +114,8 @@ public class AirParifIconProvider implements IconProvider {
return result.isEmpty() ? null : new ByteArrayInputStream(result.getBytes()); return result.isEmpty() ? null : new ByteArrayInputStream(result.getBytes());
} }
public void setColorMap(ColorMap map) {
this.colorMap = map;
}
} }

View File

@ -13,11 +13,15 @@
package org.openhab.binding.airparif.internal.api; package org.openhab.binding.airparif.internal.api;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -45,7 +49,7 @@ public class AirParifDto {
@SerializedName("droits") Set<Scope> scopes) { @SerializedName("droits") Set<Scope> scopes) {
} }
private record Message(// public record Message(//
String fr, // String fr, //
@Nullable String en) { @Nullable String en) {
} }
@ -92,36 +96,73 @@ public class AirParifDto {
Map<String, PollenAlertLevel[]> valeurs, // Map<String, PollenAlertLevel[]> valeurs, //
String commentaire, // String commentaire, //
String periode) { String periode) {
}
public class PollensResponse {
private static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yy"); private static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yy");
private static Pattern PATTERN = Pattern.compile("\\d{2}.\\d{2}.\\d{2}"); private static Pattern PATTERN = Pattern.compile("\\d{2}.\\d{2}.\\d{2}");
private static ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");
private static @Nullable LocalDate getValidity(String periode, boolean begin) { public List<Pollens> data = List.of();
Matcher matcher = PATTERN.matcher(periode);
if (matcher.find()) { public Optional<Pollens> getData() {
String extractedDate = matcher.group(); return Optional.ofNullable(data.isEmpty() ? null : data.get(0));
if (begin) {
return LocalDate.parse(extractedDate, FORMATTER);
}
if (matcher.find()) {
extractedDate = matcher.group();
return LocalDate.parse(extractedDate, FORMATTER);
}
}
return null;
} }
public @Nullable LocalDate beginValidity() { private Set<ZonedDateTime> getValidities() {
return getValidity(periode, true); Set<ZonedDateTime> result = new TreeSet<>();
getData().ifPresent(pollens -> {
Matcher matcher = PATTERN.matcher(pollens.periode);
while (matcher.find()) {
result.add(LocalDate.parse(matcher.group(), FORMATTER).atStartOfDay(DEFAULT_ZONE));
}
});
return result;
} }
public @Nullable LocalDate endValidity() { public Optional<ZonedDateTime> getBeginValidity() {
return getValidity(periode, false); return Optional.ofNullable(getValidities().iterator().next());
} }
public Optional<ZonedDateTime> getEndValidity() {
return Optional.ofNullable(getValidities().stream().reduce((prev, next) -> next).orElse(null));
}
public Optional<String> getComment() {
return getData().map(pollens -> pollens.commentaire);
}
public Map<Pollen, PollenAlertLevel> getDepartment(String id) {
Map<Pollen, PollenAlertLevel> result = new HashMap<>();
Optional<Pollens> donnees = getData();
if (donnees.isPresent()) {
Pollens depts = donnees.get();
PollenAlertLevel[] valeurs = depts.valeurs.get(id);
if (valeurs != null) {
for (int i = 0; i < valeurs.length; i++) {
result.put(depts.taxons[i], valeurs[i]);
}
}
}
return result;
}
}
public record Result(//
@SerializedName("polluant") Pollutant pollutant, //
ZonedDateTime date, //
@SerializedName("valeurs") double[] values, //
Message message) {
}
public record Route(//
@SerializedName("dateRequise") ZonedDateTime requestedDate, //
double[][] longlats, //
@SerializedName("resultats") Result[] results, //
@Nullable Message[] messages) {
} }
public record PollensResponse(ArrayList<Pollens> data) { public record ItineraireResponse(@SerializedName("itineraires") Route[] routes) {
} }
} }

View File

@ -26,8 +26,8 @@ public class ColorMap extends HashMap<Appreciation, String> {
private static final long serialVersionUID = -605462873565278453L; private static final long serialVersionUID = -605462873565278453L;
private static Appreciation fromApiName(String searched) { private static Appreciation fromApiName(String searched) {
return Objects.requireNonNull( return Objects.requireNonNull(Appreciation.AS_SET.stream().filter(mt -> searched.equals(mt.apiName)).findFirst()
Appreciation.AS_SET.stream().filter(mt -> searched.equals(mt.apiName)).findFirst().orElse(Appreciation.UNKNOWN)); .orElse(Appreciation.UNKNOWN));
} }
public String put(String key, String value) { public String put(String key, String value) {

View File

@ -24,20 +24,22 @@ import com.google.gson.annotations.SerializedName;
@NonNullByDefault @NonNullByDefault
public enum PollenAlertLevel { public enum PollenAlertLevel {
@SerializedName("0") @SerializedName("0")
NONE(0), NONE(0, "#3a8b2f"),
@SerializedName("1") @SerializedName("1")
LOW(1), LOW(1, "#f9a825"),
@SerializedName("2") @SerializedName("2")
AVERAGE(2), AVERAGE(2, "#ef6c00"),
@SerializedName("3") @SerializedName("3")
HIGH(3), HIGH(3, "#b71c1c"),
UNKNOWN(-1); UNKNOWN(-1, "#b3b3b3");
public static final EnumSet<PollenAlertLevel> AS_SET = EnumSet.allOf(PollenAlertLevel.class); public static final EnumSet<PollenAlertLevel> AS_SET = EnumSet.allOf(PollenAlertLevel.class);
public final int riskLevel; public final int riskLevel;
public final String color;
PollenAlertLevel(int riskLevel) { PollenAlertLevel(int riskLevel, String color) {
this.riskLevel = riskLevel; this.riskLevel = riskLevel;
this.color = color;
} }
} }

View File

@ -14,7 +14,12 @@ package org.openhab.binding.airparif.internal.api;
import java.util.EnumSet; import java.util.EnumSet;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.Units;
import com.google.gson.annotations.SerializedName;
/** /**
* The {@link Pollutant} enum lists all pollutants tracked by AirParif * The {@link Pollutant} enum lists all pollutants tracked by AirParif
@ -23,14 +28,31 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/ */
@NonNullByDefault @NonNullByDefault
public enum Pollutant { public enum Pollutant {
PM25, @SerializedName("pm25")
PM10, PM25(Units.MICROGRAM_PER_CUBICMETRE),
NO2,
O3, @SerializedName("pm10")
UNKNOWN; PM10(Units.MICROGRAM_PER_CUBICMETRE),
@SerializedName("no2")
NO2(Units.PARTS_PER_BILLION),
@SerializedName("o3")
O3(Units.PARTS_PER_BILLION),
@SerializedName("indice")
INDICE(Units.PERCENT),
UNKNOWN(Units.PERCENT);
public static final EnumSet<Pollutant> AS_SET = EnumSet.allOf(Pollutant.class); public static final EnumSet<Pollutant> AS_SET = EnumSet.allOf(Pollutant.class);
public final Unit<?> unit;
Pollutant(Unit<?> unit) {
this.unit = unit;
}
public static Pollutant safeValueOf(String searched) { public static Pollutant safeValueOf(String searched) {
try { try {
return Pollutant.valueOf(searched); return Pollutant.valueOf(searched);

View File

@ -23,7 +23,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
@NonNullByDefault @NonNullByDefault
public class LocationConfiguration { public class LocationConfiguration {
public static final String LOCATION = "location"; public static final String LOCATION = "location";
public static final String DEPARTMENT = "department";
public int refresh = 10; public int refresh = 10;
public String location = ""; public String location = "";
public String department = "";
} }

View File

@ -24,8 +24,6 @@ import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.PointType;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,7 +39,6 @@ import com.google.gson.JsonSyntaxException;
* @author Gaël L'hopital - Initial Contribution * @author Gaël L'hopital - Initial Contribution
*/ */
@Component(service = DepartmentDbService.class)
@NonNullByDefault @NonNullByDefault
public class DepartmentDbService { public class DepartmentDbService {
private final Logger logger = LoggerFactory.getLogger(DepartmentDbService.class); private final Logger logger = LoggerFactory.getLogger(DepartmentDbService.class);
@ -51,7 +48,6 @@ public class DepartmentDbService {
double westestLon) { double westestLon) {
} }
@Activate
public DepartmentDbService() { public DepartmentDbService() {
try (InputStream is = Thread.currentThread().getContextClassLoader() try (InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("/db/departments.json"); .getResourceAsStream("/db/departments.json");

View File

@ -44,7 +44,7 @@ public class AirParifDeserializer {
@Activate @Activate
public AirParifDeserializer(final @Reference TimeZoneProvider timeZoneProvider) { public AirParifDeserializer(final @Reference TimeZoneProvider timeZoneProvider) {
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.registerTypeAdapter(PollenAlertLevel.class, new PollenAlertLevelDeserializer()) .registerTypeAdapter(PollenAlertLevel.class, new PollenAlertLevelDeserializer())
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory()) .registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
.registerTypeAdapter(ColorMap.class, new ColorMapDeserializer()) .registerTypeAdapter(ColorMap.class, new ColorMapDeserializer())
@ -52,11 +52,11 @@ public class AirParifDeserializer {
.registerTypeAdapter(LocalDate.class, .registerTypeAdapter(LocalDate.class,
(JsonDeserializer<LocalDate>) (json, type, context) -> LocalDate (JsonDeserializer<LocalDate>) (json, type, context) -> LocalDate
.parse(json.getAsJsonPrimitive().getAsString())) .parse(json.getAsJsonPrimitive().getAsString()))
.registerTypeAdapter(ZonedDateTime.class, .registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer<ZonedDateTime>) (json, type, context) -> {
(JsonDeserializer<ZonedDateTime>) (json, type, context) -> ZonedDateTime String string = json.getAsJsonPrimitive().getAsString();
.parse(json.getAsJsonPrimitive().getAsString() + "Z") string += string.contains("+") ? "" : "Z";
.withZoneSameInstant(timeZoneProvider.getTimeZone())) return ZonedDateTime.parse(string).withZoneSameInstant(timeZoneProvider.getTimeZone());
.create(); }).create();
} }
public <T> T deserialize(Class<T> clazz, String json) throws AirParifException { public <T> T deserialize(Class<T> clazz, String json) throws AirParifException {
@ -68,8 +68,7 @@ public class AirParifDeserializer {
} }
throw new AirParifException("Deserialization of '%s' resulted in null value", json); throw new AirParifException("Deserialization of '%s' resulted in null value", json);
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
throw new AirParifException(e, "Unexpected error deserializing '%s'", json); throw new AirParifException(e, "Unexpected error deserializing '%s'", e.getMessage());
} }
} }
} }

View File

@ -42,6 +42,5 @@ class PollenAlertLevelDeserializer implements JsonDeserializer<PollenAlertLevel>
return PollenAlertLevel.AS_SET.stream().filter(s -> s.riskLevel == level).findFirst() return PollenAlertLevel.AS_SET.stream().filter(s -> s.riskLevel == level).findFirst()
.orElse(PollenAlertLevel.UNKNOWN); .orElse(PollenAlertLevel.UNKNOWN);
} }
} }

View File

@ -13,15 +13,17 @@
package org.openhab.binding.airparif.internal.discovery; package org.openhab.binding.airparif.internal.discovery;
import static org.openhab.binding.airparif.internal.AirParifBindingConstants.*; import static org.openhab.binding.airparif.internal.AirParifBindingConstants.*;
import static org.openhab.binding.airparif.internal.config.LocationConfiguration.LOCATION;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airparif.internal.config.LocationConfiguration;
import org.openhab.binding.airparif.internal.db.DepartmentDbService;
import org.openhab.binding.airparif.internal.db.DepartmentDbService.Department;
import org.openhab.binding.airparif.internal.handler.AirParifBridgeHandler; import org.openhab.binding.airparif.internal.handler.AirParifBridgeHandler;
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService; import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider; import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.PointType;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
@ -41,40 +43,47 @@ public class AirParifDiscoveryService extends AbstractThingHandlerDiscoveryServi
private static final int DISCOVER_TIMEOUT_SECONDS = 2; private static final int DISCOVER_TIMEOUT_SECONDS = 2;
private final Logger logger = LoggerFactory.getLogger(AirParifDiscoveryService.class); private final Logger logger = LoggerFactory.getLogger(AirParifDiscoveryService.class);
private final DepartmentDbService dbService;
private @NonNullByDefault({}) LocationProvider locationProvider; private @NonNullByDefault({}) LocationProvider locationProvider;
public AirParifDiscoveryService() { public AirParifDiscoveryService() {
super(AirParifBridgeHandler.class, SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS); super(AirParifBridgeHandler.class, SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS);
dbService = new DepartmentDbService();
} }
@Reference(unbind = "-") @Reference(unbind = "-")
public void bindTranslationProvider(TranslationProvider translationProvider) { public void setLocationProvider(LocationProvider locationProvider) {
this.i18nProvider = translationProvider;
}
@Reference(unbind = "-")
public void bindLocaleProvider(LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
@Reference(unbind = "-")
public void bindLocationProvider(LocationProvider locationProvider) {
this.locationProvider = locationProvider; this.locationProvider = locationProvider;
} }
@Override @Override
protected void startScan() { public void startScan() {
logger.debug("Starting AirParif discovery scan"); logger.debug("Starting AirParif discovery scan");
if (locationProvider.getLocation() instanceof PointType location) {
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);
}
private void createDepartmentResults(PointType serverLocation) {
List<Department> candidates = dbService.getBounding(serverLocation);
ThingUID bridgeUID = thingHandler.getThing().getUID(); ThingUID bridgeUID = thingHandler.getThing().getUID();
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(LOCATION_THING_TYPE, bridgeUID, LOCAL)) if (!candidates.isEmpty()) {
.withLabel("@text/discovery.airparif.location.local.label") // candidates.forEach(dep -> thingDiscovered(
.withProperty(LOCATION, location.toString()) // DiscoveryResultBuilder.create(new ThingUID(LOCATION_THING_TYPE, bridgeUID, dep.id()))//
.withRepresentationProperty(LOCATION) // .withLabel("Location Report: %s".formatted(dep.name())) //
.withBridge(bridgeUID).build()); .withProperty(LocationConfiguration.DEPARTMENT, dep.id()) //
.withProperty(LocationConfiguration.LOCATION, serverLocation.toFullString())//
.withRepresentationProperty(LocationConfiguration.DEPARTMENT) //
.withBridge(bridgeUID).build()));
} else { } else {
logger.debug("LocationProvider.getLocation() is not set, no discovery results can be provided"); logger.info("No department could be discovered matching server location");
} }
} }
} }

View File

@ -12,13 +12,22 @@
*/ */
package org.openhab.binding.airparif.internal.handler; package org.openhab.binding.airparif.internal.handler;
import static org.openhab.binding.airparif.internal.AirParifBindingConstants.*;
import static org.openhab.binding.airparif.internal.api.AirParifApi.*; import static org.openhab.binding.airparif.internal.api.AirParifApi.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -26,29 +35,38 @@ import java.util.stream.Collectors;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpStatus.Code; import org.eclipse.jetty.http.HttpStatus.Code;
import org.openhab.binding.airparif.internal.AirParifException; import org.openhab.binding.airparif.internal.AirParifException;
import org.openhab.binding.airparif.internal.AirParifIconProvider;
import org.openhab.binding.airparif.internal.api.AirParifDto.Bulletin; import org.openhab.binding.airparif.internal.api.AirParifDto.Bulletin;
import org.openhab.binding.airparif.internal.api.AirParifDto.Episode; import org.openhab.binding.airparif.internal.api.AirParifDto.Episode;
import org.openhab.binding.airparif.internal.api.AirParifDto.ItineraireResponse;
import org.openhab.binding.airparif.internal.api.AirParifDto.KeyInfo; import org.openhab.binding.airparif.internal.api.AirParifDto.KeyInfo;
import org.openhab.binding.airparif.internal.api.AirParifDto.Pollens;
import org.openhab.binding.airparif.internal.api.AirParifDto.PollensResponse; import org.openhab.binding.airparif.internal.api.AirParifDto.PollensResponse;
import org.openhab.binding.airparif.internal.api.AirParifDto.Route;
import org.openhab.binding.airparif.internal.api.AirParifDto.Version; import org.openhab.binding.airparif.internal.api.AirParifDto.Version;
import org.openhab.binding.airparif.internal.api.ColorMap; import org.openhab.binding.airparif.internal.api.ColorMap;
import org.openhab.binding.airparif.internal.config.BridgeConfiguration; import org.openhab.binding.airparif.internal.config.BridgeConfiguration;
import org.openhab.binding.airparif.internal.deserialization.AirParifDeserializer; import org.openhab.binding.airparif.internal.deserialization.AirParifDeserializer;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -66,13 +84,19 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(AirParifBridgeHandler.class); private final Logger logger = LoggerFactory.getLogger(AirParifBridgeHandler.class);
private final AirParifDeserializer deserializer; private final AirParifDeserializer deserializer;
private final AirParifIconProvider iconProvider;
private final HttpClient httpClient; private final HttpClient httpClient;
private BridgeConfiguration config = new BridgeConfiguration(); private BridgeConfiguration config = new BridgeConfiguration();
public AirParifBridgeHandler(Bridge bridge, HttpClient httpClient, AirParifDeserializer deserializer) { private @Nullable ScheduledFuture<?> pollensJob;
private @Nullable ScheduledFuture<?> dailyJob;
public AirParifBridgeHandler(Bridge bridge, HttpClient httpClient, AirParifDeserializer deserializer,
AirParifIconProvider iconProvider) {
super(bridge); super(bridge);
this.deserializer = deserializer; this.deserializer = deserializer;
this.iconProvider = iconProvider;
this.httpClient = httpClient; this.httpClient = httpClient;
} }
@ -85,16 +109,39 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
"@text/offline.config-error-unknown-apikey"); "@text/offline.config-error-unknown-apikey");
return; return;
} }
initiateConnexion(); scheduler.execute(this::initiateConnexion);
} }
public synchronized String executeUri(URI uri) throws AirParifException { private @Nullable ScheduledFuture<?> cancelFuture(@Nullable ScheduledFuture<?> job) {
if (job != null && !job.isCancelled()) {
job.cancel(true);
}
return null;
}
@Override
public void dispose() {
logger.debug("Disposing the AirParif bridge handler.");
pollensJob = cancelFuture(pollensJob);
dailyJob = cancelFuture(dailyJob);
}
public synchronized String executeUri(URI uri, HttpMethod method, @Nullable String payload)
throws AirParifException {
logger.debug("executeUrl: {} ", uri); logger.debug("executeUrl: {} ", uri);
Request request = httpClient.newRequest(uri).method(HttpMethod.GET) Request request = httpClient.newRequest(uri).method(method).timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.header(HttpHeader.ACCEPT, MediaType.APPLICATION_JSON).header("X-Api-Key", config.apikey); .header(HttpHeader.ACCEPT, MediaType.APPLICATION_JSON).header("X-Api-Key", config.apikey);
if (payload != null && HttpMethod.POST.equals(method)) {
InputStream stream = new ByteArrayInputStream(payload.getBytes(DEFAULT_CHARSET));
try (InputStreamContentProvider inputStreamContentProvider = new InputStreamContentProvider(stream)) {
request.content(inputStreamContentProvider, MediaType.APPLICATION_JSON);
}
logger.trace(" -with payload : {} ", payload);
}
try { try {
ContentResponse response = request.send(); ContentResponse response = request.send();
@ -107,7 +154,7 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
} else if (statusCode == Code.FORBIDDEN) { } else if (statusCode == Code.FORBIDDEN) {
throw new AirParifException("@text/offline.config-error-invalid-apikey"); throw new AirParifException("@text/offline.config-error-invalid-apikey");
} }
String content = new String(response.getContent(), DEFAULT_CHARSET);
throw new AirParifException("Error '%s' requesting: %s", statusCode.getMessage(), uri.toString()); throw new AirParifException("Error '%s' requesting: %s", statusCode.getMessage(), uri.toString());
} catch (TimeoutException | ExecutionException e) { } catch (TimeoutException | ExecutionException e) {
throw new AirParifException(e, "Exception while calling %s", request.getURI()); throw new AirParifException(e, "Exception while calling %s", request.getURI());
@ -117,7 +164,12 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
} }
public synchronized <T> T executeUri(URI uri, Class<T> clazz) throws AirParifException { public synchronized <T> T executeUri(URI uri, Class<T> clazz) throws AirParifException {
String content = executeUri(uri); String content = executeUri(uri, HttpMethod.GET, null);
return deserializer.deserialize(clazz, content);
}
public synchronized <T> T executeUri(URI uri, Class<T> clazz, String payload) throws AirParifException {
String content = executeUri(uri, HttpMethod.POST, payload);
return deserializer.deserialize(clazz, content); return deserializer.deserialize(clazz, content);
} }
@ -128,8 +180,6 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
private void initiateConnexion() { private void initiateConnexion() {
Version version; Version version;
KeyInfo keyInfo;
try { // This does validate communication with the server try { // This does validate communication with the server
version = executeUri(VERSION_URI, Version.class); version = executeUri(VERSION_URI, Version.class);
} catch (AirParifException e) { } catch (AirParifException e) {
@ -137,6 +187,7 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
return; return;
} }
KeyInfo keyInfo;
try { // This validates the api key value try { // This validates the api key value
keyInfo = executeUri(KEY_INFO_URI, KeyInfo.class); keyInfo = executeUri(KEY_INFO_URI, KeyInfo.class);
} catch (AirParifException e) { } catch (AirParifException e) {
@ -146,31 +197,101 @@ public class AirParifBridgeHandler extends BaseBridgeHandler {
getThing().setProperty("api-version", version.version()); getThing().setProperty("api-version", version.version());
getThing().setProperty("key-expiration", keyInfo.expiration().toString()); getThing().setProperty("key-expiration", keyInfo.expiration().toString());
logger.info("The api key is valid until {}", keyInfo.expiration().toString());
getThing().setProperty("scopes", keyInfo.scopes().stream().map(e -> e.name()).collect(Collectors.joining(","))); getThing().setProperty("scopes", keyInfo.scopes().stream().map(e -> e.name()).collect(Collectors.joining(",")));
logger.info("The api key is valid until {}", keyInfo.expiration().toString());
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
try { try {
ColorMap map = executeUri(PREV_COLORS_URI, ColorMap.class); ColorMap map = executeUri(PREV_COLORS_URI, ColorMap.class);
logger.info("The color map is {}", map.toString()); logger.debug("The color map is {}", map.toString());
iconProvider.setColorMap(map);
} catch (AirParifException e) {
logger.warn("Error reading ColorMap: {]", e.getMessage());
}
pollensJob = scheduler.schedule(this::updatePollens, 1, TimeUnit.SECONDS);
dailyJob = scheduler.schedule(this::updateDaily, 2, TimeUnit.SECONDS);
}
private void updateDaily() {
try {
Bulletin bulletin = executeUri(PREV_BULLETIN_URI, Bulletin.class); Bulletin bulletin = executeUri(PREV_BULLETIN_URI, Bulletin.class);
logger.info("The bulletin is {}", bulletin.today().dayDescription()); logger.debug("The bulletin is {}", bulletin.today().dayDescription());
Set.of(bulletin.today(), bulletin.tomorrow()).stream().forEach(aq -> {
String groupName = aq.previsionDate().equals(LocalDate.now()) ? GROUP_AQ_BULLETIN
: GROUP_AQ_BULLETIN_TOMORROW + "#";
updateState(groupName + CHANNEL_COMMENT,
!aq.available() ? UnDefType.UNDEF : new StringType(aq.bulletin().fr()));
aq.concentrations().forEach(measure -> {
String cName = groupName + measure.pollutant().name().toLowerCase();
updateState(cName + "-min", !aq.available() ? UnDefType.UNDEF
: new QuantityType<>(measure.min(), measure.pollutant().unit));
updateState(cName + "-max", !aq.available() ? UnDefType.UNDEF
: new QuantityType<>(measure.max(), measure.pollutant().unit));
});
});
Episode episode = executeUri(EPISODES_URI, Episode.class); Episode episode = executeUri(EPISODES_URI, Episode.class);
logger.info("The bulletin is {}", episode); logger.debug("The episode is {}", episode);
Pollens pollens = executeUri(POLLENS_URI, PollensResponse.class).data().get(0); // if (episode.active()) {
logger.info("The pollens are {}", pollens); // updateState(GROUP_DAILY + "#" + CHANNEL_MESSAGE, new StringType(episode.message().fr()));
LocalDate begin = pollens.beginValidity(); // updateState(GROUP_DAILY + "#" + CHANNEL_TOMORROW, new StringType(episode.message().fr()));
LocalDate end = pollens.endValidity(); // }
String response = executeUri(POLLENS_DEPT_BUILDER.path("78").build()); ZonedDateTime tomorrowMorning = ZonedDateTime.now().plusDays(1).truncatedTo(ChronoUnit.DAYS).plusMinutes(1);
logger.info("The pollens 78 {}", response); long delay = Duration.between(ZonedDateTime.now(), tomorrowMorning).getSeconds();
logger.debug("Rescheduling daily job tomorrow morning");
dailyJob = scheduler.schedule(this::updateDaily, delay, TimeUnit.SECONDS);
} catch (AirParifException e) { } catch (AirParifException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); logger.warn("Error update pollens data: {}", e.getMessage());
return;
} }
} }
private void updatePollens() {
try {
PollensResponse pollens = executeUri(POLLENS_URI, PollensResponse.class);
pollens.getComment()
.ifPresent(comment -> updateState(GROUP_POLLENS + "#" + CHANNEL_COMMENT, new StringType(comment)));
pollens.getBeginValidity().ifPresent(
begin -> updateState(GROUP_POLLENS + "#" + CHANNEL_BEGIN_VALIDITY, new DateTimeType(begin)));
pollens.getEndValidity().ifPresent(end -> {
updateState(GROUP_POLLENS + "#" + CHANNEL_END_VALIDITY, new DateTimeType(end));
logger.info("Pollens bulletin valid until {}", end);
long delay = Duration.between(ZonedDateTime.now(), end).getSeconds();
if (delay < 0) {
// what if the bulletin was not updated and the delay is passed ?
delay = 3600;
logger.debug("Update time of the bulletin is in the past - will retry in one hour");
} else {
delay += 60;
}
pollensJob = scheduler.schedule(this::updatePollens, delay, TimeUnit.SECONDS);
});
getThing().getThings().stream().map(Thing::getHandler).filter(LocationHandler.class::isInstance)
.map(LocationHandler.class::cast).forEach(locHand -> locHand.setPollens(pollens));
} catch (AirParifException e) {
logger.warn("Error updating pollens data: {}", e.getMessage());
}
}
public @Nullable Route getConcentrations(String location) {
String[] elements = location.split(",");
if (elements.length >= 2) {
String req = "{\"itineraires\": [{\"date\": \"%s\",\"longlats\": [[%s,%s]]}],\"polluants\": [\"indice\",\"no2\",\"o3\",\"pm25\",\"pm10\"]}";
req = req.formatted(LocalDateTime.now().truncatedTo(ChronoUnit.HOURS), elements[1], elements[0]);
try {
ItineraireResponse result = executeUri(HORAIR_URI, ItineraireResponse.class, req);
return result.routes()[0];
} catch (AirParifException e) {
logger.warn("Error getting detailed concentrations: {}", e.getMessage());
}
} else {
logger.warn("Wrong localisation as input : {}", location);
}
return null;
}
} }

View File

@ -12,17 +12,25 @@
*/ */
package org.openhab.binding.airparif.internal.handler; package org.openhab.binding.airparif.internal.handler;
import static org.openhab.binding.airparif.internal.AirParifBindingConstants.CHANNEL_1; import static org.openhab.binding.airparif.internal.AirParifBindingConstants.GROUP_POLLENS;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
import org.openhab.binding.airparif.internal.api.AirParifDto.PollensResponse;
import org.openhab.binding.airparif.internal.api.AirParifDto.Route;
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
import org.openhab.binding.airparif.internal.config.LocationConfiguration; import org.openhab.binding.airparif.internal.config.LocationConfiguration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -43,36 +51,50 @@ public class LocationHandler extends BaseThingHandler {
super(thing); super(thing);
} }
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (CHANNEL_1.equals(channelUID.getId())) {
if (command instanceof RefreshType) {
// TODO: handle data refresh
}
// TODO: handle command
// Note: if communication with thing fails for some reason,
// indicate that by setting the status with detail information:
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
// "Could not control device at IP address x.x.x.x");
}
}
@Override @Override
public void initialize() { public void initialize() {
config = getConfigAs(LocationConfiguration.class); config = getConfigAs(LocationConfiguration.class);
updateStatus(ThingStatus.UNKNOWN); updateStatus(ThingStatus.UNKNOWN);
// Example for background initialization: scheduler.execute(this::getConcentrations);
scheduler.execute(() -> {
boolean thingReachable = true; // <background task with long running initialization here>
// when done do:
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
} }
public void setPollens(PollensResponse pollens) {
LocationConfiguration local = config;
if (local != null) {
Map<Pollen, PollenAlertLevel> alerts = pollens.getDepartment(local.department);
alerts.forEach((pollen, level) -> {
updateState(GROUP_POLLENS + "#" + pollen.name().toLowerCase(), new DecimalType(level.ordinal()));
}); });
updateStatus(ThingStatus.ONLINE);
}
}
private void getConcentrations() {
AirParifBridgeHandler apiHandler = getApiBridgeHandler();
LocationConfiguration local = config;
if (apiHandler != null && local != null) {
Route route = apiHandler.getConcentrations(local.location);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// TODO Auto-generated method stub
}
private @Nullable AirParifBridgeHandler getApiBridgeHandler() {
Bridge bridge = this.getBridge();
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
if (bridge.getHandler() instanceof AirParifBridgeHandler airParifBridgeHandler) {
return airParifBridgeHandler;
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/incorrect-bridge");
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
return null;
} }
} }

View File

@ -1,3 +1,225 @@
# add-on
addon.airparif.name = AirParif Binding
addon.airparif.description = Air Quality data and forecasts provided by AirParif.
# thing types
thing-type.airparif.api.label = AirParif API Portal
thing-type.airparif.api.description = Bridge to the AirParif API Portal. In order to receive the data, you must register an account on https://www.airparif.fr/contact and receive your API token.
thing-type.airparif.location.label = Department Report
thing-type.airparif.location.description = AirParif air quality report for the given location
# thing types config
thing-type.config.airparif.api.apikey.label = API Key
thing-type.config.airparif.api.apikey.description = Token used to access the service
thing-type.config.airparif.location.department.label = Department
thing-type.config.airparif.location.department.description = Name of the department
thing-type.config.airparif.location.department.option.75 = Paris
thing-type.config.airparif.location.department.option.77 = Seine et Marne
thing-type.config.airparif.location.department.option.78 = Yvelines
thing-type.config.airparif.location.department.option.91 = Essonne
thing-type.config.airparif.location.department.option.92 = Hauts de Seine
thing-type.config.airparif.location.department.option.93 = Seine Saint Denis
thing-type.config.airparif.location.department.option.94 = Val de Marne
thing-type.config.airparif.location.department.option.95 = Val D'Oise
thing-type.config.airparif.location.location.label = Location
# channel group types
channel-group-type.airparif.bridge-pollens.label = Pollen information for the region
channel-group-type.airparif.bridge-pollens.channel.begin-validity.label = Begin Validity
channel-group-type.airparif.bridge-pollens.channel.begin-validity.description = Current bulletin validity start
channel-group-type.airparif.bridge-pollens.channel.comment.label = Begin Validity
channel-group-type.airparif.bridge-pollens.channel.comment.description = Current bulletin validity start
channel-group-type.airparif.bridge-pollens.channel.end-validity.label = End Validity
channel-group-type.airparif.bridge-pollens.channel.end-validity.description = Current bulletin validity ending
channel-group-type.airparif.dept-pollens.label = Pollen information for the department
# channel types
channel-type.airparif.alder-level.label = Alder
channel-type.airparif.alder-level.state.option.0 = None
channel-type.airparif.alder-level.state.option.1 = Low
channel-type.airparif.alder-level.state.option.2 = Average
channel-type.airparif.alder-level.state.option.3 = High
channel-type.airparif.ash-level.label = Ash
channel-type.airparif.ash-level.state.option.0 = None
channel-type.airparif.ash-level.state.option.1 = Low
channel-type.airparif.ash-level.state.option.2 = Average
channel-type.airparif.ash-level.state.option.3 = High
channel-type.airparif.birch-level.label = Birch Level
channel-type.airparif.birch-level.state.option.0 = None
channel-type.airparif.birch-level.state.option.1 = Low
channel-type.airparif.birch-level.state.option.2 = Average
channel-type.airparif.birch-level.state.option.3 = High
channel-type.airparif.chestnut-level.label = Chestnut
channel-type.airparif.chestnut-level.state.option.0 = None
channel-type.airparif.chestnut-level.state.option.1 = Low
channel-type.airparif.chestnut-level.state.option.2 = Average
channel-type.airparif.chestnut-level.state.option.3 = High
channel-type.airparif.comment.label = Comment
channel-type.airparif.cypress-level.label = Cypress
channel-type.airparif.cypress-level.state.option.0 = None
channel-type.airparif.cypress-level.state.option.1 = Low
channel-type.airparif.cypress-level.state.option.2 = Average
channel-type.airparif.cypress-level.state.option.3 = High
channel-type.airparif.grasses-level.label = Grasses
channel-type.airparif.grasses-level.state.option.0 = None
channel-type.airparif.grasses-level.state.option.1 = Low
channel-type.airparif.grasses-level.state.option.2 = Average
channel-type.airparif.grasses-level.state.option.3 = High
channel-type.airparif.hazel-level.label = Hazel Level
channel-type.airparif.hazel-level.state.option.0 = None
channel-type.airparif.hazel-level.state.option.1 = Low
channel-type.airparif.hazel-level.state.option.2 = Average
channel-type.airparif.hazel-level.state.option.3 = High
channel-type.airparif.hornbeam-level.label = Hornbeam
channel-type.airparif.hornbeam-level.state.option.0 = None
channel-type.airparif.hornbeam-level.state.option.1 = Low
channel-type.airparif.hornbeam-level.state.option.2 = Average
channel-type.airparif.hornbeam-level.state.option.3 = High
channel-type.airparif.linden-level.label = Linden
channel-type.airparif.linden-level.state.option.0 = None
channel-type.airparif.linden-level.state.option.1 = Low
channel-type.airparif.linden-level.state.option.2 = Average
channel-type.airparif.linden-level.state.option.3 = High
channel-type.airparif.oak-level.label = Oak
channel-type.airparif.oak-level.state.option.0 = None
channel-type.airparif.oak-level.state.option.1 = Low
channel-type.airparif.oak-level.state.option.2 = Average
channel-type.airparif.oak-level.state.option.3 = High
channel-type.airparif.olive-level.label = Olive
channel-type.airparif.olive-level.state.option.0 = None
channel-type.airparif.olive-level.state.option.1 = Low
channel-type.airparif.olive-level.state.option.2 = Average
channel-type.airparif.olive-level.state.option.3 = High
channel-type.airparif.plane-level.label = Plane
channel-type.airparif.plane-level.state.option.0 = None
channel-type.airparif.plane-level.state.option.1 = Low
channel-type.airparif.plane-level.state.option.2 = Average
channel-type.airparif.plane-level.state.option.3 = High
channel-type.airparif.plantain-level.label = Plantain
channel-type.airparif.plantain-level.state.option.0 = None
channel-type.airparif.plantain-level.state.option.1 = Low
channel-type.airparif.plantain-level.state.option.2 = Average
channel-type.airparif.plantain-level.state.option.3 = High
channel-type.airparif.poplar-level.label = Poplar
channel-type.airparif.poplar-level.state.option.0 = None
channel-type.airparif.poplar-level.state.option.1 = Low
channel-type.airparif.poplar-level.state.option.2 = Average
channel-type.airparif.poplar-level.state.option.3 = High
channel-type.airparif.ragweed-level.label = Ragweed
channel-type.airparif.ragweed-level.state.option.0 = None
channel-type.airparif.ragweed-level.state.option.1 = Low
channel-type.airparif.ragweed-level.state.option.2 = Average
channel-type.airparif.ragweed-level.state.option.3 = High
channel-type.airparif.rumex-level.label = Rumex
channel-type.airparif.rumex-level.state.option.0 = None
channel-type.airparif.rumex-level.state.option.1 = Low
channel-type.airparif.rumex-level.state.option.2 = Average
channel-type.airparif.rumex-level.state.option.3 = High
channel-type.airparif.timestamp.label = Timestamp
channel-type.airparif.urticaceae-level.label = Urticacea
channel-type.airparif.urticaceae-level.state.option.0 = None
channel-type.airparif.urticaceae-level.state.option.1 = Low
channel-type.airparif.urticaceae-level.state.option.2 = Average
channel-type.airparif.urticaceae-level.state.option.3 = High
channel-type.airparif.willow-level.label = Willow
channel-type.airparif.willow-level.state.option.0 = None
channel-type.airparif.willow-level.state.option.1 = Low
channel-type.airparif.willow-level.state.option.2 = Average
channel-type.airparif.willow-level.state.option.3 = High
channel-type.airparif.wormwood-level.label = Wormwood
channel-type.airparif.wormwood-level.state.option.0 = None
channel-type.airparif.wormwood-level.state.option.1 = Low
channel-type.airparif.wormwood-level.state.option.2 = Average
channel-type.airparif.wormwood-level.state.option.3 = High
# thing types
thing-type.airparif.location.channel.end-validity.label = End Of Validity
thing-type.airparif.location.channel.end-validity.description = Current bulletin validity ending
# channel group types
channel-group-type.airparif.pollens-group.label = Pollen information for the region
channel-group-type.airparif.pollens-group.channel.begin-validity.label = Begin Validity
channel-group-type.airparif.pollens-group.channel.begin-validity.description = Current bulletin validity start
channel-group-type.airparif.pollens-group.channel.comment.label = Begin Validity
channel-group-type.airparif.pollens-group.channel.comment.description = Current bulletin validity start
channel-group-type.airparif.pollens-group.channel.end-validity.label = End Validity
channel-group-type.airparif.pollens-group.channel.end-validity.description = Current bulletin validity ending
# channel types
channel-type.airparif.alert-level.state.option.0 = Good
channel-type.airparif.alert-level.state.option.1 = Average
channel-type.airparif.alert-level.state.option.2 = Degrated
channel-type.airparif.alert-level.state.option.3 = Bad
channel-type.airparif.alert-level.state.option.4 = Extremely Bad
channel-type.airparif.avalanches.label = Avalanches
channel-type.airparif.avalanches.description = Avalanche alert level
channel-type.airparif.avalanches.state.option.0 = No special vigilance
channel-type.airparif.avalanches.state.option.1 = Be attentive
channel-type.airparif.avalanches.state.option.2 = Be very vigilant
channel-type.airparif.avalanches.state.option.3 = Absolute vigilance
channel-type.airparif.canicule.label = Heat Wave
channel-type.airparif.canicule.description = High temperature alert level
channel-type.airparif.canicule.state.option.0 = No special vigilance
channel-type.airparif.canicule.state.option.1 = Be attentive
channel-type.airparif.canicule.state.option.2 = Be very vigilant
channel-type.airparif.canicule.state.option.3 = Absolute vigilance
channel-type.airparif.condition-icon.label = Icon
channel-type.airparif.condition-icon.description = Pictogram associated with the alert level.
channel-type.airparif.grand-froid.label = Extreme Cold
channel-type.airparif.grand-froid.description = Negative temperature alert level
channel-type.airparif.grand-froid.state.option.0 = No special vigilance
channel-type.airparif.grand-froid.state.option.1 = Be attentive
channel-type.airparif.grand-froid.state.option.2 = Be very vigilant
channel-type.airparif.grand-froid.state.option.3 = Absolute vigilance
channel-type.airparif.inondation.label = Flood
channel-type.airparif.inondation.description = Flood alert level
channel-type.airparif.inondation.state.option.0 = No special vigilance
channel-type.airparif.inondation.state.option.1 = Be attentive
channel-type.airparif.inondation.state.option.2 = Be very vigilant
channel-type.airparif.inondation.state.option.3 = Absolute vigilance
channel-type.airparif.neige.label = Snow
channel-type.airparif.neige.description = Snow event alert level
channel-type.airparif.neige.state.option.0 = No special vigilance
channel-type.airparif.neige.state.option.1 = Be attentive
channel-type.airparif.neige.state.option.2 = Be very vigilant
channel-type.airparif.neige.state.option.3 = Absolute vigilance
channel-type.airparif.orage.label = Storm
channel-type.airparif.orage.description = Storm alert level
channel-type.airparif.orage.state.option.0 = No special vigilance
channel-type.airparif.orage.state.option.1 = Be attentive
channel-type.airparif.orage.state.option.2 = Be very vigilant
channel-type.airparif.orage.state.option.3 = Absolute vigilance
channel-type.airparif.pluie-inondation.label = Rain Flood
channel-type.airparif.pluie-inondation.description = Flood caused by rainfall alert level
channel-type.airparif.pluie-inondation.state.option.0 = No special vigilance
channel-type.airparif.pluie-inondation.state.option.1 = Be attentive
channel-type.airparif.pluie-inondation.state.option.2 = Be very vigilant
channel-type.airparif.pluie-inondation.state.option.3 = Absolute vigilance
channel-type.airparif.pollen-level.label = Pollen Level
channel-type.airparif.pollen-level.state.option.0 = None
channel-type.airparif.pollen-level.state.option.1 = Low
channel-type.airparif.pollen-level.state.option.2 = Average
channel-type.airparif.pollen-level.state.option.3 = High
channel-type.airparif.vague-submersion.label = Wave Submersion
channel-type.airparif.vague-submersion.description = Submersion wave alert level
channel-type.airparif.vague-submersion.state.option.0 = No special vigilance
channel-type.airparif.vague-submersion.state.option.1 = Be attentive
channel-type.airparif.vague-submersion.state.option.2 = Be very vigilant
channel-type.airparif.vague-submersion.state.option.3 = Absolute vigilance
channel-type.airparif.vent.label = Wind
channel-type.airparif.vent.description = Wind event alert level
channel-type.airparif.vent.state.option.0 = No special vigilance
channel-type.airparif.vent.state.option.1 = Be attentive
channel-type.airparif.vent.state.option.2 = Be very vigilant
channel-type.airparif.vent.state.option.3 = Absolute vigilance
# discovery result # discovery result
@ -12,3 +234,4 @@ iconset.description = Icons illustrating air quality measures provided by AirPar
offline.config-error-unknown-apikey = Parameter 'apikey' must be configured offline.config-error-unknown-apikey = Parameter 'apikey' must be configured
offline.config-error-invalid-apikey = Parameter 'apikey' is invalid offline.config-error-invalid-apikey = Parameter 'apikey' is invalid
incorrect-bridge = Wrong bridge type

View File

@ -10,6 +10,16 @@
Bridge to the AirParif API Portal. In order to receive the data, you must register an account on Bridge to the AirParif API Portal. In order to receive the data, you must register an account on
https://www.airparif.fr/contact and receive your API token. https://www.airparif.fr/contact and receive your API token.
</description> </description>
<channel-groups>
<channel-group id="pollens" typeId="bridge-pollens"/>
<channel-group id="aq-bulletin" typeId="air-quality-bulletin">
<label>Today's Air Quality Bulletin</label></channel-group>
<channel-group id="aq-bulletin-tomorrow" typeId="air-quality-bulletin">
<label>Tomorrow's Air Quality Bulletin</label></channel-group>
<channel-group id="daily" typeId="daily"/>
</channel-groups>
<config-description> <config-description>
<parameter name="apikey" type="text" required="true"> <parameter name="apikey" type="text" required="true">
<label>API Key</label> <label>API Key</label>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="airparif"
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-group-type id="bridge-pollens">
<label>Pollen information for the region</label>
<channels>
<channel id="comment" typeId="comment">
<label>Begin Validity</label>
<description>Current bulletin validity start</description>
</channel>
<channel id="begin-validity" typeId="timestamp">
<label>Begin Validity</label>
<description>Current bulletin validity start</description>
</channel>
<channel id="end-validity" typeId="timestamp">
<label>End Validity</label>
<description>Current bulletin validity ending</description>
</channel>
</channels>
</channel-group-type>
<channel-group-type id="air-quality-bulletin">
<label>Air Quality Bulletin</label>
<channels>
<channel id="comment" typeId="comment">
<label>Message</label>
<description>General message for the air quality bulletin</description>
</channel>
<channel id="no2-min" typeId="ppb-value">
<label>NO2 Min</label>
<description>Minimum level of NO2 concentation</description>
</channel>
<channel id="no2-max" typeId="ppb-value">
<label>NO2 Max</label>
<description>Maximum level of NO2 concentation</description>
</channel>
<channel id="o3-min" typeId="ppb-value">
<label>O3 Min</label>
<description>Minimum level of O3 concentation</description>
</channel>
<channel id="o3-max" typeId="ppb-value">
<label>O3 Max</label>
<description>Maximum level of O3 concentation</description>
</channel>
<channel id="pm10-min" typeId="mpc-value">
<label>PM 10 Min</label>
<description>Minimum level of PM 10 concentation</description>
</channel>
<channel id="pm10-max" typeId="mpc-value">
<label>PM 10 Max</label>
<description>Maximum level of PM 10 concentation</description>
</channel>
<channel id="pm25-min" typeId="mpc-value">
<label>PM 2.5 Min</label>
<description>Minimum level of PM 2.5 concentation</description>
</channel>
<channel id="pm25-max" typeId="mpc-value">
<label>PM 2.5 Max</label>
<description>Maximum level of PM 2.5 concentation</description>
</channel>
</channels>
</channel-group-type>
<channel-group-type id="daily">
<label>Daily information for the region</label>
<channels>
<channel id="message" typeId="comment">
<label>Message</label>
<description>Current bulletin validity start</description>
</channel>
<channel id="tomorrow" typeId="comment">
<label>Tomorrow</label>
<description>Current bulletin validity start</description>
</channel>
</channels>
</channel-group-type>
<channel-group-type id="dept-pollens">
<label>Pollen information for the department</label>
<channels>
<channel id="cypress" typeId="cypress-level"/>
<channel id="hazel" typeId="hazel-level"/>
<channel id="alder" typeId="alder-level"/>
<channel id="poplar" typeId="poplar-level"/>
<channel id="willow" typeId="willow-level"/>
<channel id="ash" typeId="ash-level"/>
<channel id="hornbeam" typeId="hornbeam-level"/>
<channel id="birch" typeId="birch-level"/>
<channel id="plane" typeId="plane-level"/>
<channel id="oak" typeId="oak-level"/>
<channel id="olive" typeId="olive-level"/>
<channel id="linden" typeId="linden-level"/>
<channel id="chestnut" typeId="chestnut-level"/>
<channel id="rumex" typeId="rumex-level"/>
<channel id="grasses" typeId="grasses-level"/>
<channel id="plantain" typeId="plantain-level"/>
<channel id="urticaceae" typeId="urticaceae-level"/>
<channel id="wormwood" typeId="wormwood-level"/>
<channel id="ragweed" typeId="ragweed-level"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@ -4,198 +4,13 @@
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" 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"> xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="alert-level"> <channel-type id="timestamp">
<item-type>Number</item-type>
<label>@text/alertLevelChannelLabel</label>
<description>@text/alertLevelChannelDescription</description>
<category>error</category>
<tags>
<tag>Alarm</tag>
</tags>
<state readOnly="true">
<options>
<option value="0">Good</option>
<option value="1">Average</option>
<option value="2">Degrated</option>
<option value="3">Bad</option>
<option value="4">Extremely Bad</option>
</options>
</state>
</channel-type>
<channel-type id="timestamp" advanced="true">
<item-type>DateTime</item-type> <item-type>DateTime</item-type>
<label>@text/timestampChannelLabel</label> <label>Timestamp</label>
<description>@text/timestampChannelDescription</description>
<category>time</category> <category>time</category>
<state readOnly="true"/> <state readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="dominent">
<item-type>String</item-type>
<label>@text/dominentChannelLabel</label>
<state readOnly="true">
<options>
<option value="pm25">@text/pollutantPm25</option>
<option value="pm10">@text/pollutantPm10</option>
<option value="o3">@text/pollutantO3</option>
<option value="no2">@text/pollutantNO2</option>
<option value="co">@text/pollutantCO</option>
<option value="so2">@text/pollutantSO2</option>
</options>
</state>
</channel-type>
<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"> <channel-type id="comment">
<item-type>String</item-type> <item-type>String</item-type>
<label>Comment</label> <label>Comment</label>
@ -203,18 +18,290 @@
<state readOnly="true" pattern="%s"/> <state readOnly="true" pattern="%s"/>
</channel-type> </channel-type>
<channel-type id="timestamp" advanced="true"> <channel-type id="timestamp" advanced="true">
<item-type>DateTime</item-type> <item-type>DateTime</item-type>
<label>Observation Timestamp</label> <label>@text/timestampChannelLabel</label>
<description>@text/timestampChannelDescription</description>
<category>time</category> <category>time</category>
<state readOnly="true"/> <state readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="condition-icon"> <channel-type id="hazel-level">
<item-type>Image</item-type> <item-type>Number</item-type>
<label>Icon</label> <label>Hazel Level</label>
<description>Pictogram associated with the alert level.</description> <category>oh:airparif:hazel</category>
<state readOnly="true"/> <state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type> </channel-type>
<channel-type id="birch-level">
<item-type>Number</item-type>
<label>Birch Level</label>
<category>oh:airparif:birch</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="cypress-level">
<item-type>Number</item-type>
<label>Cypress</label>
<category>oh:airparif:cypress</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="alder-level">
<item-type>Number</item-type>
<label>Alder</label>
<category>oh:airparif:alder</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="poplar-level">
<item-type>Number</item-type>
<label>Poplar</label>
<category>oh:airparif:poplar</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="ash-level">
<item-type>Number</item-type>
<label>Ash</label>
<category>oh:airparif:ash</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="olive-level">
<item-type>Number</item-type>
<label>Olive</label>
<category>oh:airparif:olive</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="urticaceae-level">
<item-type>Number</item-type>
<label>Urticacea</label>
<category>oh:airparif:urticacea</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="wormwood-level">
<item-type>Number</item-type>
<label>Wormwood</label>
<category>oh:airparif:wormwood</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="rumex-level">
<item-type>Number</item-type>
<label>Rumex</label>
<category>oh:airparif:rumex</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="ragweed-level">
<item-type>Number</item-type>
<label>Ragweed</label>
<category>oh:airparif:ragweed</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="grasses-level">
<item-type>Number</item-type>
<label>Grasses</label>
<category>oh:airparif:grasses</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="plantain-level">
<item-type>Number</item-type>
<label>Plantain</label>
<category>oh:airparif:plantain</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="chestnut-level">
<item-type>Number</item-type>
<label>Chestnut</label>
<category>oh:airparif:chestnut</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="oak-level">
<item-type>Number</item-type>
<label>Oak</label>
<category>oh:airparif:oak</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="linden-level">
<item-type>Number</item-type>
<label>Linden</label>
<category>oh:airparif:linden</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="plane-level">
<item-type>Number</item-type>
<label>Plane</label>
<category>oh:airparif:plane</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="hornbeam-level">
<item-type>Number</item-type>
<label>Hornbeam</label>
<category>oh:airparif:hornbeam</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="willow-level">
<item-type>Number</item-type>
<label>Willow</label>
<category>oh:airparif:willow</category>
<state readOnly="true">
<options>
<option value="0">None</option>
<option value="1">Low</option>
<option value="2">Average</option>
<option value="3">High</option>
</options>
</state>
</channel-type>
<channel-type id="ppb-value">
<item-type unitHint="ppb">Number:Dimensionless</item-type>
<label>Measure</label>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="mpc-value">
<item-type unitHint="µg/m³">Number:Density</item-type>
<label>Measure</label>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -9,12 +9,14 @@
<bridge-type-ref id="api"/> <bridge-type-ref id="api"/>
</supported-bridge-type-refs> </supported-bridge-type-refs>
<label>Air Quality Report</label> <label>Department Report</label>
<description>AirParif air quality report for the given location</description> <description>AirParif air quality report for the given location</description>
<channels> <channel-groups>
<channel id="channel1" typeId="sample-channel"/> <channel-group id="pollens" typeId="dept-pollens"/>
</channels> </channel-groups>
<representation-property>department</representation-property>
<config-description> <config-description>
<parameter name="location" type="text" required="false" <parameter name="location" type="text" required="false"
@ -22,6 +24,22 @@
<context>location</context> <context>location</context>
<label>Location</label> <label>Location</label>
</parameter> </parameter>
<parameter name="department" type="text" required="true">
<label>Department</label>
<description>Name of the department</description>
<options>
<option value="75">Paris</option>
<option value="77">Seine et Marne</option>
<option value="78">Yvelines</option>
<option value="91">Essonne</option>
<option value="92">Hauts de Seine</option>
<option value="93">Seine Saint Denis</option>
<option value="94">Val de Marne</option>
<option value="95">Val D'Oise</option>
</options>
<limitToOptions>true</limitToOptions>
</parameter>
</config-description> </config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,5 @@
<svg viewBox="3.405 0 505.19 512" width="505.19" height="512" xmlns="http://www.w3.org/2000/svg">
<path
d="M508.595 192.58l-45.648-14.463c30.295-30.936 34.154-86.926 34.154-86.926l-28.944.977C488.695 69.363 497.809 0 497.809 0c-55.727 17.085-90.667 41.179-90.667 41.179l-5.181-39.858s-50.198 15.783-76.733 61.705L300.299.743s-97.198 31.28-112.463 131.032c0 0 1.519-46.103-45.239-99.981 0 0-208.69 199.651-42.876 368.772-18.236 11.225-42.413 35.717-42.413 35.717l-14.096 14.163s-49.174 48.519-38.214 60.032c10.314 10.838 60.261-39.771 60.261-39.771l14.182-14.248s24.201-23.89 34.881-42.06c152.638 132.448 363.507-61.003 363.507-61.003l-49.541-39.92c49.496-32.655 80.306-120.895 80.306-120.895zm-76.926 159.094c.764 2.426-13.907 8.581-36.795 13.475-22.836 5.034-53.584 9.161-84.36 12.168-61.569 6.072-123.283 8.112-123.283 8.112l-35.681 1.224-14.294.375c-10.183 7.811-17.241 11.82-19.205 9.893-2.254-2.213 2.181-10.312 10.699-21.863l-.908-20.772s-2.578-63.05-2.252-126.072c.144-63.024 2.752-126.019 7.778-125.992 5.26.029 7.642 61.556 9.868 123.036l4.333 122.877.135 3.204c11.53-13.692 25.246-29.115 39.134-44.385a3391.444 3391.444 0 0130.632-33.149l3.018-17.925s32.056-191.626 41.891-189.97c9.207 1.55-14.511 147.154-20.289 181.803 13.091-13.762 21.734-22.677 21.734-22.677l45.049-46.824s8.237-8.594 21.038-21.75l2.045-8.616 1.606-6.232 1.95-7.213c2.907-10.55 7.08-24.481 11.692-38.259 9.206-27.561 20.209-54.494 24.975-52.992 5.043 1.59-.974 29.301-7.641 56.732l-7.17 28.556a4201.89 4201.89 0 0132.005-32.104c20.372-20.223 40.981-40.2 57.091-54.572 16.081-14.377 27.588-23.23 30.428-20.767 2.986 2.589-4.164 15.17-16.907 32.202-12.751 17-30.98 38.53-49.666 59.598a2441.69 2441.69 0 01-27.665 30.633 3330.047 3330.047 0 0124.655-6.895c27.828-7.517 55.636-15.031 57.383-10.39 1.877 4.982-24.071 17.133-50.822 26.866-26.707 9.847-54.014 17.719-54.014 17.719l-3.503 1.003c-13.489 14.344-22.501 23.67-22.501 23.67l-45.095 46.883s-7.031 7.311-18.226 18.731c17.84-5.185 42.853-12.275 68.107-18.837 23.552-6.127 47.264-11.76 65.325-15.003 18.053-3.295 30.467-4.02 30.949-1.598.527 2.623-10.565 6.969-27.369 12.853-16.818 5.84-39.41 13.071-62.054 20.183-45.288 14.21-90.773 27.861-90.773 27.861l-13.463 4.09c-10.539 10.521-22.052 21.881-33.683 33.125-15.718 15.214-31.653 30.199-45.667 42.65l19.215-.641s62.758-2.16 125.345-5.791c31.29-1.812 62.505-4.112 85.805-6.299 23.316-2.046 38.646-4.425 39.402-1.934z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,5 @@
<svg viewBox="5.055 8.917 89.89 82.196" width="89.89" height="82.196" xmlns="http://www.w3.org/2000/svg">
<path
d="M47.457 63.891h-.234c-.918.195-1.82.43-2.778.695l-.515.153c-.89.265-1.778.57-2.653.918l-.445.18c-.89.359-1.766.765-2.625 1.195l-.293.152a34.28 34.28 0 00-2.668 1.484 41.828 41.828 0 002.668 1.5l.293.141v-.004a36.684 36.684 0 009.016 3.113h.168c.875-3.113.898-6.402.066-9.527zm.305 12.5h.004a38.625 38.625 0 01-5.25-1.39 39.021 39.021 0 01-4.848-1.93l-1.152-.598-.778-.402c-.418-.223-.804-.457-1.222-.696l-.653-.402c-3.25-1.777-3.234-2.89 0-4.668a46.42 46.42 0 012.64-1.5l1.169-.598a57 57 0 014.027-1.668c1.375-.5 2.777-.875 4.168-1.222l.918-.196.984-.195-.003.004a18.266 18.266 0 00-3.25-4.723 1.48 1.48 0 01-.309-1.386l1.168-3.778-3.293 2.055c-.43.246-.957.246-1.387 0a18.057 18.057 0 00-15.14-1.527c-.423.152-.895.09-1.262-.168l-4.016-2.793 1.305 3.652v.004c.215.59.004 1.25-.512 1.61a17.232 17.232 0 00-3.418 3.152c-.375.449-.992.617-1.543.418l-3.723-1.293 2.39 4.168a1.46 1.46 0 010 1.25 17.533 17.533 0 00-1.25 4.414c-.077.52-.444.949-.944 1.113l-4.637 1.555 4.695 1.57c.496.16.863.59.942 1.11a16.93 16.93 0 001.113 4.027c.187.468.105 1-.211 1.39l-2.61 3.39 4.044-.82a1.39 1.39 0 011.386.512 17.58 17.58 0 002.528 2.614c.406.336.582.875.46 1.386l-1.027 4.723 4.043-2.984h-.004c.399-.309.93-.375 1.39-.18a17.99 17.99 0 004.321 1.164c.504.07.93.41 1.11.89l1.39 3.669 1.39-3.668h-.003c.18-.48.605-.82 1.113-.891a18.052 18.052 0 007.29-2.707 1.392 1.392 0 011.554 0l3.75 2.695-1.695-4.722h.004a1.398 1.398 0 01.261-1.391 18.266 18.266 0 002.586-4.04zm-13.04-20.598l3.903-.32c.371-.028.738.086 1.027.32.282.242.45.586.473.957l.332 3.902a1.37 1.37 0 01-.332 1.02 1.365 1.365 0 01-.957.48 1.392 1.392 0 01-1.488-1.261l-.18-2.559-2.527.207v.004a1.397 1.397 0 01-1.5-1.277 1.39 1.39 0 011.277-1.5zm-11.108.695l-.004-.004A1.39 1.39 0 0125 55.098h3.93a1.386 1.386 0 011.39 1.386 1.386 1.386 0 01-1.39 1.39l-2.543.001v2.543h.004a1.39 1.39 0 01-2.781 0zm-3 12.5l-.004-.004a1.4 1.4 0 11-2 1.961l-2.778-2.777a1.391 1.391 0 010-1.96l2.778-2.778a1.392 1.392 0 011.933 1.988l-1.804 1.777zm6.945 11l-3.848.793-.004-.004a1.391 1.391 0 01-1.484-1.054l-.793-3.848a1.419 1.419 0 011.113-1.668 1.414 1.414 0 011.664 1.113l.403 2.457 2.484-.515.004.004a1.4 1.4 0 011.652 1.093c.153.762-.336 1.5-1.097 1.657zm2.777-9.57h-.004a1.395 1.395 0 11-2 1.941l-2.777-2.777c-.262-.258-.41-.613-.41-.984s.148-.727.41-.989l2.777-2.777c.254-.289.621-.465 1.008-.476.387-.012.758.136 1.031.414.27.277.414.652.399 1.039-.02.387-.196.746-.492.996l-1.668 1.82zM39.06 82.25h-.004c-.25.273-.601.434-.973.445l-3.918.153h.004c-.37.015-.73-.118-1-.368s-.43-.597-.445-.964a1.39 1.39 0 011.332-1.446l2.543-.097-.098-2.543a1.391 1.391 0 011.34-1.438 1.391 1.391 0 011.438 1.34l.168 3.918c.03.375-.09.746-.336 1.027zm-5.324-41.391h-2.528a12.664 12.664 0 011.239 6.945h1.36c.538-2.285.515-4.668-.071-6.945zm36.984 1.223a66.8 66.8 0 00-1.555 2.777h.004c.945.836 1.8 1.77 2.555 2.781a16.084 16.084 0 011.554-2.89 58.845 58.845 0 00-2.554-2.668zm.777 17.75c-.152-.375-.277-.778-.418-1.168 0-.153-.097-.305-.125-.446l.004.004c-.21-.75-.36-1.515-.441-2.293v-.097c0-.391 0-.793-.082-1.196-.082-.402 0-.82 0-1.222v-.473l-.004.004c.035-.578.105-1.152.21-1.723a14.158 14.158 0 00-2.78-3.848 51.885 51.885 0 00-2.083 5.168l2.778 3.082c1.207 1.391-.848 3.235-2.055 1.875l-1.64-1.859c-.333 1.207-.641 2.418-.848 3.625l2.89 3.234a1.39 1.39 0 11-2.054 1.875l-1.39-1.543c-.266 1.293.292 3.805-1.68 3.793-2.235-.375-.946-3.304-.973-4.804l-2.473.86a1.388 1.388 0 01-.93-2.61l3.957-1.39a35.6 35.6 0 01.82-3.583l-2.167.777h.004c-.707.2-1.446-.18-1.692-.87a1.385 1.385 0 01.758-1.739l4.168-1.473a54.505 54.505 0 011.875-4.804v-.004a13.388 13.388 0 00-4.957 23.391 13.485 13.485 0 0011.5-10.098 3.063 3.063 0 00-.168-.446zm17.848-7.582h.004a13.498 13.498 0 00-11.57-6.418c1.098 1.39 2.14 2.777 3.098 4.25l4.332.195c.77.031 1.367.68 1.336 1.445s-.68 1.364-1.446 1.332l-2.418-.11a41.365 41.365 0 011.805 3.278l4.168.196a1.388 1.388 0 011.332 1.44 1.386 1.386 0 01-1.441 1.337l-2.778-.125c.403 1.39 2.375 3.805.391 4.75-1.805.582-2.055-1.832-2.64-2.957l-.86 1.832h-.004a1.396 1.396 0 01-1.855.66 1.393 1.393 0 01-.657-1.855l1.832-3.903a48.285 48.285 0 00-1.765-3.234l-1.028 2.195v-.004a1.388 1.388 0 01-2.511-1.18l1.793-3.792a37.377 37.377 0 00-3.32-4.625 13.61 13.61 0 00-1.657 4.347 12.855 12.855 0 00.918 8.25 13.413 13.413 0 0014.168 7.555 13.527 13.527 0 00.778-14.86zm-9.527-19.027h.004c-.078-.442.062-.89.375-1.207l14.75-14.81-.25-.538L77.68 28.125a1.292 1.292 0 01-.762.234H43.723c-.457 0-.883-.227-1.144-.602a1.394 1.394 0 01-.149-1.285l6.766-17.555h-.918L36.11 27.777a1.38 1.38 0 01-1.61.57L5.055 18.429v7.96l13.223 11.734c.348 0 58.652-.082 58.945 0l17.223 3.957.43-.957-14.32-6.875a1.387 1.387 0 01-.734-1.027z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -0,0 +1,17 @@
<svg viewBox="15.935 5.445 68.13 89.11" width="68.13" height="89.11" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M80.662 49.972c-.26-.288.051-.502.155-.747.172-.4.519-.828.29-1.247-.269-.486-.809-.18-1.231-.204-.159-.009-.324.039-.483.069-.252.047-.477-.002-.621-.22-.137-.206-.039-.393.078-.584.295-.485.573-.98.979-1.676-3.034 1.36-6.009 1.702-9.084 1.608-3.025-.091-6.054-.02-9.082-.02 1.96-.932 4.021-.892 6.017-.813 3.194.126 6.33.084 9.458-.698 2.455-.613 3.195-1.883 3.573-3.897-1.426.011-1.534-.54-.72-1.599.414-.539.637-1.224 1.038-2.026-.323.097-.413.113-.493.151-4.033 1.898-8.266 3.355-12.078 5.744-3.014 1.887-6.536 2.457-9.909 3.372-3.347.907-6.668 1.993-10.213 1.779-.726-.043-.871.487-.97 1.043-.76 4.301-1.479 8.612-2.307 12.9-.232 1.195.31 1.174 1.132 1.07 4.445-.562 8.827-1.419 13.143-2.632 6.204-1.744 12.361-3.587 17.819-7.178 1.974-1.298 4.265-2.133 5.901-4.05-.561-.179-1.078-.134-1.597-.031-.28.056-.585.121-.795-.114z"
fill="#3d3c3c" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M80.284 61.983c-.571-.847-1.542-.605-2.352-.832-2.944-.82-5.998-.823-8.99-1.273-2.399-.362-4.527.975-6.794 1.474-3.32.731-6.562 1.792-9.929 2.366-2.234.381-4.478.649-6.729.865-.469.045-.788.235-.852.755-.219 1.807-.417 3.618-.69 5.417-.115.756.19.915.845.971 1.725.147 3.442.365 5.167.531 3.767.361 7.581.55 11.226-.52 4.623-1.357 9.363-2.172 14.012-3.392 1.503-.395 3.062-.57 4.728-.868-.15-.241-.207-.401-.316-.499-.796-.711-.711-1.122.385-1.283.795-.117 1.46-.361.809-1.372-.078-.119-.11-.381-.036-.464.771-.875-.109-1.321-.484-1.876zM44.42 58.148c.038-.117.137-.327.17-.547.41-2.8.773-5.608 1.231-8.399.205-1.25.353-2.401-.4-3.563-.722-1.114-1.373-2.3-1.862-3.53-.572-1.44-1.538-2.47-2.656-3.494-1.842-1.687-3.828-3.218-5.428-5.18-1.447-1.778-3.025-3.449-4.246-5.404-.234-.375-.566-.687-1.029-.588-.479.103-.4.57-.418.934-.022.462-.18.65-.665.444-.658-.278-.705.173-.582.601.263.915-.594 1.669-.149 2.55-.499.086-.66-.194-.884-.339-.557-.357-1.287-.937-1.782-.554-.915.709-1.458.051-2.145-.225-.184-.074-.392-.087-.873-.188 4.317 11.665 11.68 20.615 21.718 27.482zM72.833 28.93c-6.113 4.511-13.018 7.141-20.385 8.71-1.25.266-2.552.278-2.93 2.132-.58 2.847-1.377 5.65-1.827 8.624.354 0 .627.017.898-.003 2.45-.187 4.85-.696 7.211-1.341 4.444-1.214 9.014-2.088 12.979-4.655 2.587-1.675 5.421-2.815 8.291-3.906 1.521-.579 2.93-1.461 4.479-2.253-.229-.176-.293-.246-.372-.284-.684-.325-1.892-.15-1.425-1.564.069-.21-.289-.338-.519-.385-1.27-.26-2.566-.489-1.354-2.355-.678-.05-1.185-.142-1.684-.111-.914.059-1.154-.417-.931-1.165.168-.559.077-1.223-.286-1.369-.646-.262-1.486-.563-2.145-.075zM44.06 60.556c.189-.955.132-1.647-.789-2.324-2.92-2.145-5.837-4.308-8.446-6.84-.694-.676-1.343-1.372-2.392-1.445a276.153 276.153 0 01-3.845-.292c-3.26-.273-6.436-.752-8.717-3.491-.343-.411-.833-.914-1.364-.471-.563.469.107.931.324 1.314.453.806 1.207 1.613-.609 1.393-.163-.02-.344.108-.71.234 1.139 1.145 2.163 2.188 3.199 3.219 2.924 2.902 6.11 5.535 8.771 8.706 3.305 3.938 7.34 6.986 11.756 9.571.804.471.976.192 1.112-.558.55-3.01 1.116-6.015 1.71-9.016z"
fill="#3d3c3c" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M41.294 70.814c-2.712-1.327-5.277-2.869-7.628-4.817-3.338-2.764-6.028-6.15-9.143-9.129-2.726-2.609-5.388-5.295-8.11-7.914-.094-.089-.218-.146-.478-.315.046.315.036.46.089.576.631 1.377 1.27 2.75 1.908 4.123.161.348.381.705-.211.866-.467.128-1.156.14-1.203.672-.048.539.583.837 1.019 1.135.618.423.646.896.018 1.25-.997.563-.638.918.009 1.513.617.569 2.171.888.979 2.294-.043.05.115.337.238.45.378.348.777.674 1.183.992.641.501.889.996.138 1.64-.442.379-.442.865-.005 1.28a50.96 50.96 0 001.866 1.701c.505.434.511.742-.082 1.125-.397.255-1.051.703-.493 1.199 2.595 2.3 5.562 3.974 9.02 4.503 2.864.438 5.762.667 8.649.945 2.726.264 2.72.239 2.881-2.617.033-.615.115-1.1-.644-1.472zM75.768 69.536c-4.059.548-7.978 1.726-11.887 2.851-4.186 1.206-8.363 1.387-12.621.911-2.482-.278-4.952-.655-7.485-.995-.265 1.49-.507 2.983-.803 4.465-.121.601.021.849.655.92 4.376.499 7.862 2.772 11.102 5.563 1.144.985 2.393 1.839 3.859 2.336.443-1.935.445-1.976 1.68-1.906.805.044 1.004-.381.947-1.022-.07-.808.253-1.147 1.049-1.035.726.103 1.165-.102 1.36-.901.171-.69.388-1.49 1.434-1.35.252.033.447-.177.499-.423.246-1.217.954-1.838 2.22-1.852.35-.004.697-.182 1.048-.281-.354-1.054-.338-.979.737-1.206.718-.151 1.851.057 1.676-1.347-.04-.338.438-.427.753-.525.443-.139.907-.232 1.322-.431.513-.244 1.057-.505.507-1.258-.27-.369.15-.493.42-.63.896-.458 1.927-.737 2.5-1.89-.394.002-.688-.032-.972.006zM55.38 25.95c-1.446.633-1.938 1.485-2.31 2.738-.227.761-.484 1.512-.725 2.269l-2.041 6.471c.285 0 .453.038.596-.006 3.095-.942 6.218-1.8 9.271-2.865 4.419-1.54 8.398-3.961 12.301-6.505.928-.605 1.37-1.452 1.597-2.633-1.301.181-2.441.294-1.981-1.496.177-.691-.111-1.064-.688-1.587-.66-.597-.886-1.682-1.771-2.206-.043-.025-.011-.177-.014-.271-.023-.558-.025-1.118-.086-1.671-.021-.21-.128-.472-.286-.592-.225-.167-.421.075-.61.205-1.043.722-1.85 1.707-2.806 2.517-3.075 2.607-6.828 4.051-10.447 5.632zM44.713 42.849c.392 1.043.943 2.026 1.512 3.231.538-2.265 1.023-4.281 1.489-6.302.219-.943.721-1.783.182-2.879-2.855-5.796-4.396-11.943-4.636-18.41-.046-1.197-.139-2.394-.229-3.945-.371.48-.557.656-.662.869-.407.823-.791 1.577-1.887 1.744-.761.117-1.438.556-1.349 1.567.024.276-.158.47-.52.292-1.078-.531-1.553-.182-1.499 1.042.142 3.301 1.217 6.404 2.075 9.542 1.27 4.641 3.84 8.764 5.524 13.249z"
fill="#3d3c3c" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M38.857 75.502c-3.219-.408-6.523-.146-9.667-1.121-1.56-.483-3.053-1.177-4.585-1.747-.759-.282-1.374.098-1.739.693-.394.645.233.978.671 1.248 1.109.689 2.26 1.314 3.378 1.991.343.209.627.505.097.851-.241.158-.689.176-.636.553.058.393.493.441.82.557.353.125.694.284 1.053.389.62.182.749.445.194.877-.478.372-.434.672.166.772.744.123 1.499.176 2.309.266l-.252.856c1.817.269 3.37-.535 5.183-1.229-.475.933-1.425 1.046-1.51 2.045.752-.183 1.426-.314 2.079-.512 1.238-.374 1.106.312.83 1.097 1.757-.12 1.768-.119 1.535 1.717-.371 2.952-.468 5.921-.575 8.89-.012.319-.08.821.376.858.438.035.399-.448.439-.753.435-3.219.828-6.446 1.298-9.662.288-1.984.81-3.925.968-5.942.237-3.025.528-2.319-2.432-2.694zM50.389 30.489c.427-1.568 1.367-3.062 1.123-4.736-.573-3.925-1.043-7.839-.547-11.812.081-.634-.012-1.525-.643-1.722-.728-.227-.843.684-1.138 1.181-.048.081-.099.16-.137.246-.27.614-.593.845-1.081.178-.26-.351-.58-.402-.721.062-.387 1.287-1.036 1.213-1.879.43-.211-.196-.448-.365-.938-.758-.55 8.112 1.021 15.581 4.14 23.167.676-2.304 1.285-4.261 1.821-6.236zM57.333 17.637a.646.646 0 00-.283.301c-1.037 2.414-2.032 4.844-2.906 7.651 1.173-.611 2.104-1.113 3.048-1.584 3.415-1.704 6.807-3.447 9.582-6.147.69-.672 1.219-1.439 1.298-2.586-1.639-.049-2.711-.675-2.146-2.858-2.933 2.166-5.666 3.856-8.593 5.223zM19.06 44.713c3.158 2.344 6.536 4.144 10.604 4.25.912.023 1.82.207 2.904.338-1.2-1.461-2.257-2.76-3.328-4.046-.592-.711-.885-1.616-1.979-1.955-2.807-.869-5.993-.88-8.128-3.381-.047-.056-.168-.049-.321-.089-.077.788.302 1.346.579 1.927.304.639.159.944-.575.886-.322-.025-.63.021-.802.319-.177.31-.018.606.174.842.265.325.539.662.872.909zM36.668 33.343c1.631 1.81 3.519 3.389 5.179 4.961-.013-.027-.103-.254-.218-.469-1.002-1.877-1.924-3.796-2.638-5.801-1.226-3.43-2.235-6.923-2.831-10.523-.043-.254-.097-.54-.415-.583-.34-.046-.396.245-.502.48-.143.312-.318.87-.48.87-1.623-.002-1.273 1.241-1.399 2.153-.064.469-.183.701-.702.539-.617-.19-.897.076-.981.707-.09.672.099 1.226.445 1.788 1.305 2.122 2.888 4.044 4.542 5.878zM83.255 52.723c.248-.488.825-.816.81-1.683-4.837 3.186-9.45 6.447-14.859 8.273 3.395.254 6.703.793 10.015 1.3 1.221.187 2.432.287 2.39-1.558-.006-.247.284-.515.47-.747.622-.782 1.078-1.535-.058-2.284-.306-.201-.475-.5-.217-.902.505-.787 1.026-1.567 1.449-2.399zM60.947 5.445c-.41.264-.568.309-.629.412-.868 1.485-2.854 1.857-3.521 3.553-.114.289-.504.292-.762.205-.9-.307-1.22.176-1.41.933-.204.819-.687 1.015-1.402.545-.442-.289-.641-.13-.81.335-.399 1.101-.461 2.236-.467 3.389-.016 3.056-.075 6.112.44 9.497 2.302-6.665 5.652-12.491 8.561-18.869zM44.254 78.382c-.902-.257-1.55-.248-1.635.926-.05.672-.247 1.335-.29 2.007-.067 1.009 1.04.646 1.444 1.147.153.194.423.392.742.275.055-.02.122-.096.127-.152.124-1.17.697-.517 1.117-.255.787.494 1.723 1.605 2.281 1.433 1.214-.373 1.342.569 1.892.981.442.331.812.755 1.235 1.156.347-.371.677-.552 1.041-.142.511.574.934.7 1.789.358.872-.351 1.994-.078 3.381-.078-1.223-.852-2.156-1.396-2.965-2.083-3.008-2.557-6.342-4.491-10.159-5.573z"
fill="#3d3c3c" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M20.169 39.445c1.848 2.209 4.609 2.257 7.354 3.285-2.375-3.695-4.059-7.395-5.891-11.306-.587 1.646-.587 1.646-1.831 2.865-.048.047-.115.119-.11.172.127.98.231 1.951-.56 2.745-.032.032-.003.137.013.205a4.842 4.842 0 001.025 2.034zM63.75 12.76c.577-.341.383-.912.345-1.399-.037-.5-.453-.24-.719-.263-.447-.038-.565-.375-.482-.698.32-1.259-.072-2.14-1.332-2.624l-3.815 8.521c2.192-1.291 4.1-2.41 6.003-3.537z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,11 @@
<svg viewBox="14.777 7.042 71.004 88.309" width="71.004" height="88.309" xmlns="http://www.w3.org/2000/svg">
<path
d="M50.016 43.426c.402 3.52.953 7.066.883 10.613 2.527-3.3 5.644-7.14 8.039-10.5-1.61-7.406-2.856-16.398.855-23.395.18-.293.364-.574.575-.844-3.372 7.383-2.45 15.863-.942 23.602 2.352-3.133 4.64-6.324 6.777-9.605-1.363-4.492-2.539-10.164-.69-14.676-1.032 4.418-.333 9.761 1.108 14.027 1.735-2.414 3.438-4.922 4.95-7.473-.543-2.484.101-7.062.965-9.477-.32 2.918-.766 5.848-.504 8.793 1.011-1.335 9.746-16.262 9.71-17.008-3.09 2.95-6.456 5.704-10.354 7.512-1.915.906-4.004 1.367-5.81 2.496-.468.285-1.147.895-1.667.98-.457-.288-.223-1.28-.234-1.75-1.098.259-3.008.872-3.84 1.598-.317.274-.992 1.309-1.235 1.422-.761.254-.828-1.863-.863-2.328-2.043.871-4.023 1.59-4.926 3.801-.136.34-.246.691-.328 1.047.211-.274.414-.555.61-.84-3.192 6.848-4.036 14.551-3.079 22.004zM28.766 82.57c-2.656 3.508-4.934 7.36-7.09 11.164.168.5.367.984.602 1.453l.078.152.066.012c1.438-3.289 2.934-6.617 4.88-9.66.316-.531 1.359-2.098 1.651-2.602.106-.199-.03-.402-.19-.52z"
fill="#3d3c3c" />
<path
d="M83.816 48.441c1.266-1.281 1.633-3.621 1.965-5.328-.563.121-1.606.414-2.13.09l-.046-.11c.094-.234.645-.71.844-1.039.562-.941.695-2.433.902-3.515-.383.136-.957.359-1.363.226l-.063-.066c.192-.574.567-1.094.766-1.684.527-1.543.457-3.398.441-5.011-.582.347-1.136.55-1.789.707.153-.47.473-.856.614-1.329.386-1.28.078-3.52-.028-4.894-.113.129-.226.293-.39.348-.168-.051-.215-.121-.301-.274-.45-.824-.582-1.867-.77-2.781-.894-4.324-1.18-8.32-.8-12.723.039-.477.519-3.852.492-4.016-3.012 7.407-7.078 14.328-11.457 21.004 2.632-.851 7.285-4.882 9.433-6.804-.511 1.48-5.238 5.09-6.652 6.047-1.016.691-2.106 1.304-3.328 1.539-1.793 2.55-3.485 5.183-5.262 7.742 6.445-.75 12.41-3.606 17.52-7.504-3.851 4.504-12.14 7.676-17.94 8.207-2.454 3.41-5 6.781-7.419 10.211 10.891.363 18.25-5.11 26.711-11.051-.746 1.144-2.52 2.273-3.64 3.082-7.028 5.39-14.504 9.125-23.61 8.61-1.34 1.788-4.406 5.675-5.69 7.425 8.609-.726 17.577-1.5 25.39-5.492 2.438-1.27 4.555-3.078 6.797-4.625-7.969 8.781-21.504 10.223-32.727 10.812-2.195 2.832-4.39 5.672-6.633 8.469 6.324-1.559 12.906-.871 19.324-1.723 7.121-1.008 14.191-3.844 19.61-8.637-1.657 2.653-7.376 5.692-10.263 6.84-2.855 1.125-5.847 1.89-8.875 2.383-6.5 1.113-13.277.187-19.672 1.914-1.386.27-1.847 1.48-2.96 2.766-1.532 1.8-3.012 3.644-4.575 5.422 1.27-.446 2.508-.856 3.824-1.16 3.899-.727 7.774-.133 11.672.273 9.672 1.281 17.652-1.535 26.06-6.207-1.305 1.289-3.16 2.203-4.801 2.996-3.38 1.629-6.977 3.07-10.68 3.77-8.551 1.773-16.86-2.106-25.223 1.023-.864.324-1.899.695-2.559 1.348-.668.656-1.371 1.593-1.941 2.351 4.02-.793 8.07-.219 11.949.973 8.023 2.328 14.578 3.578 22.504-.098-.625.617-1.399 1.008-2.2 1.36-5.664 2.507-12.129 1.925-17.949.296-5.015-1.449-10.3-2.906-15.449-1.218-.433.453-.828.957-1.214 1.453 8.793-.035 16.078 5.375 24.133 8.047-2.274.062-4.84-1.075-6.907-1.957-5.773-2.454-11.297-5.43-17.785-5.137 2.77 4.719 6.961 8.941 12.262 10.652 2.594.805 4.856.644 7.485.289-.43-.469-1.453-1.516-.36-1.844 2.828.98 5.688 1.211 8.446-.14-.364-.38-1.325-1.122-1.309-1.649-.012-.308.277-.55.57-.547.465.106.961.137 1.438.055 1.765-.238 4.937-1.93 6.348-3.04-.422-.144-1.25-.339-1.45-.765-.12-.254.043-.57.301-.66.399-.14 1.375-.184 1.922-.316 1.695-.41 3.324-1.485 4.691-2.54-.609-.37-2.32-1.09-2.277-1.914.047-.078.09-.171.184-.199.418-.129 1.242-.113 1.722-.203 1.672-.234 2.844-1.262 4.032-2.375-.418-.176-.942-.59-.688-1.094.48-.351 1.059-.52 1.547-.898 1.07-.828 3.277-3.735 3.984-4.95a26.598 26.598 0 01-2.347-.413c2.289-1.344 3.898-3.426 5.238-5.668-.66.023-1.965.386-2.047-.579.055-.417 1.414-1.425 1.793-1.8 1.031-1.012 1.844-2.258 2.281-3.64-.62.066-1.41.206-1.468-.638.359-.773 1.05-1.347 1.5-2.07 1.043-1.68 1.488-3.644 1.988-5.535-1.18.129-2.227.363-3.305.871.38-.703 1.11-1.207 1.664-1.77z"
fill="#3d3c3c" />
<path
d="M25.734 63.277c-2.977-6.41-3.668-14.234-1.246-20.887l.031.02c-.137 2.058-.926 4.187-.953 6.289-.32 4.843.84 9.629 2.719 14.074.758 1.675 1.695 3.265 2.476 4.933 1.411 3.141 2.309 6.375 2.793 9.785.618-.793 1.957-2.21 2.5-3.039 1.973-5.726-.941-12.035-2.339-17.609-2.383-8.496-2.598-19.844 4.629-26.094-5.606 5.649-6.336 14.57-4.868 22.027 1.196 6.203 5.153 13.984 3.86 20.316 1.73-2.3 3.793-4.367 5.586-6.632.707-.856 1.683-1.704 1.672-2.899.367-2.808-.059-5.82-.563-8.586-.781-4.777-1.832-9.519-2.211-14.352-.133-4.992.762-11.977 4.723-15.477-2.567 3.016-3.449 6.918-3.84 10.762-.484 3.832-.016 7.664.742 11.426.75 4.871 2.614 11.168 1.961 16.074 1.547-1.969 5.27-6.406 6.719-8.347.269-3.411-.262-6.84-.707-10.215-1.078-7.739-.719-15.402 2.965-22.453-.211-.661-.907-3.36-1.274-3.75-1.957 1.742-4.301 3.273-5.359 5.753-.129.149-.363.215-.531.098-.387-.484-.825-2.086-.969-2.746-1.727 1.687-4.457 3.75-5.871 5.672-.481.687-.656 1.547-1.031 2.281-.086.141-.164.234-.325.293-.105-.023-.105-.035-.16-.125-.347-.562-.668-2.027-.804-2.707-1.625 1.859-3.504 3.457-5.055 5.395a5.419 5.419 0 00-1.141 2.082c-.058.097-.105.113-.203.16-.773-.09-.945-1.281-1.211-1.875-1.738 2.113-4.148 4.254-4.562 7.082-.028.297.093 1.461-.004 1.617-.063.098-.16.129-.266.148-.75-.238-1.258-1.242-1.644-1.879-1.145 2.399-2.664 4.754-3.313 7.352-.265.895.047 1.816-.012 2.707-.347.637-1.296-.644-1.55-.957-.414 1.992-1.055 4.238-.336 6.231.133.367.64 1.019.316 1.339-.797.16-1.508-.566-2.16-.929-.059 2.468.039 6.187 1.809 8.066.195.207.46.348.464.66-.05.113-.058.149-.179.203-.426.188-1.782-.351-2.235-.511.637 8.335 6.348 13.73 13.32 17.508l.555-.75c.047-4.524-2.387-8.524-5.32-11.785-2.364-2.813-4.813-5.59-6.211-9.036-.11-.269-.426-.961-.34-1.246 2.926 7.028 10.168 10.832 12.234 18.328.258.953.394 1.922.539 2.899.379-.469.809-.942 1.141-1.446-.266-5.445-2.512-10.469-4.965-15.25l.004.002z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,8 @@
<svg viewBox="13.535 4.769 72.918 91.169" width="72.918" height="91.169" xmlns="http://www.w3.org/2000/svg">
<path
d="M49.969 76.527c.48 5.715.582 11.438-.02 17.148-.062.578-.257 1.426-.195 1.984.016.133.055.168.149.254.18.051.308.016.488-.023.293-.32.277-1.313.324-1.758.192-1.926.309-3.855.356-5.789.09-3.918-.122-7.844-.575-11.734-.175-.031-.351-.063-.53-.078z"
fill="#3d3c3c" />
<path
d="M86.426 51.605c-.332.121-.984.473-1.258.16 1.082-2.043 1.492-4.445.942-6.715-.211.18-.454.442-.746.45-.086-.07-.098-.067-.106-.184.059-1.617-.023-3.445-.809-4.914-.34.355-.668.738-1.074 1.023.348-2.011-.37-4.054-1.43-5.758-.152.305-.32.708-.601.915l-.078-.008c-.793-2.125-1.965-4.254-3.652-5.82-.184.37-.391.968-.72 1.222h-.089c-.191-.25-.184-.867-.262-1.183-.34-1.43-1.203-2.524-2.234-3.536-.184.38-.438.957-.836 1.153l-.098-.008c-.93-2.89-3.285-5.254-5.851-6.844.004.383.129 1.102-.325 1.266l-.109-.016c-.57-1.723-2.273-2.707-3.816-3.5.05.14.156.348.125.5-.325-.027-4.114-3.137-4.676-3.516-.106.282-.254.735-.59.797-.836-.953-1.32-2.18-2.191-3.136.007.195.015.351-.051.539l-.078.07c-.535-.3-.684-1.04-1.051-1.512-1.828-2.625-3.371-5.426-4.8-8.28-1.009 1.96-1.825 4.038-3.044 5.894-.789 1.175-1.723 2.77-2.707 3.718a.455.455 0 01-.242-.086c-.41.207-1.871 2.625-2.516 2.665-.183-.192-.066-.5-.043-.739-1.168.793-2.293 1.64-3.343 2.586-.465.387-.852.961-1.422 1.184-.196-.047-.223-.258-.27-.422-.633.058-2.25 1.644-2.668 2.152-.223.274-.469.914-.7 1.07-.386-.316-.35-.937-.448-1.394a19.013 19.013 0 00-5.211 5.43c-.196.312-.598 1.363-.852 1.53-.066.044-.156.04-.23.048-.426-.133-.473-.657-.559-1.031-1.14.906-2.09 2.101-2.344 3.57-.082.312-.011.715-.238.969-.45-.137-.594-.715-.836-1.079-1.05 1.149-1.957 2.454-2.558 3.895-.137.352-.383 1.434-.618 1.621-.37-.098-.62-.566-.863-.844-.867 1.207-1.559 2.961-1.66 4.45-.02.316.101 1.105-.02 1.351-.472-.008-.68-.574-.945-.89-.676 1.64-.902 3.12-.633 4.863.024.14.055.238-.062.34-.356.015-.715-.375-.969-.598-.277 1.707-.29 3.473.18 5.148.093.332.644 1.559.527 1.79-.34.093-.832-.168-1.133-.325.012 1.364.371 2.61 1.121 3.754.215.332.735.801.63 1.211-.388.195-.837-.105-1.216-.219.254.961.668 1.965 1.25 2.778.352.562 1.012 1.054 1.036 1.757-.196.375-.774-.144-.996-.289.66 1.63 1.687 3.157 2.878 4.446.399.449.989.808 1.215 1.379-.152.27-.683.125-.949.136 1.582 1.59 3.488 2.77 5.2 4.203-.083.086-.032.059-.122.086-.258.075-.476.2-.71.328 1.421.864 3.007 1.524 4.495 2.274 3.414 1.61 6.707 3.644 10.316 4.762 2.438.546 4.973-.102 7.18-1.168 1.309-.653 2.66-1.274 4.144-1.348-1.789-.883-2.957-2.762-4.21-4.25-.989-1.168-2.055-2.352-3.278-3.281-2.148-1.63-4.644-2.536-7.105-3.57-7.621-3.028-14.934-8.239-17.891-16.149l.043-.011c3.383 7.199 9.207 11.707 16.418 14.699 2.715 1.187 5.855 2.214 8.242 3.968 3.285 2.254 4.918 6.22 8.086 8.567l.582-.008-.192-3.992c-2.289-3.504-4.421-7.235-7.492-10.125-7.004-6.7-24.805-11.758-23.613-23.367C18.5 50.637 36.95 54.269 43.93 62.195c2.063 2.21 4.22 5.668 5.72 8.316-.18-1.996-.407-6.133-.536-8.172-.05-.77-.016-1.941-.223-2.648-2.304-3.836-5.633-7.125-9.078-9.984-1.765-1.418-3.629-2.723-5.336-4.211-4.37-3.77-7.285-9.551-6.594-15.38.211 9.184 5.137 13.919 12.203 18.856a37.319 37.319 0 018.707 9.441c-.187-3.46-.386-6.926-.636-10.383-7.883-6.898-14.828-14.473-12.57-25.75-1.094 11.324 4.707 17.598 12.527 24.77-.297-4.273-.332-8.57-.371-12.852-4.957-5.465-7.13-10.738-4.203-17.988-1.942 7.512-.48 11.301 4.207 17.316.125-8.793 2.039-17.414 2.418-26.176.296 4.008-.532 8.027-.895 12.012-.379 3.719-.723 7.445-.883 11.18 3.79-4.2 8.242-9.098 8.192-15.121.722 2.14-.516 5.344-1.493 7.305-1.597 3.219-4.086 5.933-6.726 8.332-.024 6.32.156 12.676.66 18.973 4.945-8.64 15.422-15.648 14.906-26.578 1.621 11.21-10.047 18.598-14.785 27.512.207 4.719.656 9.453 1 14.164 1.977-3.133 4.098-6.184 6.73-8.809 3.82-4 8.79-7.848 11.645-12.566 2.422-3.93 3.227-8.566 3.508-13.109.02-.246.04-1.219.145-1.371 1.27 19.363-13.402 22.078-21.992 36.48.172 1.988.562 7.77.758 9.668 2.062-8.32 11.512-12.418 17.91-17.035 3.59-2.57 6.992-5.613 9.425-9.332 1.965-2.91 3.313-6.379 3.668-9.855-.129 7.55-5.43 14.16-11.176 18.586-6.882 5.398-16.05 8.69-19.496 17.363 2.696-2.336 5.907-4.153 9.227-5.45 7.832-2.902 16.887-4.308 22.527-11.082l-.016.063c-7.922 10.273-22.594 8.844-31.945 17.059 3.61 1.078 7.098 3.383 11.023 2.562 3.254-.586 6.442-2.414 9.336-3.941 1.938-1.031 3.906-2.031 5.79-3.16-.243-.153-.852-.364-.673-.73 1.848-1 3.551-2.235 5.028-3.735-.34-.031-.64-.074-.957-.207 1.656-1.77 3.375-3.598 4.219-5.934-.29.176-.75.578-1.106.406-.07-.125-.098-.199-.055-.34.164-.562.754-1.16 1.086-1.644a8.065 8.065 0 001.2-2.668c-.305.137-.864.414-1.126.09.028-.707.915-1.398 1.13-2.102.386-.851.577-1.8.472-2.734zM21.528 64.527c4.45 3.695 10.043 5.809 15.707 6.738 2.438.434 5.531.36 7.856 1.125.23.078.445.153.554.375-1.094.242-2.234-.14-3.336-.203-2.008-.172-3.984-.277-5.969-.64a31.696 31.696 0 01-14.043-6.391c-1.035-.836-2.14-1.813-2.898-2.91.27.136 1.684 1.543 2.129 1.906zm61.496-5.871a.412.412 0 01.082-.106c-.027.036-.055.067-.082.106z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,5 @@
<svg viewBox="0 4.3 512 503.4" width="512" height="503.4" xmlns="http://www.w3.org/2000/svg">
<path
d="M512 259.914c.286-55.128-129.372-52.643-129.372-52.643s135.594-24.009 125.938-72.994c-9.657-48.986-54.308-34.909-54.308-34.909s33.638-44.135-3.244-74.927c-36.88-30.791-64.844 18.08-64.844 18.08s-.224-42.813-41.45-37.815c-41.962 5.088-55.949 121.798-55.949 121.798s1.202-90.049-47.24-87.586c-59.176 3.009-54.019 145.357-51.996 178.717-2.058-24.747-12.032-105.884-53.146-78.732-44.184 29.182-13.496 178.618-6.655 240.572-13.593 12.192-23.786 21.622-23.786 21.622l-24.653 22.531s-21.449 19.328-42.401 39.124c-10.516 9.835-20.851 19.889-28.321 27.754-7.399 7.97-11.966 13.857-10.189 16.38 1.581 2.259 9.003-.319 18.775-6.071 9.857-5.661 21.974-14.453 33.766-23.612 23.512-18.389 45.459-38.456 45.459-38.456l24.807-22.712s9.456-8.663 21.663-20.638c119.132 9.711 264.004 39.176 272.553-15.456 8.259-52.764-54.741-58.795-88.797-58.276 51.966-3.735 183.16-17.542 183.39-61.752zm-107.377 14.162c-21.918 3.435-43.965 4.988-43.965 4.988l-25.395 1.984s-22.359 1.739-44.715 2.735c-14.107.575-28.288.919-37.044-.319-9.284 9.965-19.685 20.92-30.349 31.637-11.115 11.318-22.487 22.359-32.89 31.948 7.105-.802 15.938-1.112 24.749-1.438 19.403-.56 38.964-.545 38.964-.545l22.761-.018s5.057-.015 12.666.195c7.617.242 17.8.677 27.983 1.819 20.405 2.046 40.409 7.168 39.739 12.75-.71 6.037-20.314 6.657-39.728 7.374-19.404.559-38.964.544-38.964.544l-22.761.018s-5.058.014-12.666-.195c-7.616-.242-17.8-.677-27.983-1.819-13.847-1.389-27.488-4.194-34.63-7.599a22.817 22.817 0 01-1.544-.814c-15.68 13.73-27.404 22.221-29.543 20.034-2.099-2.142 4.433-11.739 15.283-25.13a28.85 28.85 0 01-1.226-2.088c-3.092-5.796-5.888-14.766-8.195-23.841-4.004-15.724-6.45-31.768-6.45-31.768l-2.885-18.557s-2.552-16.427-3.417-33.051c-.888-16.623-.152-33.386 5.465-34.081 5.986-.743 10.831 14.922 14.367 30.688 3.52 15.768 5.8 31.671 5.8 31.671l2.844 18.384s2.651 16.239 4.004 32.757c.469 5.759.759 11.534.709 16.671 9.457-11.105 20.478-23.607 31.463-36.145 9.424-10.677 18.844-21.347 27.394-31.035-1.927-7.106-2.81-18.119-3.455-29.089-.968-18.145-1.248-36.309-1.248-36.309l-.494-21.094-.186-11.722c-.051-7.037.05-16.506.629-25.996 1.083-19.047 4.932-37.986 10.589-37.592 6.013.411 7.877 18.555 9.515 36.626.773 9.058 1.306 18.14 1.645 25.006l.433 11.455.512 21.324s.484 18.941-1.269 37.898c-.37 4.163-.857 8.303-1.442 12.244l19.294-21.859c11.88-13.564 23.776-27.119 35.452-40.867l10.098-11.767c-.196-5.078.276-11.204.992-17.259 1.652-13.721 4.458-27.129 4.458-27.129l3.023-15.562s2.532-13.763 6.131-27.388c3.633-13.615 8.342-27.16 13.968-26.363 5.953.843 6.873 15.023 5.974 29.018-.872 13.994-3.561 27.719-3.561 27.719l-3.064 15.859s-.954 4.872-2.638 11.64l26.916-31.366c31.359-36.158 62.725-72.342 67.188-68.952 4.848 3.674-21.585 42.768-50.304 80.065-9.648 12.627-19.533 25.054-28.402 35.985a205.286 205.286 0 017.903-2.715c5.013-1.593 8.375-2.489 8.375-2.489l15.325-4.222s13.346-3.683 26.734-6.79c13.411-3.064 27.22-5.906 29.452-.655 2.347 5.511-9.446 13.65-22.41 19.502-6.464 2.902-13.119 5.335-18.133 6.935-5.02 1.636-8.379 2.523-8.379 2.523l-15.39 4.249-8.395 2.211c-5.002 1.309-11.683 2.907-18.349 4.49-6.586 1.452-13.24 2.926-18.583 3.428-5.611 6.677-9.036 10.662-9.036 10.662-11.723 13.846-23.701 27.479-35.609 41.169l-20.067 22.397c-.234.257-.495.541-.733.8 6.055-1.494 12.792-2.839 19.574-3.964 21.955-3.727 44.051-5.289 44.051-5.289l25.429-1.991s22.4-1.749 44.85-2.463c22.457-.682 45.088-.377 45.71 5.273.659 5.972-20.995 11.265-42.914 14.663z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,5 @@
<svg viewBox="5.005 10.805 89.984 78.382" width="89.984" height="78.382" xmlns="http://www.w3.org/2000/svg">
<path
d="M73.938 10.812c-.434-.047-.93.156-1.031.191-.137.047-.16.926-.184 1.317-.023.39-1.062 3.07-1.273 3.668-.207.601-.969.695-1.2.738-.124.024-.374 1.063-.535 1.094-16.984 3.562-35.445 9.664-52.89 15.258 18.784-7.05 37.57-12.95 52.753-16.027 0 0-2.054-.371-4.34-.531-2.285-.16-12.21.117-16.71.437-4.5.324-12.008 1.457-15.723 2.332-3.72.879-8.149 3.211-12.121 5.633-3.973 2.426-5.61 4.871-6.625 6.625s-1.153 2.469-1.57 2.863c-.415.39-.462 1.086-.462 1.086l.579-.3c.578-.301 2.101.206 4.085 1.062 1.985.855 6.25.59 6.25.59s.262-.72 4.243-3.036c3.984-2.316 9.273-4.343 10.512-4.832a56.796 56.796 0 019.078-2.71c4.406-.914 10.25-1.454 10.66-1.684.406-.23 8.328-4.129 10.008-4.914 1.68-.785 2.695-1.715 2.84-1.86.148-.148.457-.246.343-.082-.113.165-1.304 2.614-1.535 3.56-.23.944-.965 1.077-1.406 1.077s-1.613.637-1.828.797c-.211.164-.942.695-1.309.832-.37.137-3 .418-13.25 1.984-10.25 1.57-18.84 5.727-22.602 7.641-3.762 1.918-8.101 5.727-10.109 7.781-2.008 2.055-5.238 6.489-8.055 10.066s-4.015 8.567-4.109 9.442c-.094.879-.691 3.53-1.246 5.03-.555 1.5.367 1.2.969.993.601-.207 1.156-.32 2.586-.46 1.43-.138 4.57-1.204 9.093-3.095 4.524-1.894 8.22-6.093 9.282-7.039l.636-.554c1.88-1.645 11.688-10.066 13.38-11.871l.046-.047c1.79-1.887 5.203-4.278 6.649-5.262 1.453-.992 8.086-5.477 9.882-6.902 2.114-1.68 5.34-4.395 6.997-6.63-2.524.524-11.875 5.446-17.273 7.571C38.86 36.023 30.61 42.874 24.04 48.23c-6.176 5.023-8.493 10.246-16.38 15.973 6.762-5.309 9.387-10.602 15.962-16.375 7.21-6.328 14.777-12.242 23.602-15.914 5.527-2.297 11.129-5.754 16.805-7.418.785-.23 1.351.086 1.867-.14.582-.258 1.105-.383 1.558-.56.946-.37.72.071.649.255-.07.183-.461.898-.715 1.476-.238.547-3.191 5.762-3.543 6.442a16.264 16.264 0 013.477-2.637c.515-1.144 1.882-3.82 2.12-4.543.278-.832.602-.207.692.047.094.254.531 1.73.645 2.191.082.325.164.414.304.817.453-.133.91-.25 1.38-.348-.161-.512-.278-1.387-.423-2.074-.164-.785-.586-1.797-.945-2.285s0-1.14.066-1.536c.067-.39 3.82-9.335 3.82-9.335.09-.22-.183-.961-.644-1.309a.808.808 0 00-.398-.145zm1.898 16.703c-1.117.004-2.21.148-3.273.371-.469.098-.926.215-1.379.348-1.394.41-2.707.968-3.898 1.652a16.4 16.4 0 00-3.477 2.637c-.25.254-.508.504-.73.765-2.91 3.446-4.211 6.614-4.235 11.97 0 .027-.004.05-.004.077-.007 4.704 1.57 9.086 2.668 11.863 0 0 1.743 4.938 5.54 8.399 2.382 2.172 5.09 4.496 8.488 5.254 9.511 2.125 15.41-1.418 18.168-9.07 3.03-8.399.14-19.896-2.867-25.13-2.63-4.437-6.391-8.109-12.738-8.988a15.856 15.856 0 00-2.27-.148zm6.11 3.734c12.672 4.957 10.352 18.406 10.55 22.746-.503-6.894-1.355-18.125-10.55-22.742zm-14.598 1.02c1.054 0 2 .34 2.879.828-2.172 1.508-4.864 4.938-5.235 8.246-1.676-.95-2.812-1.906-3.441-3.105.738-2 2.484-4.664 4.285-5.743a5.273 5.273 0 011.508-.226zM49.036 42.1c-1.985.012-3.996.34-5.887.941-8.398 8.38-14.766 12.844-16.555 15.031 0 0-5.957 10.391-1.66 20.594 3.402 8.082 9.601 11.91 18.098 10.062 8.496-1.847 17.934-9.586 20.5-14.402 1.387-2.605 2.535-4.972 3.265-8.078-4.386-3.722-5.996-8.984-5.996-8.984-1.597-3.809-2.582-8.207-2.699-12.336-2.511-1.977-5.754-2.844-9.062-2.828zm-1.586 5.836c.523-.008 1.058.008 1.605.039-14.82.015-22.066 18.383-21.484 25.859-1.75-6.047 3.618-25.684 19.875-25.898z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,11 @@
<svg viewBox="10.985 9.99 78.046 80.42" width="78.046" height="80.42" xmlns="http://www.w3.org/2000/svg">
<path
d="M40.61 90.34v-.03c-.02-.07-.04-.14-.04-.22v-.06c0-.09 0-.19.03-.28 0-.03.02-.05.03-.07.02-.05.03-.11.06-.16 0-.02.03-.06.06-.11-4.49-.9-6.7-8.95-7.35-11.82-.21.03-.39.05-.49.06-.1.03-.27.09-.49.15.68 3.06 3.05 11.69 8.25 12.61-.01-.02-.03-.04-.04-.07zM48.88 61.65c.46 3.49-.25 6.23-1.6 8.38.87 1.73 2.1 3.22 3.43 4.5 1.55-3.99 2.7-8.77 2.84-14.28-1.88-.71-2.8-1.89-2.85-1.96-.1-.14-.16-.3-.18-.46a63.48 63.48 0 01-.4-4.26c-.74 1.21-1.42 2.45-2.04 3.71.32 1.39.59 2.85.8 4.37zM87.43 49.95c-2.24-6.39-5.15-10.62-4.19-14.7-2.93 3-8.06 2.81-14.66 4.35-2.12.49-4.05 1.18-5.83 2.03-.09 7.37-.05 13.66.11 14.37.25.91.05 1.86-.56 2.65-.54.7-1.34 1.24-2.35 1.6.14 2.5.04 4.88-.22 7.12 6.12-4.87 5.75-14.4 5.74-14.5-.01-.28.2-.51.48-.52.26 0 .51.2.52.48.02.46.44 10.97-6.92 15.93-.55 3.71-1.55 7.03-2.72 9.91.05-.05.12-.09.19-.11 19.69-5.67 17.92-28.16 17.9-28.39a.496.496 0 11.99-.09c.02.23 1.81 23.17-18.15 29.29.74.4 1.99 1.08 2.73 1.5l.25.14c.43.25.93.5 1.46.77 15-1.31 16.4-11.99 16.42-12.1.03-.28.28-.48.55-.44.27.03.47.28.44.55-.01.11-1.41 10.78-15.64 12.79 2.24.93 4.99 1.73 7.82 1.73 3.8 0 7.72-1.45 10.7-5.98 5.25-7.99 8.74-17.52 4.94-28.38z"
fill="#3d3c3c" />
<path
d="M39.28 43.09c.56 7.51 2.21 12.61 3.24 15.79.43 1.34.75 2.31.82 3.01.08.74-.16 1.43-.67 2-.21.23-.47.44-.77.62.64 1.37 1.36 2.62 2.16 3.72.57.78 1.08 1.57 1.55 2.36 1.74-2.13 2.78-4.98 2.28-8.82-.9-6.8-3.16-13.61-8.61-18.69zM40.11 74.33a61.902 61.902 0 01-4.24-8.46c-.51.02-.99.03-1.41.03h-.66a.996.996 0 01-.76-.4c-5.66-7.52-5.82-19.23-4.95-27.73.05-.47.1-.93.16-1.39-2.32-1.2-4.12-2.38-5.09-4.23-.22 3.75-3.81 6.63-7.38 11.54-6.06 8.33-5.51 17.42-3.07 25.62 2.07 6.94 7.71 8.54 12.47 8.54 1.39 0 2.68-.14 3.81-.32-10.8-6.93-12.37-19.57-12.39-19.71-.03-.27.17-.52.44-.55.28-.04.52.17.55.44.02.13 1.64 13.04 12.88 19.55.07-.01.15-.03.22-.04.16-.04.33-.08.48-.11.95-.23 1.53-.43 1.53-.43s.06 0 .17-.02c-13.69-21.4-7.94-33.48-7.69-33.99.12-.25.42-.34.67-.22a.5.5 0 01.23.67c-.06.12-5.78 12.26 7.88 33.38.17-.03.35-.06.56-.1l.25-.05c1.44-.29 3.47-.83 5.51-1.77-.05-.08-.08-.16-.13-.24zM58.96 60.53c-.73.16-1.53.25-2.41.25-.75 0-1.4-.1-2-.24-.25 8.15-2.62 14.69-5.28 19.51.79 3.72.81 6.85.72 8.61 2.76-3.56 9.66-13.96 8.97-28.12zM51.52 57.69s1.53 2.08 5.03 2.08c3.98 0 5.8-1.83 5.34-3.51-.46-1.68-.1-33.87 1.02-39.36s-1.47-6.86-1.47-6.86-.2-.05-.53-.05c-1.27 0-4.46.67-6.03 7.06-1.98 8.09-5.19 26.44-3.36 40.63z"
fill="#3d3c3c" />
<path
d="M43.26 68.82c-.84-1.14-1.58-2.45-2.25-3.86-1.2.48-2.69.73-4.09.84a59.96 59.96 0 004.05 8.01c4.94 8.24.58 16.18.58 16.18h7.32s1.55-11.38-5.61-21.18zM33.51 17.88c-1.93 4.34-10 33.3.32 47.02h.63c2.01 0 8.17-.22 7.89-2.91-.33-3.21-7.55-15.81-2.41-42.1 0 0 1.45-4.03-1.61-5 0 0-.43-.2-1.07-.2-1.02 0-2.57.51-3.76 3.18l.01.01z"
fill="#3d3c3c" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,14 @@
<svg viewBox="862.028 780.548 3275.945 3438.905" width="3275.945" height="3438.905" xmlns="http://www.w3.org/2000/svg">
<g fill="#3d3c3c">
<circle cx="2777.703" cy="2293.937" r="106.016" />
<circle cx="2777.703" cy="1870.695" r="106.016" />
<circle cx="2222.306" cy="1659.07" r="106.016" />
<path
d="M1927.52 3225.869l-10.478-45.755c-8.532-37.252-42.016-62.809-77.802-59.09-24.12 2.412-44.65 15.747-56.579 37.16-8.968 16.097-10.597 35.316-6.484 53.278l32.06 140.004c9.369 40.914-2.281 85.053-33.768 112.808-36.541 32.209-89.497 37.307-132.238 11.186-26.693-16.314-44.22-44.259-50.906-74.82l-60.257-275.41c-7.36-33.638-36.783-59.284-71.214-58.833-47.331.619-79.768 43.925-70.216 87.643l5.983 27.355c7.275 33.262.896 68.841-20.274 95.507-31.997 40.304-85.001 53.099-130.547 34.093L965.954 3194.57c-37.76-15.779-81.492-.029-97.415 35.095-9.989 22.034-8.51 47.094 4.612 68.379 8.187 13.281 21.151 22.994 35.548 29.004l276.371 115.389c34.17 14.266 61.452 43.105 69.648 79.216 10.666 46.995-9.294 94.069-48.925 119.136l-24.56 15.548c-29.096 18.419-43.144 54.837-30.956 87.045 16.751 44.264 68.534 59.935 106.351 36.024l243.471-154.06c18.041-11.417 38.728-17.227 59.521-17.227 14.504 0 29.065 2.828 42.812 8.57a31.79 31.79 0 011.831.834c43.666 18.157 72.194 65.284 65.662 116.701-4.312 33.938-25.536 63.447-54.617 81.467l-123.33 76.422c-13.272 8.224-24.545 19.875-30.531 34.295-9.586 23.089-7.066 48.035 6.271 68.199 21.281 32.171 66.96 40.809 101.786 19.22l39.246-24.32a1111.084 1111.084 0 01629.572-165.741c18.175.726 27.281-21.311 13.987-33.725a1111.265 1111.265 0 01-324.789-564.172zM1376.786 2559.146l-126.866 21.095c-15.322 2.552-28.629 10.988-37.466 23.757-8.837 12.759-12.042 28.197-9.037 43.466 5.873 29.874 36.823 49.824 68.911 44.499l40.039-6.665a983.552 983.552 0 01161.368-13.329 983.73 983.73 0 01389.137 80.248c16.28 7.012 32.522-9.36 25.51-25.64a983.638 983.638 0 01-66.899-550.459l6.759-40.693c4.893-29.452-12.222-58.777-38.974-66.781-19.591-5.844-39.849-1.406-55.376 12.618-9.764 8.818-15.628 21.221-17.786 34.2l-20.73 124.662c-5.835 35.09-27.117 66.869-59.38 81.851-45.684 21.215-99.023 7.491-128.906-33.486-17.272-23.685-22.452-54.154-17.362-83.022l42.878-243.215c4.773-27.073-8.972-55.212-34.594-65.174-34.484-13.408-71.047 8.396-77.171 43.067l-4.058 23.044c-6.015 34.159-26.777 65.007-58.097 79.908-40.793 19.409-87.979 10.873-119.079-20.239l-187.867-187.867c-23.009-23.009-59.737-24.728-81.912-3.88-11.336 10.682-17.705 25.107-17.937 40.64-.232 15.533 5.684 30.137 16.671 41.125l187.481 187.48c24.557 24.557 37.97 59.289 32.239 93.542-7.46 44.581-41.557 78.32-84.893 85.949l-24.271 4.268c-28.865 5.076-51.396 30.292-49.72 59.552 1.021 17.834 9.878 33.606 24.209 43.653 12.475 8.731 27.575 12.126 42.559 9.438l249.481-43.919c33.48-5.969 67.803 4.924 91.846 28.967 31.217 31.205 39.736 78.473 20.124 119.35-15.119 31.513-46.354 52.258-80.831 57.99z" />
<circle cx="2222.306" cy="1235.828" r="106.016" />
<circle cx="2777.703" cy="1447.444" r="106.016" />
<circle cx="2222.306" cy="2082.311" r="106.016" />
<path
d="M4031.446 1912.879c10.977-10.988 16.903-25.592 16.671-41.125-.232-15.522-6.612-29.958-17.947-40.64-22.144-20.868-58.893-19.118-81.902 3.88l-184.876 184.866c-24.552 24.551-59.277 37.958-93.522 32.226-44.569-7.461-78.299-41.549-85.937-84.87l-4.377-24.804c-4.786-27.121-27.435-48.835-54.97-49.343-37.019-.683-63.693 32.259-57.606 66.85l43.284 245.518c5.09 28.87-.076 59.342-17.337 83.036-29.866 40.997-83.227 54.726-128.922 33.496-32.271-14.993-53.554-46.787-59.39-81.889l-20.756-124.843c-2.149-12.925-8.032-25.255-17.775-34.015-15.547-13.979-35.768-18.442-55.353-12.565-26.752 7.993-43.866 37.318-38.974 66.76l6.77 40.703c31.108 187.055 7.333 378.063-66.887 550.423-7.031 16.329 9.291 32.65 25.62 25.618a983.494 983.494 0 01550.372-66.862l40.049 6.664c32.214 5.357 63.026-14.626 68.91-44.499 3.005-15.269-.2-30.707-9.037-43.466-8.847-12.77-22.155-21.206-37.476-23.758l-126.887-21.098c-34.482-5.733-65.711-26.497-80.817-58.021-19.586-40.875-11.073-88.099 20.111-119.283 24.063-24.074 58.408-34.914 91.867-28.999l249.501 43.93c34.478 5.937 67.416-20.482 66.845-57.473-.425-27.574-22.131-50.303-49.29-55.088l-20.647-3.637c-34.188-6.023-65.085-26.77-80.033-58.102-19.46-40.79-10.898-87.989 20.214-119.089l190.504-190.501zM4034.054 3194.57L3755.2 3310.995c-42.157 17.584-90.641 7.938-123.024-25.503-26.652-27.521-35.754-67.687-27.566-105.113l5.32-24.318c7.366-33.672-8.719-69.28-40.244-83.218-43.313-19.149-90.753 6.679-100.308 50.348l-60.553 276.792c-7.66 35.013-29.766 66.183-62.183 81.47-40.427 19.063-87.546 12.313-120.99-17.081-31.568-27.745-43.271-71.939-33.888-112.906l32.074-140.037c4.112-17.953 2.474-37.162-6.494-53.249-11.933-21.405-32.45-34.742-56.585-37.155-35.929-3.662-69.27 21.837-77.792 59.099l-10.487 45.745c-49.731 217.221-163.346 413.402-324.719 564.111-13.355 12.472-4.106 34.506 14.153 33.782a1111.198 1111.198 0 01629.346 165.755l39.246 24.301c34.865 21.588 80.495 12.97 101.786-19.211 13.333-20.158 15.841-45.111 6.288-68.188-5.955-14.386-17.181-26.019-30.416-34.22l-122.342-75.808c-35.712-22.128-58.921-61.494-56.485-103.436 2.581-44.436 30.898-82.681 72.852-98.078 33.652-12.35 71.373-6.198 101.665 12.969l237.777 150.452c29.096 18.411 68.016 15.499 91.889-9.313 32.821-34.112 24.823-87.607-12.98-111.526l-22.637-14.325c-32.397-20.501-54.591-55.23-55.436-93.559-1.026-46.565 26.235-87.833 68.362-105.418l280.454-117.097c14.401-6.013 27.371-15.727 35.562-29.01 13.126-21.287 14.603-46.348 4.625-68.385-15.923-35.143-59.656-50.855-97.416-35.095zM2500 1075.037c-8.625 0-17.061-.784-25.265-2.252-12.353-2.21-23.874 6.589-23.874 19.139v3075.655c0 25.674 18.477 49.066 44.024 51.62 29.381 2.938 54.139-20.05 54.139-48.828V1091.902c0-12.522-11.493-21.306-23.82-19.106a143.286 143.286 0 01-25.204 2.241zM2500 780.548c-58.461 0-106.016 47.555-106.016 106.016 0 44.395 27.487 82.371 66.283 98.138 12.294 4.995 25.667 7.878 39.733 7.878 14.029 0 27.367-2.876 39.629-7.847 38.853-15.742 66.386-53.737 66.386-98.169 0-58.461-47.555-106.016-106.015-106.016z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,13 @@
<svg viewBox="915.634 1018.864 3168.732 2962.272" width="3168.732" height="2962.272" xmlns="http://www.w3.org/2000/svg">
<g fill="#3d3c3c">
<path
d="M2353.072 2592.594c.067-.687.154-1.355.285-2.031 4.652-23.927 1.221-48.899-14.095-67.862-53.505-66.245-138.922-45.02-166.332 14.745-5.048 11.01-16.356 17.869-28.378 17.223a29.585 29.585 0 01-26.509-19.969l-14.328-41.848c-16.06-46.905-64.599-77.201-112.856-65.828-55.344 13.044-86.205 70.648-68.235 123.127l15.788 46.073c3.93 11.453.472 24.149-8.708 32.039-9.17 7.87-22.242 9.382-32.973 3.786-13.833-7.215-28.908-10.712-43.877-10.712-25.585 0-50.88 10.211-69.269 29.544-17.435 18.341-26.673 42.403-25.999 67.728.684 25.334 11.184 48.915 29.592 66.398l464.028 486.587c11.626 12.191 32.197 5.143 33.903-11.616l67.963-667.384zM1770.695 1141.19c4.122-4.051 8.928-6.934 14.024-8.809 3.534-9.262 5.694-19.204 5.694-29.693 0-46.218-37.597-83.824-83.815-83.824-46.218 0-83.824 37.606-83.824 83.824 0 46.218 37.606 83.815 83.824 83.815 19.945 0 38.037-7.292 52.442-18.966.439-9.603 4.256-19.08 11.655-26.347zM1746.85 1729.778c-47.626-40.084-123.54-25.214-149.757 37.905-12.158 29.271-5.998 62.02 10.617 89.011a29.61 29.61 0 01-.747 32.169c-6.81 10.008-19.092 14.825-30.863 12.224l-47.557-10.548a95.55 95.55 0 00-20.73-2.283c-17.859 0-35.362 5.067-50.784 14.911-21.375 13.64-36.171 34.803-41.671 59.588-6.363 28.686.686 58.28 19.851 81.457 14.036 16.974 34.264 27.712 55.767 32.482l43.973 9.754a29.587 29.587 0 0122.801 24.13 29.596 29.596 0 01-12.853 29.42c-26.446 17.518-45.866 44.628-47.275 76.319-3.035 68.277 59.439 113.745 119.61 97.599l657.125-142.357c16.464-3.567 21.172-24.796 7.759-34.988l-534.119-405.879a45.915 45.915 0 01-1.147-.914zM3108.982 1602.201c51.105-38.86 52.953-116.185-3.263-155.559-25.899-18.14-59.061-18.795-89.094-8.905-10.958 3.609-23.025.565-30.913-7.905a29.58 29.58 0 01-5.134-32.8l19.169-40.713c9.387-19.938 13.282-42.517 8.452-64.019-6.593-29.348-25.686-53.03-52.271-65.556-26.675-12.565-57.17-12.238-84.029 1.42-19.679 10.007-34.604 27.45-44.012 47.422l-19.1 40.55c-5.154 10.952-16.501 17.666-28.552 16.925a29.56 29.56 0 01-26.307-20.238c-12.889-38.916-49.763-65.329-90.519-65.329-1.262 0-2.524.019-3.795.077-52.18 2.196-92.946 46.507-90.817 98.784.029.482.029.973.029 1.464l-5.831 670.804c-.146 16.845 19.526 26.112 32.421 15.273l513.566-431.695z" />
<circle cx="3887.135" cy="2448.547" r="83.815" />
<path
d="M3766.745 1811.847c21.356-16.26 34.399-38.55 37.857-63.653 6.549-47.52-22.726-94.453-79.712-109.593-15.84-4.208-32.072.132-46.819 7.285a29.597 29.597 0 01-34.606-6.408 29.579 29.579 0 01-5.134-32.8l18.967-40.265c21.117-44.832 7.26-100.329-35.345-125.641-48.886-29.044-111.052-9.138-134.654 40.941l-20.749 44.06c-5.163 10.962-16.607 17.801-28.551 16.925a29.57 29.57 0 01-26.307-20.238c-12.889-38.907-49.763-65.32-90.519-65.32-1.262 0-2.524.019-3.795.077-52.19 2.187-92.956 46.507-90.818 98.774.029.491.029.983.029 1.474l-5.84 670.804c-.147 16.845 19.526 26.113 32.421 15.273l513.575-431.695zM3396.255 3160.41c.937 1.019 1.874 2.118 2.585 3.295 9.257 15.317 22.584 28.229 40.255 31.066 97.767 15.694 146.165-69.168 117.362-133.453-4.942-11.049-2.668-24.005 5.76-32.693a29.58 29.58 0 0132.501-6.772l40.848 16.787c25.12 10.323 53.723 11.704 78.564.724 23.265-10.283 41.191-29.109 50.353-53.054 18.223-47.625-9.224-103.791-56.395-123.159l-41.211-16.922a29.597 29.597 0 01-18.35-27.665c.121-11.729 7.139-22.246 17.839-26.869 29.103-12.572 52.685-36.292 59.476-67.259 14.627-66.698-39.607-122.019-101.214-116.387l-671.786 28.136c-16.831.705-25.092 20.819-13.616 33.151l457.029 491.074zM1401.886 2838.952c0-46.218-37.597-83.814-83.815-83.814-46.218 0-83.824 37.596-83.824 83.814s37.606 83.814 83.824 83.814c46.218.001 83.815-37.596 83.815-83.814zM4000.542 2061.744c-43.984 0-79.76 34.165-83.155 77.292 4.021 4.845 7.072 10.597 8.414 17.196 1.603 7.881.705 15.677-2.101 22.613 12.91 29.685 42.459 50.529 76.842 50.529 46.218 0 83.824-37.597 83.824-83.815s-37.606-83.815-83.824-83.815zM1071.694 3028.467c1.141-8.802 5.199-16.451 10.954-22.357-3.226-43.293-39.086-77.64-83.19-77.64-46.218 0-83.824 37.597-83.824 83.815 0 46.218 37.606 83.814 83.824 83.814 33.1 0 61.511-19.445 75.125-47.368-2.612-6.211-3.818-13.103-2.889-20.264zM1750.09 1522.762c46.218 0 83.814-37.597 83.814-83.815s-37.597-83.824-83.814-83.824c-46.218 0-83.815 37.606-83.815 83.824s37.597 83.815 83.815 83.815zM994.97 3408.441c-73.369 12.271-113.205 106.114-41.904 173.024 11.752 11.028 27.846 14.717 43.961 14.859a29.566 29.566 0 0128.295 21.784 29.587 29.587 0 01-10.712 31.422l-38.824 29.31c-29.693 22.417-45.252 60.382-35.916 96.396 18.629 71.863 98.265 94.429 150.28 55.178l38.868-29.351a29.597 29.597 0 0117.83-5.972 29.55 29.55 0 0115.316 4.277 29.604 29.604 0 0112.906 34.238c-4.677 14.929-4.19 31.057 2.759 45.074 26.31 53.073 78.453 72.301 123.808 56.096 23.851-8.535 42.991-25.864 53.876-48.8l317.859-592.494c7.964-14.844-5.141-32.197-21.598-28.599l-655.37 143.28c-.471.105-.952.201-1.434.278zM2248.816 1257.438c0-46.218-37.606-83.815-83.824-83.815s-83.814 37.597-83.814 83.815 37.596 83.815 83.814 83.815 83.824-37.597 83.824-83.815zM1759.04 1167.537c11.402-9.239 20.36-21.217 25.679-35.156-5.096 1.875-9.902 4.759-14.024 8.809-7.399 7.267-11.216 16.744-11.655 26.347z" />
<path
d="M3925.801 2156.231c-1.343-6.598-4.394-12.35-8.414-17.196-.172 2.19-.66 4.289-.66 6.522 0 11.835 2.529 23.068 6.973 33.286 2.806-6.935 3.704-14.731 2.101-22.612zM3863.877 2187.327c-2.899-9.351-3.7-19.04-6.205-28.414-1.705-6.378-1.297-14.582-6.329-19.512-2.48-2.432-5.917-3.698-9.361-4.156-14.85-1.977-32.816 5.35-47.237 8.667-67.857 15.611-135.241 33.274-202.016 53.013-169.357 50.064-335.685 114.442-494.933 191.198a3280.91 3280.91 0 00-167.789 86.932 3308.357 3308.357 0 00-148.071 87.331 3350.915 3350.915 0 00-110.454 72.129 3308.988 3308.988 0 00-57.225-197.104 3276.057 3276.057 0 00-48.268-138.232c-23.678-63.229-50.027-124.573-77.446-186.337-76.867-173.147-168.22-338.964-274.288-496.372-105.675-156.826-224.838-304.509-355.491-441.215-9.115-9.538-24.435-7.241-31.518 3.889a143.72 143.72 0 01-21.697 26.485 19.48 19.48 0 00-.603 27.595c130.053 136.476 248.379 284.121 352.917 441.01 105.263 157.977 194.974 325.019 272.527 497.865 40.795 81.59 69.411 171.85 98.469 258.431a3192.184 3192.184 0 0178.903 275.596c.198 1.381.591 2.762.986 4.143 57.802 243.245 87.592 494.775 87.592 750.843 0 68.85-.395 126.259-2.367 179.918a3281.445 3281.445 0 00-78.254-57.592 3321.685 3321.685 0 00-142.86-96.409c-123.658-78.863-255.004-149.318-386.975-211.776-144.599-68.432-295.704-124.53-448.966-170.147a3301.228 3301.228 0 00-234.763-60.573 3327.945 3327.945 0 00-119.263-23.868c-39.023-7.07-79.376-10.612-118.026-19.212-1.775-.395-3.353-.593-5.13-.789-8.877.196-15.782 6.116-17.361 14.006 0 2.367-.196 3.946-.196 5.524-.668 10.105-2.207 19.551-4.554 28.793-1.856 7.308-4.159 14.798.272 21.724 6.254 9.772 20.757 9.504 30.945 11.021a3262.702 3262.702 0 01115.08 19.25 3233.333 3233.333 0 01113.964 23.29 3207.092 3207.092 0 01112.761 27.317 3192.548 3192.548 0 0155.902 15.163c147.919 39.445 289.082 94.25 428.506 155.333 90.161 39.501 184.596 79.462 268.258 131.727 1.579.986 2.96 1.579 4.341 2.367 147.564 82.462 289.012 176.762 420.598 280.333-5.13 60.762-13.219 122.707-26.436 198.462-3.748 21.503 10.653 41.823 32.156 45.571a38.48 38.48 0 006.708.591c18.938 0 35.51-13.415 38.864-32.748 32.354-185.836 35.707-291.184 35.707-487.279 0-253.7-28.606-503.061-83.844-744.53-1.199-5.241 64.694-43.625 72.53-48.734 26.502-17.276 53.239-34.189 80.099-50.903 49.545-30.832 98.897-64.441 151.847-88.16 53.871-24.134 105.213-55.172 158.709-80.436a3200.815 3200.815 0 01162.757-71.405c172.23-69.928 350.513-124.765 532.125-164.332 11.428-2.49 17.865-14.113 14.403-25.282zM1071.694 3028.467c-.93 7.161.277 14.053 2.889 20.264 5.394-11.067 8.69-23.329 8.69-36.447 0-2.115-.471-4.099-.625-6.175-5.754 5.907-9.812 13.556-10.954 22.358z" />
<path
d="M1177.295 3018.219c0 9.864-7.89 17.756-17.558 17.756-8.877 0-16.373-6.51-17.557-15.191-.198-1.184-.198-1.972-.198-2.565 0-.986 0-1.972.394-2.958 1.579-8.483 8.484-14.402 16.967-14.598 2.17.196 3.748.394 5.523.789 7.102 2.169 12.429 8.877 12.429 16.767z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB