[AirQuality] Tagging channels, prepared for Crowdin, dynamic channels (#11192)

* Adding semantic tags
Adding alert level and code refined.

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Commiting intermediate work

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Complete review of the binding.
Added measures for each pollutant.
Added extensible channel for pollutant sensibility.

Signed-off-by: clinique <gael@lhopital.org>

* Finishing translation

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* One last code cleansing to be ready to push

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Documentation updates

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Spotless apply

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Correcting conflicting file

Signed-off-by: Gaël L'hopital <gael@lhopital.org>

* Code review correction

Signed-off-by: Gael L'hopital <gael@lhopital.org>

* Introducing a bridge to Api

Signed-off-by: clinique <gael@lhopital.org>

* Code review correction

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2021-11-09 08:59:13 +01:00 committed by GitHub
parent c79fd06256
commit 7bd71fdf12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1408 additions and 732 deletions

View File

@ -11,26 +11,32 @@ To use this binding, you first need to [register and get your API token](https:/
## Supported Things
There is exactly one supported thing type, which represents the air quality information for an observation location.
It has the `aqi` id.
Of course, you can add multiple Things, e.g. for measuring AQI for different locations.
Bridge: The binding supports a bridge to connect to the [AQIcn.org service](https://aqicn.org). A bridge uses the thing ID "api".
Station: Represents the air quality information for an observation location.
Of course, you can add multiple Stations, e.g. for measuring AQI for different locations.
## Discovery
Local Air Quality can be autodiscovered based on system location.
You will have complete default configuration with your apiKey.
You will created a Bridge with your apiKey.
## Binding Configuration
## Bridge Configuration
The binding has no configuration options, all configuration is done at Thing level.
## Thing Configuration
The thing has a few configuration parameters:
The bridge configuration only holds the api key :
| Parameter | Description |
|-----------|-------------------------------------------------------------------------|
| apiKey | Data-platform token to access the AQIcn.org service. Mandatory. |
## Thing Configuration
The 'Station' thing has a few configuration parameters:
| Parameter | Description |
|-----------|-------------------------------------------------------------------------|
| apikey | Data-platform token to access the AQIcn.org service. Mandatory. |
| location | Geo coordinates to be considered by the service. |
| stationId | Unique ID of the measuring station. |
| refresh | Refresh interval in minutes. Optional, the default value is 60 minutes. |
@ -45,59 +51,75 @@ For the location parameter, the following syntax is allowed (comma separated lat
If you always want to receive data from specific station and you know its unique ID, you can enter it instead of the coordinates.
This `stationId` can be found by using the following link:
https://api.waqi.info/search/?token=TOKEN&keyword=NAME, replacing TOKEN by your apikey and NAME by the station you are looking for.
https://api.waqi.info/search/?token=TOKEN&keyword=NAME, replacing TOKEN by your apiKey and NAME by the station you are looking for.
### Thing properties
Once created, at first execution, the station's properties will be filled with informations gathered from the web service :
- Nearest measuring station location
- Measuring station ID
- Latitude/longitude of measuring station
## Channels
The AirQuality information that is retrieved is available as these channels:
The AirQuality information that is retrieved for a given is available as these channels:
### AQI Channels Group - Global Results
| Channel ID | Item Type | Description |
|-----------------|----------------------|----------------------------------------------|
| alert-level | Number | Alert level (*) associated to AQI Index. |
| index | Number | Air Quality Index |
| timestamp | DateTime | Observation date and time |
| dominent | String | Dominent Pollutant |
| icon | Image | Pictogram associated to alert-level |
| color | Color | Color associated to alert level. |
### Weather Channels Group
| Channel ID | Item Type | Description |
|-----------------|----------------------|----------------------------------------------|
| aqiLevel | Number | Air Quality Index |
| aqiColor | Color | Color associated to given AQI Index. |
| aqiDescription | String | AQI Description |
| locationName | String | Nearest measuring station location |
| stationId | Number | Measuring station ID |
| stationLocation | Location | Latitude/longitude of measuring station |
| pm25 | Number | Fine particles pollution level (PM2.5) |
| pm10 | Number | Coarse dust particles pollution level (PM10) |
| o3 | Number | Ozone level (O3) |
| no2 | Number | Nitrogen Dioxide level (NO2) |
| co | Number | Carbon monoxide level (CO) |
| so2 | Number | Sulfur dioxide level (SO2) |
| observationTime | DateTime | Observation date and time |
| temperature | Number:Temperature | Temperature in Celsius degrees |
| pressure | Number:Pressure | Pressure level |
| humidity | Number:Dimensionless | Humidity level |
| dominentpol | String | Dominent Polutor |
| dew-point | Number:Temperature | Dew point temperature |
| wind-speed | Number:Speed | Wind speed |
`AQI Description` item provides a human-readable output that can be interpreted e.g. by MAP transformation.
### Pollutants Channels Group
For each pollutant (PM25, PM10, O3, NO2, CO, SO2) , depending upon availability of the station,
you will be provided with the following informations
| Channel ID | Item Type | Description |
|-----------------|----------------------|----------------------------------------------|
| value | Number:Density | Measured density of the pollutant |
| index | Number | AQI Index of the single pollutant |
| alert-level | Number | Alert level associate to the index |
(*) The alert level is described by a color :
| Code | Color | Description |
|------|--------|--------------------------------|
| 0 | Green | Good |
| 1 | Yellow | Moderate |
| 2 | Orange | Unhealthy for Sensitive Groups |
| 3 | Red | Unhealthy |
| 4 | Purple | Very Unhealthy |
| 5 | Maroon | Hazardous |
*Note that channels like* `pm25`, `pm10`, `o3`, `no2`, `co`, `so2` *can sometimes return* `UNDEF` *value due to the fact that some stations don't provide measurements for them.*
## Full Example
airquality.map:
```text
-=-
UNDEF=No data
NULL=No data
NO_DATA=No data
GOOD=Good
MODERATE=Moderate
UNHEALTHY_FOR_SENSITIVE=Unhealthy for sensitive groups
UNHEALTHY=Unhealthy
VERY_UNHEALTHY=Very unhealthy
HAZARDOUS=Hazardous
```
airquality.things:
```java
airquality:aqi:home "AirQuality" @ "Krakow" [ apikey="XXXXXXXXXXXX", location="50.06465,19.94498", refresh=60 ]
airquality:aqi:warsaw "AirQuality in Warsaw" [ apikey="XXXXXXXXXXXX", location="52.22,21.01", refresh=60 ]
airquality:aqi:brisbane "AirQuality in Brisbane" [ apikey="XXXXXXXXXXXX", stationId=5115 ]
Bridge airquality:api:main "Bridge" [apiKey="xxxyyyzzz"] {
station MyHouse "Krakow"[location="50.06465,19.94498", refresh=60]
}
```
airquality.items:
@ -105,24 +127,19 @@ airquality.items:
```java
Group AirQuality <flow>
Number Aqi_Level "Air Quality Index" <flow> (AirQuality) { channel="airquality:aqi:home:aqiLevel" }
String Aqi_Description "AQI Level [MAP(airquality.map):%s]" <flow> (AirQuality) { channel="airquality:aqi:home:aqiDescription" }
Number Aqi_Level "Air Quality Index" <flow> (AirQuality) { channel="airquality:station:local:aqi#index" }
Number Aqi_Pm25 "PM\u2082\u2085 Level" <line> (AirQuality) { channel="airquality:station:local:pm25#value" }
Number Aqi_Pm10 "PM\u2081\u2080 Level" <line> (AirQuality) { channel="airquality:station:local:pm10#value" }
Number Aqi_O3 "O\u2083 Level" <line> (AirQuality) { channel="airquality:station:local:o3#value" }
Number Aqi_No2 "NO\u2082 Level" <line> (AirQuality) { channel="airquality:station:local:no2#value" }
Number Aqi_Co "CO Level" <line> (AirQuality) { channel="airquality:station:local:co#value" }
Number Aqi_So2 "SO\u2082 Level" <line> (AirQuality) { channel="airquality:station:local:so2#value" }
Number Aqi_Pm25 "PM\u2082\u2085 Level" <line> (AirQuality) { channel="airquality:aqi:home:pm25" }
Number Aqi_Pm10 "PM\u2081\u2080 Level" <line> (AirQuality) { channel="airquality:aqi:home:pm10" }
Number Aqi_O3 "O\u2083 Level" <line> (AirQuality) { channel="airquality:aqi:home:o3" }
Number Aqi_No2 "NO\u2082 Level" <line> (AirQuality) { channel="airquality:aqi:home:no2" }
Number Aqi_Co "CO Level" <line> (AirQuality) { channel="airquality:aqi:home:co" }
Number Aqi_So2 "SO\u2082 Level" <line> (AirQuality) { channel="airquality:aqi:home:so2" }
DateTime Aqi_ObservationTime "Time of observation [%1$tH:%1$tM]" <clock> (AirQuality) { channel="airquality:station:local:aqi#timestamp" }
String Aqi_LocationName "Measuring Location" <settings> (AirQuality) { channel="airquality:aqi:home:locationName" }
Location Aqi_StationGeo "Station Location" <office> (AirQuality) { channel="airquality:aqi:home:stationLocation" }
Number Aqi_StationId "Station ID" <pie> (AirQuality) { channel="airquality:aqi:home:stationId" }
DateTime Aqi_ObservationTime "Time of observation [%1$tH:%1$tM]" <clock> (AirQuality) { channel="airquality:aqi:home:observationTime" }
Number:Temperature Aqi_Temperature "Temperature" <temperature> (AirQuality) { channel="airquality:aqi:home:temperature" }
Number:Pressure Aqi_Pressure "Pressure" <pressure> (AirQuality) { channel="airquality:aqi:home:pressure" }
Number:Dimensionless Aqi_Humidity "Humidity" <humidity> (AirQuality) { channel="airquality:aqi:home:humidity" }
Number:Temperature Aqi_Temperature "Temperature" <temperature> (AirQuality) { channel="airquality:station:local:weather#temperature" }
Number:Pressure Aqi_Pressure "Pressure" <pressure> (AirQuality) { channel="airquality:station:local:weather#pressure" }
Number:Dimensionless Aqi_Humidity "Humidity" <humidity> (AirQuality) { channel="airquality:station:localweather#humidity" }
```
airquality.sitemap:
@ -143,7 +160,7 @@ sitemap airquality label="Air Quality" {
Aqi_Description=="HAZARDOUS"="#7e0023",
=="VERY_UNHEALTHY"="#660099",
=="UNHEALTHY"="#cc0033",
=="UNHEALTHY_FOR_SENSITIVE"="#ff9933",
=="UNHEALTHY_FSG"="#ff9933",
=="MODERATE"="#ffde33",
=="GOOD"="#009966"
]
@ -159,16 +176,12 @@ sitemap airquality label="Air Quality" {
}
Frame {
Text item=Aqi_LocationName
Text item=Aqi_ObservationTime
Text item=Aqi_Temperature
Text item=Aqi_Pressure
Text item=Aqi_Humidity
}
Frame label="Station Location" {
Mapview item=Aqi_StationGeo height=10
}
}
```
@ -189,7 +202,7 @@ then
hsb = "280,100,60"
case "UNHEALTHY":
hsb = "345,100,80"
case "UNHEALTHY_FOR_SENSITIVE":
case "UNHEALTHY_FSG":
hsb = "30,80,100"
case "MODERATE":
hsb = "50,80,100"

View File

@ -12,74 +12,44 @@
*/
package org.openhab.binding.airquality.internal;
import static org.openhab.core.library.unit.MetricPrefix.HECTO;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.Unit;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.State;
/**
* The {@link AirQualityBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Kuba Wolanin - Initial contribution
* @author Łukasz Dywicki - Initial contribution
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class AirQualityBindingConstants {
public static final String BINDING_ID = "airquality";
private static final String BINDING_ID = "airquality";
public static final String LOCAL = "local";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_AQI = new ThingTypeUID(BINDING_ID, "aqi");
// List of thing properties
public static final String ATTRIBUTIONS = "Attributions";
public static final String DISTANCE = "Distance";
// List of all Channel groups id's
public static final String AQI = "aqi";
public static final String SENSITIVE = "sensitive-group";
// List of all Channel id's
public static final String AQI = "aqiLevel";
public static final String AQI_COLOR = "aqiColor";
public static final String AQIDESCRIPTION = "aqiDescription";
public static final String PM25 = "pm25";
public static final String PM10 = "pm10";
public static final String O3 = "o3";
public static final String NO2 = "no2";
public static final String CO = "co";
public static final String SO2 = "so2";
public static final String LOCATIONNAME = "locationName";
public static final String STATIONLOCATION = "stationLocation";
public static final String STATIONID = "stationId";
public static final String OBSERVATIONTIME = "observationTime";
public static final String INDEX = "index";
public static final String VALUE = "value";
public static final String ALERT_LEVEL = "alert-level";
public static final String TEMPERATURE = "temperature";
public static final String PRESSURE = "pressure";
public static final String HUMIDITY = "humidity";
public static final String DOMINENTPOL = "dominentpol";
public static final String DEW_POINT = "dew-point";
public static final String WIND_SPEED = "wind-speed";
public static final String TIMESTAMP = "timestamp";
public static final String DOMINENT = "dominent";
public static final String ICON = "icon";
public static final String COLOR = "color";
public static final State GOOD = new StringType("GOOD");
public static final State MODERATE = new StringType("MODERATE");
public static final State UNHEALTHY_FOR_SENSITIVE = new StringType("UNHEALTHY_FOR_SENSITIVE");
public static final State UNHEALTHY = new StringType("UNHEALTHY");
public static final State VERY_UNHEALTHY = new StringType("VERY_UNHEALTHY");
public static final State HAZARDOUS = new StringType("HAZARDOUS");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_AQI);
public static final Set<String> SUPPORTED_CHANNEL_IDS = Stream.of(AQI, AQIDESCRIPTION, PM25, PM10, O3, NO2, CO, SO2,
LOCATIONNAME, STATIONLOCATION, STATIONID, OBSERVATIONTIME, TEMPERATURE, PRESSURE, HUMIDITY)
.collect(Collectors.toSet());
// Units of measurement of the data delivered by the API
public static final Unit<Temperature> API_TEMPERATURE_UNIT = SIUnits.CELSIUS;
public static final Unit<Dimensionless> API_HUMIDITY_UNIT = Units.PERCENT;
public static final Unit<Pressure> API_PRESSURE_UNIT = HECTO(SIUnits.PASCAL);
// Thing Type UIDs
public static final ThingTypeUID THING_TYPE_STATION = new ThingTypeUID(BINDING_ID, "station");
public static final ThingTypeUID BRIDGE_TYPE_API = new ThingTypeUID(BINDING_ID, "api");
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* An exception that occurred while operating the binding
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class AirQualityException extends Exception {
private static final long serialVersionUID = -3398100220952729815L;
private int statusCode = -1;
public AirQualityException(String message, Exception e) {
super(message, e);
}
public AirQualityException(String message) {
super(message);
}
public int getStatusCode() {
return statusCode;
}
@Override
public @Nullable String getMessage() {
String message = super.getMessage();
return message == null ? null
: String.format("Rest call failed: statusCode=%d, message=%s", statusCode, message);
}
}

View File

@ -14,10 +14,15 @@ package org.openhab.binding.airquality.internal;
import static org.openhab.binding.airquality.internal.AirQualityBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.handler.AirQualityHandler;
import org.openhab.binding.airquality.internal.handler.AirQualityBridgeHandler;
import org.openhab.binding.airquality.internal.handler.AirQualityStationHandler;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
@ -27,23 +32,25 @@ import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import com.google.gson.Gson;
/**
* The {@link AirQualityHandlerFactory} is responsible for creating things and thing
* handlers.
* The {@link AirQualityHandlerFactory} is responsible for creating thing and thing
* handler.
*
* @author Kuba Wolanin - Initial contribution
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.airquality")
@NonNullByDefault
public class AirQualityHandlerFactory extends BaseThingHandlerFactory {
private final Gson gson = new Gson();
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(BRIDGE_TYPE_API, THING_TYPE_STATION);
private final TimeZoneProvider timeZoneProvider;
private final LocationProvider locationProvider;
@Activate
public AirQualityHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) {
public AirQualityHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider,
final @Reference LocationProvider locationProvider) {
this.timeZoneProvider = timeZoneProvider;
this.locationProvider = locationProvider;
}
@Override
@ -55,10 +62,9 @@ public class AirQualityHandlerFactory extends BaseThingHandlerFactory {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_AQI.equals(thingTypeUID)) {
return new AirQualityHandler(thing, gson, timeZoneProvider);
}
return null;
return THING_TYPE_STATION.equals(thingTypeUID)
? new AirQualityStationHandler(thing, timeZoneProvider, locationProvider)
: BRIDGE_TYPE_API.equals(thingTypeUID) ? new AirQualityBridgeHandler((Bridge) thing, locationProvider)
: null;
}
}

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.api;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airquality.internal.AirQualityException;
import org.openhab.binding.airquality.internal.api.dto.AirQualityData;
import org.openhab.binding.airquality.internal.api.dto.AirQualityResponse;
import org.openhab.binding.airquality.internal.api.dto.AirQualityResponse.ResponseStatus;
import org.openhab.core.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* The {@link ApiBridge} is the interface between handlers
* and the actual web service
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class ApiBridge {
private static final Gson GSON = new Gson();
private static final String URL = "http://api.waqi.info/feed/%query%/?token=%apiKey%";
private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);
private final Logger logger = LoggerFactory.getLogger(ApiBridge.class);
private final String apiKey;
public ApiBridge(String apiKey) {
this.apiKey = apiKey;
}
/**
* Build request URL from configuration data
*
* @return a valid URL for the aqicn.org service
* @throws AirQualityException
*/
private String buildRequestURL(String key, int stationId, String location) {
String geoStr = stationId != 0 ? String.format("@%d", stationId)
: String.format("geo:%s",
location.replace(" ", "").replace(",", ";").replace("\"", "").replace("'", "").trim());
return URL.replace("%apiKey%", key).replace("%query%", geoStr);
}
/**
* Request new air quality data to the aqicn.org service
*
* @return an air quality data object mapping the JSON response
* @throws AirQualityException
*/
public AirQualityData getData(int stationId, String location, int retryCounter) throws AirQualityException {
String urlStr = buildRequestURL(apiKey, stationId, location);
logger.debug("URL = {}", urlStr);
try {
String response = HttpUtil.executeUrl("GET", urlStr, null, null, null, REQUEST_TIMEOUT_MS);
logger.debug("aqiResponse = {}", response);
AirQualityResponse result = GSON.fromJson(response, AirQualityResponse.class);
if (result != null && result.getStatus() == ResponseStatus.OK) {
return result.getData();
} else if (retryCounter == 0) {
logger.debug("Error in aqicn.org, retrying once");
return getData(stationId, location, retryCounter + 1);
}
throw new AirQualityException("Error in aqicn.org response: Missing data sub-object");
} catch (IOException | JsonSyntaxException e) {
throw new AirQualityException("Communication error", e);
}
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.types.State;
/**
* The {@link Appreciation} enum lists all possible appreciation
* of the AQI Level associated with their standard color.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Appreciation {
GOOD(HSBType.fromRGB(0, 228, 0)),
MODERATE(HSBType.fromRGB(255, 255, 0)),
UNHEALTHY_FSG(HSBType.fromRGB(255, 126, 0)),
UNHEALTHY(HSBType.fromRGB(255, 0, 0)),
VERY_UNHEALTHY(HSBType.fromRGB(143, 63, 151)),
HAZARDOUS(HSBType.fromRGB(126, 0, 35));
private HSBType color;
Appreciation(HSBType color) {
this.color = color;
}
public State getColor() {
return color;
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link ConcentrationRange} is responsible to store the range of
* a given physical measure associated with the corresponding AQI
* index.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class ConcentrationRange {
private final double min;
private final double span;
private final Index index;
ConcentrationRange(double min, double max, Index index) {
this.min = min;
this.span = max - min;
this.index = index;
}
/*
* Computes the concentration corresponding to the index
* if contained in the range
*
* @return : a physical concentration or -1 if not in range
*/
double getConcentration(double idx) {
return index.contains(idx) ? span / index.getSpan() * (idx - index.getMin()) + min : -1;
}
}

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.api;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link Index} enum lists standard ranges of AQI indices
* along with their appreciation category.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Index {
ZERO(0, 50, Appreciation.GOOD),
FIFTY(51, 100, Appreciation.MODERATE),
ONE_HUNDRED(101, 150, Appreciation.UNHEALTHY_FSG),
ONE_HUNDRED_FIFTY(151, 200, Appreciation.UNHEALTHY),
TWO_HUNDRED(201, 300, Appreciation.VERY_UNHEALTHY),
THREE_HUNDRED(301, 400, Appreciation.HAZARDOUS),
FOUR_HUNDRED(401, 500, Appreciation.HAZARDOUS);
private double min;
private double max;
private Appreciation category;
Index(double min, double max, Appreciation category) {
this.min = min;
this.max = max;
this.category = category;
}
public double getMin() {
return min;
}
public double getSpan() {
return max - min;
}
boolean contains(double idx) {
return min <= idx && idx <= max;
}
public static @Nullable Index find(double idx) {
return Stream.of(Index.values()).filter(i -> i.contains(idx)).findFirst().orElse(null);
}
public Appreciation getCategory() {
return category;
}
}

View File

@ -0,0 +1,96 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.api;
import static org.openhab.binding.airquality.internal.api.Index.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Set;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* The {@link Pollutant} enum lists all measures
* of the AQI Level associated with their standard color.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum Pollutant {
PM25(Units.MICROGRAM_PER_CUBICMETRE, 1,
Set.of(SensitiveGroup.RESPIRATORY, SensitiveGroup.HEART, SensitiveGroup.ELDERLY, SensitiveGroup.CHILDREN),
new ConcentrationRange(0, 12, ZERO), new ConcentrationRange(12.1, 35.4, FIFTY),
new ConcentrationRange(35.5, 55.4, ONE_HUNDRED), new ConcentrationRange(55.5, 150.4, ONE_HUNDRED_FIFTY),
new ConcentrationRange(150.5, 250.4, TWO_HUNDRED), new ConcentrationRange(250.5, 350.4, THREE_HUNDRED),
new ConcentrationRange(350.5, 500.4, FOUR_HUNDRED)),
PM10(Units.MICROGRAM_PER_CUBICMETRE, 0, Set.of(SensitiveGroup.RESPIRATORY), new ConcentrationRange(0, 54, ZERO),
new ConcentrationRange(55, 154, FIFTY), new ConcentrationRange(155, 254, ONE_HUNDRED),
new ConcentrationRange(255, 354, ONE_HUNDRED_FIFTY), new ConcentrationRange(355, 424, TWO_HUNDRED),
new ConcentrationRange(425, 504, THREE_HUNDRED), new ConcentrationRange(505, 604, FOUR_HUNDRED)),
NO2(Units.PARTS_PER_BILLION, 0,
Set.of(SensitiveGroup.ASTHMA, SensitiveGroup.RESPIRATORY, SensitiveGroup.ELDERLY, SensitiveGroup.CHILDREN),
new ConcentrationRange(0, 53, ZERO), new ConcentrationRange(54, 100, FIFTY),
new ConcentrationRange(101, 360, ONE_HUNDRED), new ConcentrationRange(361, 649, ONE_HUNDRED_FIFTY),
new ConcentrationRange(650, 1249, TWO_HUNDRED), new ConcentrationRange(1250, 1649, THREE_HUNDRED),
new ConcentrationRange(1650, 2049, FOUR_HUNDRED)),
SO2(Units.PARTS_PER_BILLION, 0, Set.of(SensitiveGroup.ASTHMA), new ConcentrationRange(0, 35, ZERO),
new ConcentrationRange(36, 75, FIFTY), new ConcentrationRange(76, 185, ONE_HUNDRED),
new ConcentrationRange(186, 304, ONE_HUNDRED_FIFTY), new ConcentrationRange(305, 604, TWO_HUNDRED),
new ConcentrationRange(605, 804, THREE_HUNDRED), new ConcentrationRange(805, 1004, FOUR_HUNDRED)),
CO(Units.PARTS_PER_BILLION, 1, Set.of(SensitiveGroup.HEART), new ConcentrationRange(0, 4.4, ZERO),
new ConcentrationRange(4.5, 9.4, FIFTY), new ConcentrationRange(9.5, 12.4, ONE_HUNDRED),
new ConcentrationRange(12.5, 15.4, ONE_HUNDRED_FIFTY), new ConcentrationRange(15.5, 30.4, TWO_HUNDRED),
new ConcentrationRange(30.5, 40.4, THREE_HUNDRED), new ConcentrationRange(40.5, 50.4, FOUR_HUNDRED)),
O3(Units.PARTS_PER_BILLION, 3, Set.of(SensitiveGroup.CHILDREN, SensitiveGroup.ASTHMA),
new ConcentrationRange(0, 54, ZERO), new ConcentrationRange(55, 124, FIFTY),
new ConcentrationRange(125, 164, ONE_HUNDRED), new ConcentrationRange(165, 204, ONE_HUNDRED_FIFTY),
new ConcentrationRange(205, 404, TWO_HUNDRED), new ConcentrationRange(405, 504, THREE_HUNDRED),
new ConcentrationRange(505, 604, FOUR_HUNDRED));
public static enum SensitiveGroup {
RESPIRATORY,
HEART,
ELDERLY,
CHILDREN,
ASTHMA;
}
public final Set<SensitiveGroup> sensitiveGroups;
private final Unit<?> unit;
private final Set<ConcentrationRange> breakpoints;
private final int scale;
Pollutant(Unit<?> unit, int scale, Set<SensitiveGroup> groups, ConcentrationRange... concentrations) {
this.sensitiveGroups = groups;
this.unit = unit;
this.breakpoints = Set.of(concentrations);
this.scale = scale;
}
public State toQuantity(double idx) {
for (ConcentrationRange concentration : breakpoints) {
double equivalent = concentration.getConcentration(idx);
if (equivalent != -1) {
return new QuantityType<>(BigDecimal.valueOf(equivalent).setScale(scale, RoundingMode.HALF_UP), unit);
}
}
return UnDefType.UNDEF;
}
}

View File

@ -10,9 +10,8 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -20,17 +19,16 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link AirQualityJsonCity} is responsible for storing
* The {@link AirQualityCity} is responsible for storing
* the "city" node from the waqi.org JSON response
*
* @author Kuba Wolanin - Initial contribution
*/
@NonNullByDefault
public class AirQualityJsonCity {
public class AirQualityCity {
private String name = "";
private @Nullable String url;
private List<Double> geo = new ArrayList<>();
private List<Double> geo = List.of();
public String getName() {
return name;

View File

@ -10,32 +10,30 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airquality.internal.api.Pollutant;
/**
* The {@link AirQualityJsonData} is responsible for storing
* The {@link AirQualityData} is responsible for storing
* the "data" node from the waqi.org JSON response
*
* @author Kuba Wolanin - Initial contribution
*/
@NonNullByDefault
public class AirQualityJsonData {
public class AirQualityData {
private int aqi;
private int idx;
private @NonNullByDefault({}) AirQualityJsonTime time;
private @NonNullByDefault({}) AirQualityJsonCity city;
private List<Attribute> attributions = new ArrayList<>();
private Map<String, AirQualityValue> iaqi = new HashMap<>();
private @NonNullByDefault({}) AirQualityTime time;
private @NonNullByDefault({}) AirQualityCity city;
private List<Attribution> attributions = List.of();
private Map<String, AirQualityValue> iaqi = Map.of();
private String dominentpol = "";
/**
@ -61,7 +59,7 @@ public class AirQualityJsonData {
*
* @return {AirQualityJsonTime}
*/
public AirQualityJsonTime getTime() {
public AirQualityTime getTime() {
return time;
}
@ -70,20 +68,18 @@ public class AirQualityJsonData {
*
* @return {AirQualityJsonCity}
*/
public AirQualityJsonCity getCity() {
public AirQualityCity getCity() {
return city;
}
/**
* Collects a list of attributions (vendors making data available)
* and transforms it into readable string.
* Currently displayed in Thing Status description when ONLINE
*
* @return {String}
*/
public String getAttributions() {
String attributionsString = attributions.stream().map(Attribute::getName).collect(Collectors.joining(", "));
return "Attributions : " + attributionsString;
return attributions.stream().map(Attribution::getName).collect(Collectors.joining(", "));
}
public String getDominentPol() {
@ -92,9 +88,10 @@ public class AirQualityJsonData {
public double getIaqiValue(String key) {
AirQualityValue result = iaqi.get(key);
if (result != null) {
return result.getValue();
}
return -1;
return result != null ? result.getValue() : -1;
}
public double getIaqiValue(Pollutant pollutant) {
return getIaqiValue(pollutant.name().toLowerCase());
}
}

View File

@ -10,20 +10,20 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* The {@link AirQualityJsonResponse} is the Java class used to map the JSON
* The {@link AirQualityResponse} is the Java class used to map the JSON
* response to the aqicn.org request.
*
* @author Kuba Wolanin - Initial contribution
*/
@NonNullByDefault
public class AirQualityJsonResponse {
public class AirQualityResponse {
public static enum ResponseStatus {
NONE,
@ -34,15 +34,13 @@ public class AirQualityJsonResponse {
}
private ResponseStatus status = ResponseStatus.NONE;
@SerializedName("data")
private @NonNullByDefault({}) AirQualityJsonData data;
private @NonNullByDefault({}) AirQualityData data;
public ResponseStatus getStatus() {
return status;
}
public AirQualityJsonData getData() {
public AirQualityData getData() {
return data;
}
}

View File

@ -10,32 +10,22 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* The {@link AirQualityJsonTime} is responsible for storing
* The {@link AirQualityTime} is responsible for storing
* the "time" node from the waqi.org JSON response
*
* @author Kuba Wolanin - Initial contribution
* @author Gaël L'hopital - Use ZonedDateTime instead of Calendar
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class AirQualityJsonTime {
@SerializedName("s")
private String dateString = "";
@SerializedName("tz")
private String timeZone = "";
private String iso = "";
public class AirQualityTime {
private String iso = ""; // ISO representation of the timestamp, including TZ
/**
* Get observation time

View File

@ -10,24 +10,20 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* Wrapper type around values reported by aqicn index values.
*
* @author Łukasz Dywicki - Initial contribution
*/
@NonNullByDefault
public class AirQualityValue {
@SerializedName("v")
private double value;
class AirQualityValue {
private double v;
public double getValue() {
return value;
return v;
}
}

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.json;
package org.openhab.binding.airquality.internal.api.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -21,8 +21,7 @@ import org.eclipse.jdt.annotation.Nullable;
* @author Łukasz Dywicki - Initial contribution
*/
@NonNullByDefault
public class Attribute {
class Attribution {
private @NonNullByDefault({}) String name;
private @Nullable String url;
private @Nullable String logo;

View File

@ -10,10 +10,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal;
package org.openhab.binding.airquality.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.AirQualityException;
/**
* The {@link AirQualityConfiguration} is the class used to match the
@ -23,11 +23,19 @@ import org.eclipse.jdt.annotation.Nullable;
*/
@NonNullByDefault
public class AirQualityConfiguration {
public static final String LOCATION = "location";
public static final String STATION_ID = "stationId";
public String apikey = "";
public String location = "";
public @Nullable Integer stationId;
public int stationId = 0;
public int refresh = 60;
public void checkValid() throws AirQualityException {
if (location.trim().isEmpty() && stationId == 0) {
throw new AirQualityException("Either 'location' or 'stationId' is mandatory and must be configured");
}
if (refresh < 30) {
throw new AirQualityException("Parameter 'refresh' must be at least 30 minutes");
}
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.api.Pollutant.SensitiveGroup;
/**
* The {@link SensitiveGroupConfiguration} is the class used to match the
* sensitive-group channel configuration.
*
* @author Gaël L"hopital - Initial contribution
*/
@NonNullByDefault
public class SensitiveGroupConfiguration {
private String group = "RESPIRATORY";
public @Nullable SensitiveGroup asSensitiveGroup() {
try {
SensitiveGroup value = SensitiveGroup.valueOf(group);
return value;
} catch (IllegalArgumentException e) {
return null;
}
}
}

View File

@ -13,26 +13,24 @@
package org.openhab.binding.airquality.internal.discovery;
import static org.openhab.binding.airquality.internal.AirQualityBindingConstants.*;
import static org.openhab.binding.airquality.internal.AirQualityConfiguration.LOCATION;
import static org.openhab.binding.airquality.internal.config.AirQualityConfiguration.LOCATION;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.handler.AirQualityBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.library.types.PointType;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Activate;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,80 +41,60 @@ import org.slf4j.LoggerFactory;
*/
@Component(service = DiscoveryService.class, configurationPid = "discovery.airquality")
@NonNullByDefault
public class AirQualityDiscoveryService extends AbstractDiscoveryService {
public class AirQualityDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
private static final int DISCOVER_TIMEOUT_SECONDS = 2;
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_STATION);
private final Logger logger = LoggerFactory.getLogger(AirQualityDiscoveryService.class);
private static final int DISCOVER_TIMEOUT_SECONDS = 10;
private static final int LOCATION_CHANGED_CHECK_INTERVAL = 60;
private final LocationProvider locationProvider;
private @Nullable ScheduledFuture<?> discoveryJob;
private @Nullable PointType previousLocation;
private @Nullable LocationProvider locationProvider;
private @Nullable AirQualityBridgeHandler bridgeHandler;
/**
* Creates a AirQualityDiscoveryService with enabled autostart.
*/
@Activate
public AirQualityDiscoveryService(@Reference LocationProvider locationProvider) {
super(SUPPORTED_THING_TYPES, DISCOVER_TIMEOUT_SECONDS, true);
this.locationProvider = locationProvider;
public AirQualityDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
}
@Override
protected void activate(@Nullable Map<String, Object> configProperties) {
super.activate(configProperties);
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof AirQualityBridgeHandler) {
final AirQualityBridgeHandler bridgeHandler = (AirQualityBridgeHandler) handler;
this.bridgeHandler = bridgeHandler;
this.locationProvider = bridgeHandler.getLocationProvider();
}
}
@Override
@Modified
protected void modified(@Nullable Map<String, Object> configProperties) {
super.modified(configProperties);
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void deactivate() {
super.deactivate();
}
@Override
protected void startScan() {
logger.debug("Starting Air Quality discovery scan");
PointType location = locationProvider.getLocation();
if (location == null) {
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
return;
}
createResults(location);
}
@Override
protected void startBackgroundDiscovery() {
if (discoveryJob == null) {
discoveryJob = scheduler.scheduleWithFixedDelay(() -> {
PointType currentLocation = locationProvider.getLocation();
if (currentLocation != null && !Objects.equals(currentLocation, previousLocation)) {
logger.debug("Location has been changed from {} to {}: Creating new discovery results",
previousLocation, currentLocation);
createResults(currentLocation);
previousLocation = currentLocation;
}
}, 0, LOCATION_CHANGED_CHECK_INTERVAL, TimeUnit.SECONDS);
logger.debug("Scheduled Air Qualitylocation-changed job every {} seconds", LOCATION_CHANGED_CHECK_INTERVAL);
}
}
public void createResults(PointType location) {
ThingUID localAirQualityThing = new ThingUID(THING_TYPE_AQI, LOCAL);
Map<String, Object> properties = new HashMap<>();
properties.put(LOCATION, String.format("%s,%s", location.getLatitude(), location.getLongitude()));
thingDiscovered(DiscoveryResultBuilder.create(localAirQualityThing).withLabel("Local Air Quality")
.withProperties(properties).build());
}
@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stopping Air Quality background discovery");
ScheduledFuture<?> job = this.discoveryJob;
if (job != null && !job.isCancelled()) {
if (job.cancel(true)) {
discoveryJob = null;
logger.debug("Stopped Air Quality background discovery");
LocationProvider provider = locationProvider;
if (provider != null) {
PointType location = provider.getLocation();
AirQualityBridgeHandler bridge = this.bridgeHandler;
if (location == null || bridge == null) {
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
return;
}
createResults(location, bridge.getThing().getUID());
}
}
public void createResults(PointType location, ThingUID bridgeUID) {
ThingUID localAirQualityThing = new ThingUID(THING_TYPE_STATION, bridgeUID, LOCAL);
thingDiscovered(DiscoveryResultBuilder.create(localAirQualityThing).withLabel("Local Air Quality")
.withProperty(LOCATION, String.format("%s,%s", location.getLatitude(), location.getLongitude()))
.withBridge(bridgeUID).build());
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.handler;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.api.ApiBridge;
import org.openhab.binding.airquality.internal.discovery.AirQualityDiscoveryService;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
/**
* The {@link AirQualityBridgeHandler} is responsible for handling communication
* with the service via the API.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public class AirQualityBridgeHandler extends BaseBridgeHandler {
private final LocationProvider locationProvider;
private @Nullable ApiBridge apiBridge;
public AirQualityBridgeHandler(Bridge bridge, LocationProvider locationProvider) {
super(bridge);
this.locationProvider = locationProvider;
}
@Override
public void initialize() {
String apiKey = (String) getConfig().get("apiKey");
if (apiKey != null && apiKey.length() != 0) {
apiBridge = new ApiBridge(apiKey);
updateStatus(ThingStatus.ONLINE);
} else {
apiBridge = null;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/null-or-empty-api-key");
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// We do nothing
}
public @Nullable ApiBridge getApiBridge() {
return apiBridge;
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(AirQualityDiscoveryService.class);
}
public LocationProvider getLocationProvider() {
return locationProvider;
}
}

View File

@ -1,285 +0,0 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.handler;
import static org.openhab.binding.airquality.internal.AirQualityBindingConstants.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.AirQualityConfiguration;
import org.openhab.binding.airquality.internal.json.AirQualityJsonData;
import org.openhab.binding.airquality.internal.json.AirQualityJsonResponse;
import org.openhab.binding.airquality.internal.json.AirQualityJsonResponse.ResponseStatus;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* The {@link AirQualityHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Kuba Wolanin - Initial contribution
* @author Łukasz Dywicki - Initial contribution
*/
@NonNullByDefault
public class AirQualityHandler extends BaseThingHandler {
private static final String URL = "http://api.waqi.info/feed/%QUERY%/?token=%apikey%";
private static final int REQUEST_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);
private final Logger logger = LoggerFactory.getLogger(AirQualityHandler.class);
private @Nullable ScheduledFuture<?> refreshJob;
private final Gson gson;
private int retryCounter = 0;
private final TimeZoneProvider timeZoneProvider;
public AirQualityHandler(Thing thing, Gson gson, TimeZoneProvider timeZoneProvider) {
super(thing);
this.gson = gson;
this.timeZoneProvider = timeZoneProvider;
}
@Override
public void initialize() {
logger.debug("Initializing Air Quality handler.");
AirQualityConfiguration config = getConfigAs(AirQualityConfiguration.class);
logger.debug("config apikey = (omitted from logging)");
logger.debug("config location = {}", config.location);
logger.debug("config stationId = {}", config.stationId);
logger.debug("config refresh = {}", config.refresh);
List<String> errorMsg = new ArrayList<>();
if (config.apikey.trim().isEmpty()) {
errorMsg.add("Parameter 'apikey' is mandatory and must be configured");
}
if (config.location.trim().isEmpty() && config.stationId == null) {
errorMsg.add("Parameter 'location' or 'stationId' is mandatory and must be configured");
}
if (config.refresh < 30) {
errorMsg.add("Parameter 'refresh' must be at least 30 minutes");
}
if (errorMsg.isEmpty()) {
ScheduledFuture<?> job = this.refreshJob;
if (job == null || job.isCancelled()) {
refreshJob = scheduler.scheduleWithFixedDelay(this::updateAndPublishData, 0, config.refresh,
TimeUnit.MINUTES);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.join(", ", errorMsg));
}
}
private void updateAndPublishData() {
retryCounter = 0;
AirQualityJsonData aqiResponse = getAirQualityData();
if (aqiResponse != null) {
// Update all channels from the updated AQI data
getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID().getId())).forEach(channel -> {
String channelId = channel.getUID().getId();
State state = getValue(channelId, aqiResponse);
updateState(channelId, state);
});
}
}
@Override
public void dispose() {
logger.debug("Disposing the Air Quality handler.");
ScheduledFuture<?> job = this.refreshJob;
if (job != null && !job.isCancelled()) {
job.cancel(true);
refreshJob = null;
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateAndPublishData();
} else {
logger.debug("The Air Quality binding is read-only and can not handle command {}", command);
}
}
/**
* Build request URL from configuration data
*
* @return a valid URL for the aqicn.org service
*/
private String buildRequestURL() {
AirQualityConfiguration config = getConfigAs(AirQualityConfiguration.class);
String location = config.location.trim();
Integer stationId = config.stationId;
String geoStr = "geo:" + location.replace(" ", "").replace(",", ";").replace("\"", "").replace("'", "").trim();
String urlStr = URL.replace("%apikey%", config.apikey.trim());
return urlStr.replace("%QUERY%", stationId == null ? geoStr : "@" + stationId);
}
/**
* Request new air quality data to the aqicn.org service
*
* @param location geo-coordinates from config
* @param stationId station ID from config
* @return the air quality data object mapping the JSON response or null in case of error
*/
private @Nullable AirQualityJsonData getAirQualityData() {
String errorMsg;
String urlStr = buildRequestURL();
logger.debug("URL = {}", urlStr);
try {
String response = HttpUtil.executeUrl("GET", urlStr, null, null, null, REQUEST_TIMEOUT_MS);
logger.debug("aqiResponse = {}", response);
AirQualityJsonResponse result = gson.fromJson(response, AirQualityJsonResponse.class);
if (result.getStatus() == ResponseStatus.OK) {
AirQualityJsonData data = result.getData();
String attributions = data.getAttributions();
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, attributions);
return data;
} else {
retryCounter++;
if (retryCounter == 1) {
logger.warn("Error in aqicn.org, retrying once");
return getAirQualityData();
}
errorMsg = "Missing data sub-object";
logger.warn("Error in aqicn.org response: {}", errorMsg);
}
} catch (IOException e) {
errorMsg = e.getMessage();
} catch (JsonSyntaxException e) {
errorMsg = "Configuration is incorrect";
logger.warn("Error running aqicn.org request: {}", errorMsg);
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errorMsg);
return null;
}
public State getValue(String channelId, AirQualityJsonData aqiResponse) {
String[] fields = channelId.split("#");
switch (fields[0]) {
case AQI:
return new DecimalType(aqiResponse.getAqi());
case AQIDESCRIPTION:
return getAqiDescription(aqiResponse.getAqi());
case PM25:
case PM10:
case O3:
case NO2:
case CO:
case SO2:
double value = aqiResponse.getIaqiValue(fields[0]);
return value != -1 ? new DecimalType(value) : UnDefType.UNDEF;
case TEMPERATURE:
double temp = aqiResponse.getIaqiValue("t");
return temp != -1 ? new QuantityType<>(temp, API_TEMPERATURE_UNIT) : UnDefType.UNDEF;
case PRESSURE:
double press = aqiResponse.getIaqiValue("p");
return press != -1 ? new QuantityType<>(press, API_PRESSURE_UNIT) : UnDefType.UNDEF;
case HUMIDITY:
double hum = aqiResponse.getIaqiValue("h");
return hum != -1 ? new QuantityType<>(hum, API_HUMIDITY_UNIT) : UnDefType.UNDEF;
case LOCATIONNAME:
return new StringType(aqiResponse.getCity().getName());
case STATIONID:
return new DecimalType(aqiResponse.getStationId());
case STATIONLOCATION:
return new PointType(aqiResponse.getCity().getGeo());
case OBSERVATIONTIME:
return new DateTimeType(
aqiResponse.getTime().getObservationTime().withZoneSameLocal(timeZoneProvider.getTimeZone()));
case DOMINENTPOL:
return new StringType(aqiResponse.getDominentPol());
case AQI_COLOR:
return getAsHSB(aqiResponse.getAqi());
default:
return UnDefType.UNDEF;
}
}
/**
* Interprets the current aqi value within the ranges;
* Returns AQI in a human readable format
*
* @return
*/
public State getAqiDescription(int index) {
if (index >= 300) {
return HAZARDOUS;
} else if (index >= 201) {
return VERY_UNHEALTHY;
} else if (index >= 151) {
return UNHEALTHY;
} else if (index >= 101) {
return UNHEALTHY_FOR_SENSITIVE;
} else if (index >= 51) {
return MODERATE;
} else if (index > 0) {
return GOOD;
}
return UnDefType.UNDEF;
}
private State getAsHSB(int index) {
State state = getAqiDescription(index);
if (state == HAZARDOUS) {
return HSBType.fromRGB(343, 100, 49);
} else if (state == VERY_UNHEALTHY) {
return HSBType.fromRGB(280, 100, 60);
} else if (state == UNHEALTHY) {
return HSBType.fromRGB(345, 100, 80);
} else if (state == UNHEALTHY_FOR_SENSITIVE) {
return HSBType.fromRGB(30, 80, 100);
} else if (state == MODERATE) {
return HSBType.fromRGB(50, 80, 100);
} else if (state == GOOD) {
return HSBType.fromRGB(160, 100, 60);
}
return UnDefType.UNDEF;
}
}

View File

@ -0,0 +1,298 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airquality.internal.handler;
import static org.openhab.binding.airquality.internal.AirQualityBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airquality.internal.AirQualityException;
import org.openhab.binding.airquality.internal.api.ApiBridge;
import org.openhab.binding.airquality.internal.api.Appreciation;
import org.openhab.binding.airquality.internal.api.Index;
import org.openhab.binding.airquality.internal.api.Pollutant;
import org.openhab.binding.airquality.internal.api.Pollutant.SensitiveGroup;
import org.openhab.binding.airquality.internal.api.dto.AirQualityData;
import org.openhab.binding.airquality.internal.config.AirQualityConfiguration;
import org.openhab.binding.airquality.internal.config.SensitiveGroupConfiguration;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AirQualityStationHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Kuba Wolanin - Initial contribution
* @author Łukasz Dywicki - Initial contribution
*/
@NonNullByDefault
public class AirQualityStationHandler extends BaseThingHandler {
private final @NonNullByDefault({}) ClassLoader classLoader = AirQualityStationHandler.class.getClassLoader();
private final Logger logger = LoggerFactory.getLogger(AirQualityStationHandler.class);
private final TimeZoneProvider timeZoneProvider;
private final LocationProvider locationProvider;
private @Nullable ScheduledFuture<?> refreshJob;
public AirQualityStationHandler(Thing thing, TimeZoneProvider timeZoneProvider, LocationProvider locationProvider) {
super(thing);
this.timeZoneProvider = timeZoneProvider;
this.locationProvider = locationProvider;
}
@Override
public void initialize() {
logger.debug("Initializing Air Quality handler.");
if (thing.getProperties().isEmpty()) {
discoverAttributes();
}
AirQualityConfiguration config = getConfigAs(AirQualityConfiguration.class);
try {
config.checkValid();
freeRefreshJob();
refreshJob = scheduler.scheduleWithFixedDelay(this::updateAndPublishData, 0, config.refresh,
TimeUnit.MINUTES);
} catch (AirQualityException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
}
}
private void discoverAttributes() {
getAirQualityData().ifPresent(data -> {
// Update thing properties
Map<String, String> properties = new HashMap<>();
properties.put(ATTRIBUTIONS, data.getAttributions());
PointType serverLocation = locationProvider.getLocation();
if (serverLocation != null) {
PointType stationLocation = new PointType(data.getCity().getGeo());
double distance = serverLocation.distanceFrom(stationLocation).doubleValue();
properties.put(DISTANCE, new QuantityType<>(distance / 1000, KILO(SIUnits.METRE)).toString());
}
// Search and remove missing pollutant channels
List<Channel> channels = new ArrayList<>(getThing().getChannels());
Stream.of(Pollutant.values()).forEach(pollutant -> {
String groupName = pollutant.name().toLowerCase();
double value = data.getIaqiValue(pollutant);
channels.removeIf(channel -> value == -1 && groupName.equals(channel.getUID().getGroupId()));
});
// Update thing configuration
Configuration config = editConfiguration();
config.put(AirQualityConfiguration.STATION_ID, data.getStationId());
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(channels).withConfiguration(config).withProperties(properties)
.withLocation(data.getCity().getName());
updateThing(thingBuilder.build());
});
}
private void updateAndPublishData() {
getAirQualityData().ifPresent(data -> {
getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID().getId())).forEach(channel -> {
State state;
ChannelUID channelUID = channel.getUID();
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
if (channelTypeUID != null && SENSITIVE.equals(channelTypeUID.getId().toString())) {
SensitiveGroupConfiguration configuration = channel.getConfiguration()
.as(SensitiveGroupConfiguration.class);
state = getSensitive(configuration.asSensitiveGroup(), data);
} else {
state = getValue(channelUID.getIdWithoutGroup(), channelUID.getGroupId(), data);
}
updateState(channelUID, state);
});
});
}
@Override
public void dispose() {
logger.debug("Disposing the Air Quality handler.");
freeRefreshJob();
}
private void freeRefreshJob() {
ScheduledFuture<?> job = this.refreshJob;
if (job != null && !job.isCancelled()) {
job.cancel(true);
refreshJob = null;
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateAndPublishData();
return;
}
logger.debug("The Air Quality binding is read-only and can not handle command {}", command);
}
/**
* Request new air quality data to the aqicn.org service
*
* @return an optional air quality data object mapping the JSON response
*/
private Optional<AirQualityData> getAirQualityData() {
AirQualityData result = null;
ApiBridge apiBridge = getApiBridge();
if (apiBridge != null) {
AirQualityConfiguration config = getConfigAs(AirQualityConfiguration.class);
try {
result = apiBridge.getData(config.stationId, config.location, 0);
updateStatus(ThingStatus.ONLINE);
} catch (AirQualityException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
return Optional.ofNullable(result);
}
private @Nullable ApiBridge getApiBridge() {
Bridge bridge = this.getBridge();
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
BridgeHandler handler = bridge.getHandler();
if (handler instanceof AirQualityBridgeHandler) {
return ((AirQualityBridgeHandler) handler).getApiBridge();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/incorrect-bridge");
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
return null;
}
private State indexedValue(String channelId, double idx, @Nullable Pollutant pollutant) {
Index index = Index.find(idx);
if (index != null) {
switch (channelId) {
case INDEX:
return new DecimalType(idx);
case VALUE:
return pollutant != null ? pollutant.toQuantity(idx) : UnDefType.UNDEF;
case ICON:
byte[] bytes = getResource(String.format("picto/%s.svg", index.getCategory().name().toLowerCase()));
return bytes != null ? new RawType(bytes, "image/svg+xml") : UnDefType.UNDEF;
case COLOR:
return index.getCategory().getColor();
case ALERT_LEVEL:
return new DecimalType(index.getCategory().ordinal());
}
}
return UnDefType.UNDEF;
}
private State getSensitive(@Nullable SensitiveGroup sensitiveGroup, AirQualityData data) {
if (sensitiveGroup != null) {
int threshHold = Appreciation.UNHEALTHY_FSG.ordinal();
for (Pollutant pollutant : Pollutant.values()) {
Index index = Index.find(data.getIaqiValue(pollutant));
if (index != null && pollutant.sensitiveGroups.contains(sensitiveGroup)
&& index.getCategory().ordinal() >= threshHold) {
return OnOffType.ON;
}
}
return OnOffType.OFF;
}
return UnDefType.NULL;
}
private State getValue(String channelId, @Nullable String groupId, AirQualityData data) {
switch (channelId) {
case TEMPERATURE:
double temp = data.getIaqiValue("t");
return temp != -1 ? new QuantityType<>(temp, SIUnits.CELSIUS) : UnDefType.UNDEF;
case PRESSURE:
double press = data.getIaqiValue("p");
return press != -1 ? new QuantityType<>(press, HECTO(SIUnits.PASCAL)) : UnDefType.UNDEF;
case HUMIDITY:
double hum = data.getIaqiValue("h");
return hum != -1 ? new QuantityType<>(hum, Units.PERCENT) : UnDefType.UNDEF;
case TIMESTAMP:
return new DateTimeType(
data.getTime().getObservationTime().withZoneSameLocal(timeZoneProvider.getTimeZone()));
case DOMINENT:
return new StringType(data.getDominentPol());
case DEW_POINT:
double dp = data.getIaqiValue("dew");
return dp != -1 ? new QuantityType<>(dp, SIUnits.CELSIUS) : UnDefType.UNDEF;
case WIND_SPEED:
double w = data.getIaqiValue("w");
return w != -1 ? new QuantityType<>(w, Units.METRE_PER_SECOND) : UnDefType.UNDEF;
default:
if (groupId != null) {
double idx = -1;
Pollutant pollutant = null;
if (AQI.equals(groupId)) {
idx = data.getAqi();
} else {
pollutant = Pollutant.valueOf(groupId.toUpperCase());
idx = data.getIaqiValue(pollutant);
}
return indexedValue(channelId, idx, pollutant);
}
return UnDefType.UNDEF;
}
}
private byte @Nullable [] getResource(String iconPath) {
try (InputStream stream = classLoader.getResourceAsStream(iconPath)) {
return stream != null ? stream.readAllBytes() : null;
} catch (IOException e) {
logger.warn("Unable to load ressource '{}' : {}", iconPath, e.getMessage());
}
return null;
}
}

View File

@ -4,6 +4,5 @@
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Air Quality Binding</name>
<description>Measure Air Quality Index and details about pollution particles for a given location</description>
</binding:binding>

View File

@ -0,0 +1,62 @@
# binding
binding.airquality.name = Air Quality Binding
binding.airquality.description = Measure Air Quality Index and details about pollution particles for a given location.
# thing types
thing-type.airquality.api.label = Air Quality API
thing-type.airquality.api.description = Bridge to the Air Quality API service.
thing-type.airquality.station.label = Air Quality Station
thing-type.airquality.station.description = Provides various air quality data from the World Air Quality Project. In order to receive the data, you must register an account on http://aqicn.org/data-platform/token/ and get your API token.
# thing types config
thing-type.config.airquality.api.apiKey.label = API Key
thing-type.config.airquality.api.apiKey.description = Data-platform token to access the AQIcn.org service.
thing-type.config.airquality.station.location.label = Location
thing-type.config.airquality.station.location.description = Your geo coordinates separated with comma (e.g. "37.8,-122.4").
thing-type.config.airquality.station.stationId.label = Station ID
thing-type.config.airquality.station.stationId.description = Fill in case you want to receive data from the specific station.
thing-type.config.airquality.station.refresh.label = Refresh Interval
thing-type.config.airquality.station.refresh.description = Specifies the refresh interval in minutes.
# Channel groups labels
pm25ChannelGroupLabel = PM 2.5 - Particles less than 2.5 µm in diameter.
pm10ChannelGroupLabel = PM 10 - Coarse Dust Particles.
no2ChannelGroupLabel = NO2 - Nitrogen Dioxide.
so2ChannelGroupLabel = SO2 - Sulfur Dioxide.
coChannelGroupLabel = CO - Carbon Monoxide.
o3ChannelGroupLabel = O3 - Ozone.
aqiChannelGroupLabel = AQI - Synthetic Air Quality Index.
weatherChannelGroupLabel = Weather Data
# Channel types labels
timestampChannelLabel = Observation Time
timestampChannelDescription = Observation date and time.
dominentChannelLabel = Dominent Pollutant
dewPointLabel = Dew-Point Temperature
dewPointDescription = Forecasted dew-point temperature.
windSpeedLabel = Wind Speed
pictoChannelLabel = Pictogram
pictoChannelDescription = Pictogram associated to alert level.
colorChannelLabel = AQI Color
colorChannelDescription = Color associated to given AQI Index.
# Channel options values
alertLevelChannelLabel = Alert Level
alertLevelChannelDescription = Alert level associated to Air Quality Index scale.
alertLevelOption0 = Good
alertLevelOption1 = Moderate
alertLevelOption2 = Unhealthy for Sensitive Groups
alertLevelOption3 = Unhealthy
alertLevelOption4 = Very Unhealthy
alertLevelOption5 = Hazardous
pollutantPm25 = Fine particles
pollutantPm10 = Coarse dust particles
pollutantO3 = Ozone
pollutantNO2 = Nitrogen Dioxide
pollutantCO = Carbon Monoxide
pollutantSO2 = Sulfur Dioxide
# Error messages
null-or-empty-api-key = Null or empty API key
incorrect-bridge = Wrong bridge type

View File

@ -1,30 +1,62 @@
# binding
binding.airquality.name = Extension Air Quality
binding.airquality.description = Indice de qualité de l'air et informations sur la pollution aux particules pour un emplacement donné.
binding.airquality.description = Indice de qualité de l'air et informations sur la pollution aux particules pour un emplacement donné.
# thing types
thing-type.airquality.aqi.label = Qualité de l'air
thing-type.airquality.aqi.description = Fournit diverses données sur la qualité de l'air du World Air Quality Project. Pour recevoir les données, vous devez créer un compte sur http://aqicn.org/data-platform/token/ pour obtenir votre token API.
thing-type.airquality.api.label = API Air Quality
thing-type.airquality.api.description = Passerelle vers le service de données Air Quality.
thing-type.airquality.station.label = Qualité de l'air
thing-type.airquality.station.description = Fournit diverses données sur la qualité de l'air du World Air Quality Project. Pour recevoir les données, vous devez créer un compte sur http://aqicn.org/data-platform/token/ pour obtenir votre token API.
channel-type.airquality.aqiLevel.label = Indice
channel-type.airquality.aqiDescription.label = Appréciation
channel-type.airquality.observationTime.label = Heure d'observation
channel-type.airquality.temperature.label = Température
channel-type.airquality.pressure.label = Pression
channel-type.airquality.humidity.label = Humidité
channel-type.airquality.dominentpol.label = Polluant principal
# thing types config
thing-type.config.airquality.api.apiKey.label = Clé API
thing-type.config.airquality.api.apiKey.description = Jeton d'accès aux données du service AQIcn.org.
thing-type.config.airquality.station.location.label = Localisation
thing-type.config.airquality.station.location.description = Coordonnées géographiques (séparées par une virgule : "37.8,-122.4").
thing-type.config.airquality.station.stationId.label = ID Station
thing-type.config.airquality.station.stationId.description = Renseignez cette valeur si vous souhaitez les données d'une station spécifique.
thing-type.config.airquality.station.refresh.label = Période de mise à jour
thing-type.config.airquality.station.refresh.description = Définisser l'intervalle de mise à jour en minutes.
# Channel groups labels
pm25ChannelGroupLabel = PM 2.5 - Particules de diametre inférieur à 2.5 µm.
pm10ChannelGroupLabel = PM 10 - Particules fines de poussière.
no2ChannelGroupLabel = NO2 - Dioxyde d'azote.
so2ChannelGroupLabel = SO2 - Dioxyde de soufre.
coChannelGroupLabel = CO - Monoxyde de carbone.
o3ChannelGroupLabel = O3 - Ozone.
aqiChannelGroupLabel = AQI - Indice Global de Qualité de l'Air.
weatherChannelGroupLabel = Données Méteo.
channel-type.airquality.aqiDescription.state.option.GOOD = Bonne
channel-type.airquality.aqiDescription.state.option.MODERATE = Modérée
channel-type.airquality.aqiDescription.state.option.UNHEALTHY_FOR_SENSITIVE = Mauvaise pour les groupes sensibles
channel-type.airquality.aqiDescription.state.option.UNHEALTHY = Mauvaise
channel-type.airquality.aqiDescription.state.option.VERY_UNHEALTHY = Très mauvaise
channel-type.airquality.aqiDescription.state.option.HAZARDOUS = Dangereuse
# Channel types labels
timestampChannelLabel = Horodatage
timestampChannelDescription = Date et heure des oObservations.
dominentChannelLabel = Polluant Principal
dewPointLabel = Température de Rosée
dewPointDescription = Température du point de Rosée.
windSpeedLabel = Vitesse du Vent
pictoChannelLabel = Pictogramme
pictoChannelDescription = Pictogramme associé au niveau d'alerte.
colorChannelLabel = Couleur AQI
colorChannelDescription = Couleur associée à l'Indice de Qualité d'Air.
channel-type.airquality.dominentPol.state.option.pm25 = Particules fines
channel-type.airquality.dominentPol.state.option.pm10 = Particules de poussière
channel-type.airquality.dominentPol.state.option.o3 = Ozone
channel-type.airquality.dominentPol.state.option.no2 = Dioxyde d'azote
channel-type.airquality.dominentPol.state.option.co = Monoxyde de carbone
channel-type.airquality.dominentPol.state.option.so2 = Dioxyde de soufre
# Channel options values
alertLevelChannelLabel = Niveau d'Alerte
alertLevelChannelDescription = Niveau d'alerte associé à l'Indice de Qualité d'Air.
alertLevelOption0 = Bonne
alertLevelOption1 = Modérée
alertLevelOption2 = Mauvaise pour les groupes sensibles
alertLevelOption3 = Mauvaise
alertLevelOption4 = Très mauvaise
alertLevelOption5 = Dangereuse
pollutantPm25 = Particules de diametre inférieur à 2.5 µm.
pollutantPm10 = Particules fines de poussière.
pollutantO3 = Ozone
pollutantNO2 = Dioxyde d'azote.
pollutantCO = Monoxyde de carbone.
pollutantSO2 = Dioxyde de soufre.
# Error messages
null-or-empty-api-key = Clef API nulle ou vide
incorrect-bridge = Le type de bridge est incorrect

View File

@ -4,55 +4,58 @@
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">
<!-- Air Quality Thing -->
<thing-type id="aqi">
<label>Air Quality</label>
<description>
Provides various air quality data from the World Air Quality Project.
In order to receive the data, you
must register an account on http://aqicn.org/data-platform/token/ and get your API
token.
</description>
<channels>
<channel id="aqiLevel" typeId="aqiLevel"/>
<channel id="aqiColor" typeId="aqiColor"/>
<channel id="aqiDescription" typeId="aqiDescription"/>
<channel id="pm25" typeId="pm25"/>
<channel id="pm10" typeId="pm10"/>
<channel id="o3" typeId="o3"/>
<channel id="no2" typeId="no2"/>
<channel id="co" typeId="co"/>
<channel id="so2" typeId="so2"/>
<channel id="locationName" typeId="locationName"/>
<channel id="stationLocation" typeId="stationLocation"/>
<channel id="stationId" typeId="stationId"/>
<channel id="observationTime" typeId="observationTime"/>
<channel id="temperature" typeId="temperature"/>
<channel id="pressure" typeId="pressure"/>
<channel id="humidity" typeId="humidity"/>
<channel id="dominentpol" typeId="dominentPol"/>
</channels>
<bridge-type id="api">
<label>Air Quality API</label>
<config-description>
<parameter name="apikey" type="text" required="true">
<parameter name="apiKey" type="text" required="true">
<context>password</context>
<label>API Key</label>
<description>Data-platform token to access the AQIcn.org service</description>
</parameter>
</config-description>
</bridge-type>
<!-- Air Quality Thing -->
<thing-type id="station" extensible="sensitive-group">
<supported-bridge-type-refs>
<bridge-type-ref id="api"/>
</supported-bridge-type-refs>
<label>Air Quality Station</label>
<channel-groups>
<channel-group id="aqi" typeId="aqi"/>
<channel-group id="weather" typeId="weather"/>
<channel-group id="pm25" typeId="pollutant-density">
<label>@text/pm25ChannelGroupLabel</label>
</channel-group>
<channel-group id="pm10" typeId="pollutant-density">
<label>@text/pm10ChannelGroupLabel</label>
</channel-group>
<channel-group id="no2" typeId="pollutant-ppb">
<label>@text/no2ChannelGroupLabel</label>
</channel-group>
<channel-group id="so2" typeId="pollutant-ppb">
<label>@text/so2ChannelGroupLabel</label>
</channel-group>
<channel-group id="co" typeId="pollutant-ppb">
<label>@text/coChannelGroupLabel</label>
</channel-group>
<channel-group id="o3" typeId="pollutant-ppb">
<label>@text/o3ChannelGroupLabel</label>
</channel-group>
</channel-groups>
<config-description>
<parameter name="location" type="text" required="false"
pattern="^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)[,]\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$">
<label>Location</label>
<description>Your geo coordinates separated with comma (e.g. "37.8,-122.4").</description>
</parameter>
<parameter name="stationId" type="integer" required="false">
<label>Station ID</label>
<description>Fill only in case you want to receive data from the specific station</description>
<advanced>true</advanced>
</parameter>
<parameter name="refresh" type="integer" min="30" required="false" unit="min">
<label>Refresh Interval</label>
<description>Specifies the refresh interval in minutes.</description>
<advanced>true</advanced>
<default>60</default>
<unitLabel>Minutes</unitLabel>
@ -60,155 +63,172 @@
</config-description>
</thing-type>
<channel-type id="aqiLevel">
<channel-group-type id="aqi">
<label>@text/aqiChannelGroupLabel</label>
<channels>
<channel id="alert-level" typeId="alert-level"/>
<channel id="index" typeId="index"/>
<channel id="timestamp" typeId="timestamp"/>
<channel id="dominent" typeId="dominent"/>
<channel id="icon" typeId="alert-icon"/>
<channel id="color" typeId="alert-color"/>
</channels>
</channel-group-type>
<channel-group-type id="weather">
<label>@text/weatherChannelGroupLabel</label>
<channels>
<channel id="temperature" typeId="system.outdoor-temperature"/>
<channel id="pressure" typeId="system.barometric-pressure"/>
<channel id="humidity" typeId="system.atmospheric-humidity"/>
<channel id="dew-point" typeId="dew-point"/>
<channel id="wind-speed" typeId="system.wind-speed"/>
</channels>
</channel-group-type>
<channel-group-type id="pollutant-density">
<label>Pollutant</label>
<channels>
<channel id="value" typeId="density-value"/>
<channel id="index" typeId="index-adv"/>
<channel id="alert-level" typeId="alert-level"/>
</channels>
</channel-group-type>
<channel-group-type id="pollutant-ppb">
<label>Pollutant</label>
<channels>
<channel id="value" typeId="ppb-value"/>
<channel id="index" typeId="index-adv"/>
<channel id="alert-level" typeId="alert-level"/>
</channels>
</channel-group-type>
<channel-type id="index">
<item-type>Number</item-type>
<label>Air Quality Index</label>
<description></description>
<category>Air Quality Index</category>
<label>Index</label>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true" pattern="%d" min="0" max="500"/>
</channel-type>
<channel-type id="aqiDescription">
<item-type>String</item-type>
<label>AQI Description</label>
<description></description>
<category>AQI Description</category>
<state readOnly="true">
<options>
<option value="GOOD">Good</option>
<option value="MODERATE">Moderate</option>
<option value="UNHEALTHY_FOR_SENSITIVE">Unhealthy for Sensitive Groups</option>
<option value="UNHEALTHY">Unhealthy</option>
<option value="VERY_UNHEALTHY">Very Unhealthy</option>
<option value="HAZARDOUS">Hazardous</option>
</options>
</state>
</channel-type>
<channel-type id="pm25">
<channel-type id="index-adv" advanced="true">
<item-type>Number</item-type>
<label>PM2.5</label>
<description>Fine particles pollution level</description>
<category>PM2.5</category>
<label>Index</label>
<tags>
<tag>Measurement</tag>
</tags>
<state readOnly="true" pattern="%d" min="0" max="500"/>
</channel-type>
<channel-type id="pm10">
<item-type>Number</item-type>
<label>PM10</label>
<description>Coarse dust particles pollution level</description>
<category>PM10</category>
<state readOnly="true" pattern="%d" min="0" max="500"/>
<channel-type id="density-value">
<item-type>Number:Density</item-type>
<label>Measure</label>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="o3">
<item-type>Number</item-type>
<label>O3</label>
<description>Ozone level</description>
<category>O3</category>
<state readOnly="true" pattern="%.1f" min="0" max="500"/>
</channel-type>
<channel-type id="no2">
<item-type>Number</item-type>
<label>NO2</label>
<description>Nitrogen dioxide level</description>
<category>NO2</category>
<state readOnly="true" pattern="%.1f" min="0" max="500"/>
</channel-type>
<channel-type id="co">
<item-type>Number</item-type>
<label>CO</label>
<description>Carbon monoxide level</description>
<category>CO</category>
<state readOnly="true" pattern="%.1f" min="0" max="500"/>
</channel-type>
<channel-type id="so2">
<item-type>Number</item-type>
<label>SO2</label>
<description>Sulfur dioxide level</description>
<category>SO2</category>
<state readOnly="true" pattern="%.1f"/>
</channel-type>
<channel-type id="locationName" advanced="true">
<item-type>String</item-type>
<label>Location</label>
<description>Nearest measuring station location</description>
<category>Location</category>
<state readOnly="true" pattern="%s"/>
</channel-type>
<channel-type id="stationLocation" advanced="true">
<item-type>Location</item-type>
<label>Station Location</label>
<description>Location of the measuring station</description>
<category>Station Location</category>
<state readOnly="true" pattern="%2$s°N,%3$s°W"/>
</channel-type>
<channel-type id="stationId" advanced="true">
<item-type>Number</item-type>
<label>Station ID</label>
<description>Unique measuring station ID</description>
<category>Station ID</category>
<state readOnly="true" pattern="%d"/>
</channel-type>
<channel-type id="observationTime" advanced="true">
<item-type>DateTime</item-type>
<label>Observation Time</label>
<description>Observation date and time</description>
<category>Observation time</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="temperature" advanced="true">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Temperature</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="pressure" advanced="true">
<item-type>Number:Pressure</item-type>
<label>Pressure</label>
<description>Current Pressure</description>
<category>Pressure</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="humidity" advanced="true">
<channel-type id="ppb-value">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>Current humidity</description>
<category>Humidity</category>
<state readOnly="true" min="0" max="100" pattern="%.2f %unit%"/>
<label>Measure</label>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="dominentPol">
<item-type>String</item-type>
<label>Dominent Polutor</label>
<channel-type id="alert-level">
<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="pm25">Fine particles</option>
<option value="pm10">Coarse dust particles</option>
<option value="o3">Ozone</option>
<option value="no2">Nitrogen Dioxide</option>
<option value="co">Carbon Monoxide</option>
<option value="so2">Sulfur Dioxide</option>
<option value="0">@text/alertLevelOption0</option>
<option value="1">@text/alertLevelOption1</option>
<option value="2">@text/alertLevelOption2</option>
<option value="3">@text/alertLevelOption3</option>
<option value="4">@text/alertLevelOption4</option>
<option value="5">@text/alertLevelOption5</option>
</options>
</state>
</channel-type>
<channel-type id="aqiColor" advanced="true">
<item-type>Color</item-type>
<label>AQI Color</label>
<description>Color associated to given AQI Index.</description>
<channel-type id="timestamp" advanced="true">
<item-type>DateTime</item-type>
<label>@text/timestampChannelLabel</label>
<description>@text/timestampChannelDescription</description>
<category>time</category>
<tags>
<tag>Status</tag>
<tag>Timestamp</tag>
</tags>
<state readOnly="true"/>
</channel-type>
<channel-type id="dominent">
<item-type>String</item-type>
<label>@text/dominentChannelLabel</label>
<tags>
<tag>Status</tag>
</tags>
<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="dew-point" advanced="true">
<item-type>Number:Temperature</item-type>
<label>@text/dewPointLabel</label>
<description>@text/dewPointDescription</description>
<category>Temperature</category>
<tags>
<tag>Point</tag>
<tag>Temperature</tag>
</tags>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="alert-color" advanced="true">
<item-type>Color</item-type>
<label>@text/colorChannelLabel</label>
<description>@text/colorChannelDescription</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="alert-icon">
<item-type>Image</item-type>
<label>@text/pictoChannelLabel</label>
<description>@text/pictoChannelDescription</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="sensitive-group">
<item-type>Switch</item-type>
<label>Sensitive Group</label>
<description>Checked if sensitive group is exposed to pollutant.</description>
<state readOnly="true"/>
<config-description>
<parameter name="group" type="text">
<label>Group</label>
<description>Defines the kind of sensitivity</description>
<options>
<option value="RESPIRATORY">People with respiratory disease.</option>
<option value="HEART">People with heart disease.</option>
<option value="ELDERLY">The elderly.</option>
<option value="CHILDREN">Children.</option>
<option value="ASTHMA">People with asthma.</option>
</options>
<default>RESPIRATORY</default>
</parameter>
</config-description>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="good-air-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#00e400" stroke="#00e400" stroke-width="2" opacity="1.00" d="M 306.143 12.125 C 349.223 9.135 392.903 15.025 433.623 29.385 C 471.263 42.665 506.383 63.115 536.423 89.395 C 593.563 138.785 632.063 209.375 641.783 284.345 C 650.263 346.445 639.623 411.065 611.263 466.985 C 588.543 512.235 554.663 551.805 513.573 581.365 C 465.893 615.905 408.383 636.675 349.623 640.475 C 307.233 643.405 264.223 637.835 224.063 623.905 C 153.253 599.745 91.793 549.415 54.083 484.805 C 33.383 449.715 19.703 410.505 14.083 370.155 C 7.803 325.485 11.193 279.455 24.183 236.255 C 35.883 197.155 55.413 160.445 81.143 128.775 C 109.873 93.325 146.343 64.175 187.283 44.015 C 224.303 25.725 264.943 14.805 306.143 12.125 M 256.393 98.485 C 231.103 104.515 208.583 119.915 192.323 140.045 C 175.843 160.115 165.253 184.365 158.433 209.235 C 157.343 213.885 155.703 218.405 155.603 223.245 C 153.403 236.825 155.313 250.535 156.203 264.135 C 157.733 278.795 160.913 293.215 163.223 307.765 C 164.383 318.965 166.643 330.075 168.173 341.255 C 164.183 344.785 159.333 347.335 155.763 351.325 C 152.073 355.435 153.433 361.265 153.563 366.265 C 155.443 382.635 161.873 397.965 167.473 413.325 C 169.643 419.515 173.303 425.215 178.413 429.365 C 184.623 435.015 192.703 437.605 200.493 440.235 C 217.353 475.275 238.823 508.805 268.133 534.745 C 275.113 540.525 281.793 546.995 290.313 550.495 C 305.483 556.815 322.313 557.785 338.553 556.995 C 353.393 556.165 368.733 552.605 380.683 543.335 C 415.123 516.455 439.753 479.195 458.553 440.215 C 468.003 437.385 477.563 433.455 484.183 425.835 C 490.383 419.355 492.363 410.345 495.623 402.265 C 500.903 387.505 506.543 372.205 505.603 356.265 C 503.893 348.845 495.933 345.985 490.873 341.205 C 494.603 317.035 499.053 292.765 498.213 268.205 C 498.253 254.515 496.033 240.965 493.023 227.655 L 492.483 227.165 C 489.283 211.515 485.763 195.795 479.993 180.855 C 475.643 170.025 470.963 158.665 462.073 150.695 C 455.643 140.715 446.953 132.375 437.523 125.245 C 420.383 112.515 400.393 104.085 379.743 99.055 C 339.363 89.145 297.033 91.595 256.393 98.485 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 254.845 96.98 C 266.968 94.65 279.335 94.2 291.608 93.16 C 322.469 91.88 354.045 95.17 383.031 106.58 C 405.86 115.729 427.428 130.169 441.234 150.908 C 453.285 163.028 458.63 179.828 463.719 195.687 C 466.63 206.457 471.188 216.887 472.511 228.016 C 473.977 232.846 474.595 237.936 474.533 242.996 C 474.696 243.456 475.023 244.356 475.197 244.806 C 476.208 269.045 476.572 293.654 471.189 317.404 C 468.175 326.273 466.835 335.603 464.833 344.743 C 460.622 365.972 456.31 387.192 452.68 408.531 C 452.07 407.251 451.481 405.971 450.901 404.691 C 448.31 385.812 445.779 366.932 443.427 348.023 C 442.7 328.333 436.621 308.474 424.334 292.794 C 411.3 276.215 392.509 264.185 372.13 259.045 C 381.141 272.525 395.187 281.325 407.127 291.914 C 399.591 292.534 392.215 290.394 385.225 287.765 C 371.064 282.335 357.699 275.015 344.671 267.285 C 326.058 256.225 308.225 243.916 290.621 231.316 C 293.759 237.746 298.559 243.156 303.378 248.376 C 319.725 265.295 338.513 279.585 357.1 293.914 C 348.068 294.924 339.092 292.864 330.404 290.544 C 311.843 285.035 294.388 276.435 277.28 267.475 C 251.736 253.666 227.08 238.226 203.236 221.646 C 201.42 239.626 197.102 257.225 192.812 274.725 C 187.463 289.565 184.99 305.264 183.547 320.934 C 181.442 349.603 182.828 378.472 186.451 407.011 C 187.144 412.801 187.628 418.621 187.771 424.461 C 187.766 425.091 187.755 426.341 187.75 426.971 C 187.585 426.341 187.246 425.071 187.071 424.441 C 185.812 419.201 184.273 414.031 182.815 408.841 C 176.914 387.432 170.785 366.082 164.395 344.823 C 162.971 340.193 161.556 335.563 159.982 330.983 C 153.161 312.194 149.454 292.484 146.646 272.785 C 145.307 266.175 145.257 259.415 144.547 252.736 C 143.191 238.586 143.332 224.056 147.129 210.307 C 153.308 185.987 163.051 162.248 178.281 142.209 C 196.531 118.049 224.032 99.38 254.845 96.98 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 1.951674, -2.308377)"/>
<path fill="#00e400" stroke="#00e400" stroke-width="5" opacity="1.00" d="M 202.369 221.696 C 226.213 238.275 250.869 253.715 276.413 267.525 C 293.521 276.484 310.975 285.084 329.537 290.594 C 338.225 292.914 347.2 294.974 356.233 293.964 C 337.646 279.634 318.858 265.345 302.511 248.425 C 297.692 243.205 292.892 237.795 289.753 231.366 C 307.358 243.965 325.191 256.275 343.804 267.335 C 356.832 275.064 370.197 282.384 384.358 287.814 C 391.348 290.444 398.724 292.584 406.26 291.964 C 394.32 281.374 380.274 272.574 371.263 259.095 C 391.642 264.235 410.432 276.264 423.467 292.844 C 435.754 308.523 441.833 328.383 442.56 348.072 C 444.911 366.982 447.443 385.861 450.034 404.741 C 450.188 406.691 450.283 408.641 450.408 410.611 C 445.991 408.381 441.604 406.101 437.177 403.901 C 425.426 429.18 413.024 454.369 396.642 476.999 C 385.122 493.058 371.492 507.738 355.589 519.538 C 331.276 533.877 299.004 533.817 274.565 519.288 C 258.727 507.548 245.018 493.088 233.389 477.209 C 216.63 454.549 203.781 429.34 191.641 404.021 C 189.609 405.011 187.596 406.021 185.584 407.061 C 181.96 378.521 180.575 349.652 182.68 320.983 C 184.123 305.314 186.596 289.614 191.945 274.774 C 196.234 257.275 200.553 239.675 202.369 221.696 M 340.816 313.193 C 359.611 311.043 378.64 310.903 397.468 313.143 C 382.446 298.274 355.749 298.764 340.816 313.193 M 229.923 313.143 C 248.867 310.983 268.047 310.923 286.996 313.373 C 271.796 298.644 245.004 298.444 229.923 313.143 M 356.804 330.203 C 353.026 333.113 353 339.023 356.185 342.432 C 365.461 348.502 379.584 347.552 386.265 338.183 C 385.629 334.763 383.984 331.423 380.979 329.493 C 373.794 324.723 363.656 325.093 356.804 330.203 M 241.361 334.293 C 242.559 339.383 246.154 343.952 251.384 345.242 C 260.832 347.692 270.963 343.732 277.794 337.203 C 272.524 330.503 264.129 325.883 255.485 326.753 C 249.939 327.193 245.021 330.263 241.361 334.293 M 293.685 396.081 C 306.648 401.811 321.798 401.711 334.768 396.311 C 322.049 389.811 306.43 390.021 293.685 396.081 M 267.056 439.21 C 271.91 455.789 283.02 471.769 299.668 478.079 C 312.181 482.469 326.814 481.439 338.01 474.229 C 350.321 466.329 358.343 453.099 362.269 439.2 C 330.579 436.67 298.718 436.62 267.056 439.21 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.819187, -2.351964)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 341.325 313.592 C 356.258 299.162 382.955 298.672 397.977 313.542 C 379.149 311.302 360.12 311.442 341.325 313.592 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.313265, -2.754535)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 230.434 312.715 C 245.515 298.015 272.307 298.215 287.507 312.945 C 268.559 310.495 249.379 310.555 230.434 312.715 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.30482, -1.927218)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 463.57 344.932 C 465.572 335.792 466.913 326.463 469.926 317.593 C 469.802 322.313 468.676 326.923 468.121 331.602 C 471.868 332.582 475.626 333.532 479.383 334.492 C 483.07 342.142 483.856 350.882 482.317 359.142 C 480.157 369.831 476.354 380.081 473.242 390.521 C 470.174 401.59 464.385 412.43 454.876 419.22 C 450.092 422.77 444.285 424.58 438.442 425.47 C 423.167 458.389 403.903 489.988 377.581 515.227 C 371.482 520.767 365.526 526.667 358.256 530.697 C 342.742 539.507 324.244 541.177 306.684 539.987 C 297.668 539.237 288.481 538.287 280.054 534.727 C 264.204 528.127 251.563 515.897 240.407 503.158 C 219.106 480.418 203.472 453.159 189.764 425.39 C 188.943 425.21 187.32 424.83 186.509 424.65 C 186.365 418.81 185.881 412.99 185.188 407.2 C 187.2 406.16 189.213 405.15 191.246 404.16 C 203.385 429.48 216.234 454.689 232.994 477.348 C 244.623 493.228 258.331 507.687 274.17 519.427 C 298.609 533.957 330.88 534.017 355.194 519.677 C 371.096 507.877 384.727 493.198 396.247 477.138 C 412.628 454.509 425.03 429.32 436.782 404.04 C 441.208 406.24 445.595 408.52 450.012 410.75 C 449.887 408.78 449.793 406.83 449.638 404.88 C 450.218 406.16 450.807 407.44 451.417 408.72 L 451.57 409.14 C 464.279 390.191 469.69 367.531 473.223 345.242 C 470.002 345.102 466.781 345.002 463.57 344.932 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 3.215811, -2.485598)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 357.1 330.608 C 363.952 325.498 374.089 325.128 381.275 329.898 C 384.28 331.828 385.925 335.168 386.56 338.588 C 379.88 347.957 365.757 348.907 356.481 342.838 C 353.296 339.428 353.321 333.518 357.1 330.608 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.526449, -2.759088)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 241.659 333.872 C 245.319 329.842 250.237 326.772 255.783 326.332 C 264.427 325.462 272.822 330.082 278.092 336.782 C 271.261 343.312 261.13 347.272 251.682 344.822 C 246.452 343.532 242.857 338.962 241.659 333.872 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.518041, -1.933186)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 159.106 329.907 C 160.68 334.487 162.094 339.116 163.519 343.746 C 160.378 343.586 157.227 343.456 154.086 343.356 C 158.04 366.566 164.305 390.085 178.022 409.665 C 179.317 409.025 180.633 408.395 181.938 407.765 C 183.397 412.954 184.936 418.124 186.195 423.364 C 173.793 420.494 163.242 411.064 158.773 399.175 C 155.007 389.015 151.58 378.745 148.074 368.506 C 144.119 357.186 143.083 344.396 148.02 333.267 C 151.692 332.087 155.639 331.667 159.106 329.907 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.819973, -1.226198)"/>
<path fill="#00e400" stroke="#00e400" stroke-width="5" opacity="1.00" d="M 154.087 343.376 C 157.228 343.476 160.379 343.606 163.52 343.766 C 169.909 365.026 176.039 386.375 181.939 407.784 C 180.634 408.414 179.318 409.044 178.023 409.684 C 164.306 390.105 158.04 366.586 154.087 343.376 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.819256, -1.245378)"/>
<path fill="#00e400" stroke="#00e400" stroke-width="5" opacity="1.00" d="M 463.955 345.889 C 467.166 345.959 470.386 346.059 473.608 346.199 C 470.074 368.488 464.663 391.148 451.955 410.097 L 451.802 409.677 C 455.432 388.338 459.744 367.118 463.955 345.889 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.838425, -3.44816)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 293.536 396.067 C 306.281 390.007 321.899 389.797 334.618 396.297 C 321.648 401.697 306.499 401.797 293.536 396.067 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 2.968232, -2.336665)"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 266.436 439.198 C 298.097 436.609 329.959 436.659 361.648 439.188 C 357.722 453.088 349.701 466.318 337.39 474.217 C 326.193 481.427 311.56 482.457 299.047 478.067 C 282.4 471.758 271.29 455.778 266.436 439.198 M 278.081 446.488 C 282.901 455.898 289.759 464.908 299.523 469.538 C 306.178 472.827 313.826 472.448 321.002 471.938 C 334.657 469.848 344.351 458.288 350.023 446.488 C 326.038 445.968 302.057 445.978 278.081 446.488 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 3.439647, -2.334642)"/>
<path fill="#00e400" stroke="#00e400" stroke-width="5" opacity="1.00" d="M 278.079 446.488 C 302.056 445.978 326.037 445.968 350.021 446.488 C 344.349 458.288 334.655 469.847 321 471.937 C 313.824 472.447 306.177 472.827 299.522 469.537 C 289.757 464.907 282.9 455.898 278.079 446.488 Z" style="" transform="matrix(0.999972, 0.007475, -0.007475, 0.999972, 3.441201, -2.334695)"/>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="hazardous-air-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 320.535 11.437 C 368.105 10.307 415.935 20.007 459.205 39.827 C 528.185 71.007 585.215 127.827 616.325 196.867 C 648.905 267.857 653.145 351.227 628.275 425.237 C 610.775 478.107 578.695 525.997 536.765 562.607 C 498.645 596.107 452.345 620.217 403.025 632.177 C 364.565 641.537 324.365 643.647 285.135 638.387 C 209.845 628.687 138.755 590.487 88.865 533.307 C 62.515 503.427 41.945 468.457 28.625 430.907 C 11.745 383.477 6.735 331.947 13.795 282.117 C 20.125 237.037 36.525 193.397 61.475 155.307 C 99.975 95.917 159.025 50.147 226.205 27.827 C 256.555 17.577 288.505 12.067 320.535 11.437 M 261.945 105.867 C 230.365 111.887 202.875 132.667 185.655 159.447 C 174.545 176.627 166.695 195.867 162.005 215.747 C 158.225 244.567 164.245 273.407 169.825 301.557 C 169.605 310.327 172.155 318.877 173.015 327.577 C 168.095 333.187 162.545 338.477 159.035 345.137 C 157.675 353.507 160.275 361.917 162.355 369.977 C 166.375 382.077 169.655 394.507 175.275 405.997 C 177.965 411.567 182.145 416.477 187.495 419.637 C 195.835 424.627 204.575 429.217 211.475 436.207 C 200.285 442.467 189.045 448.637 177.855 454.887 C 183.715 475.157 194.575 493.377 204.135 512.037 C 212.965 529.067 221.585 546.237 231.485 562.687 C 238.515 559.987 245.745 557.727 252.435 554.207 C 264.345 548.747 275.415 541.597 287.155 535.817 C 302.195 537.177 316.855 542.287 332.095 541.497 C 344.655 540.427 356.925 537.357 369.385 535.607 C 377.185 539.767 384.865 544.137 392.605 548.407 C 403.005 554.207 413.985 558.847 425.245 562.697 C 444.085 527.267 465.075 492.707 478.765 454.837 C 468.555 449.007 458.115 443.617 447.615 438.337 C 448.865 434.957 449.215 431.047 451.125 428.027 C 458.985 424.397 467.965 422.397 474.375 416.187 C 479.125 411.817 481.105 405.497 483.945 399.897 C 490.115 385.797 494.065 370.857 497.715 355.947 C 498.795 351.797 498.465 347.307 496.555 343.457 C 493.275 337.327 488.165 332.447 483.685 327.207 C 485.275 315.737 487.895 304.347 487.995 292.737 C 490.925 274.337 490.465 255.497 488.365 237.037 C 485.575 217.917 481.365 198.847 474.085 180.897 C 470.565 172.687 466.655 164.337 460.185 157.997 C 457.285 155.167 455.235 151.637 452.535 148.637 C 446.395 141.707 439.265 135.677 431.605 130.507 C 402.075 110.857 366.185 103.077 331.155 101.897 C 308.015 100.687 284.855 102.717 261.945 105.867 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 261.945 105.867 C 284.855 102.717 308.015 100.687 331.155 101.897 C 366.185 103.077 402.075 110.857 431.605 130.507 C 439.265 135.677 446.395 141.707 452.535 148.637 C 455.235 151.637 457.285 155.167 460.185 157.997 C 466.655 164.337 470.565 172.687 474.085 180.897 C 481.365 198.847 485.575 217.917 488.365 237.037 C 490.465 255.497 490.925 274.337 487.995 292.737 C 485.815 302.907 482.575 312.807 480.055 322.907 C 478.085 330.507 476.315 338.157 474.475 345.797 C 470.595 361.457 467.165 377.237 463.395 392.927 C 461.075 372.117 457.855 351.397 456.175 330.517 C 455.365 319.917 453.285 309.107 447.955 299.787 C 443.065 287.837 433.845 278.097 423.735 270.317 C 412.825 262.347 400.465 256.157 387.285 253.047 C 396.535 265.577 409.855 274.157 422.075 283.487 C 414.195 283.987 406.515 281.747 399.195 279.097 C 380.995 272.317 364.005 262.737 347.355 252.847 C 333.475 244.627 320.155 235.537 306.665 226.707 C 314.425 239.517 326.205 249.167 337.575 258.637 C 348.475 267.697 359.985 275.967 371.495 284.237 C 352.795 285.497 335.005 278.077 318.035 271.147 C 283.745 256.197 251.465 237.017 220.135 216.667 C 218.985 227.657 216.045 238.327 213.215 248.977 C 211.535 254.917 210.155 260.967 207.935 266.737 C 201.275 283.327 198.665 301.217 197.545 318.967 C 196.935 333.247 196.385 347.597 197.635 361.867 C 198.145 375.347 200.905 388.667 200.605 402.197 C 195.145 383.307 190.015 364.337 184.335 345.517 C 179.895 330.747 175.785 315.807 169.825 301.557 C 164.245 273.407 158.225 244.567 162.005 215.747 C 166.695 195.867 174.545 176.627 185.655 159.447 C 202.875 132.667 230.365 111.887 261.945 105.867 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 220.135 216.667 C 251.465 237.017 283.745 256.197 318.035 271.147 C 335.005 278.077 352.795 285.497 371.495 284.237 C 359.985 275.967 348.475 267.697 337.575 258.637 C 326.205 249.167 314.425 239.517 306.665 226.707 C 320.155 235.537 333.475 244.627 347.355 252.847 C 364.005 262.737 380.995 272.317 399.195 279.097 C 406.515 281.747 414.195 283.987 422.075 283.487 C 409.855 274.157 396.535 265.577 387.285 253.047 C 400.465 256.157 412.825 262.347 423.735 270.317 C 433.845 278.097 443.065 287.837 447.955 299.787 C 448.355 310.507 451.865 320.787 456.175 330.517 C 457.855 351.397 461.075 372.117 463.395 392.927 C 467.165 377.237 470.595 361.457 474.475 345.797 C 478.915 346.407 483.365 346.897 487.825 347.407 C 484.035 369.287 476.295 390.217 467.355 410.467 C 461.425 409.057 455.485 407.707 449.565 406.267 C 445.515 414.737 441.545 423.257 437.605 431.777 C 428.225 432.437 418.865 433.227 409.485 433.757 C 398.135 416.227 385.455 399.277 369.515 385.627 C 360.355 377.307 350.095 369.527 337.985 366.217 C 330.135 364.297 321.425 364.167 314.075 367.897 C 296.375 375.497 281.735 388.667 269.415 403.227 C 261.155 412.807 254.355 423.487 247.045 433.777 C 237.755 433.227 228.495 432.367 219.215 431.807 C 215.155 423.317 211.215 414.777 207.175 406.277 C 201.235 407.697 195.285 409.077 189.335 410.457 C 180.385 390.067 172.545 369.037 168.735 347.027 C 173.945 346.647 179.135 346.087 184.335 345.517 C 190.015 364.337 195.145 383.307 200.605 402.197 C 200.905 388.667 198.145 375.347 197.635 361.867 C 196.385 347.597 196.935 333.247 197.545 318.967 C 198.665 301.217 201.275 283.327 207.935 266.737 C 210.155 260.967 211.535 254.917 213.215 248.977 C 216.045 238.327 218.985 227.657 220.135 216.667 M 243.695 306.947 C 262.215 315.227 283.945 314.707 302.685 307.417 C 283.255 303.507 263.215 303.767 243.695 306.947 M 353.885 307.497 C 372.545 314.847 393.765 314.437 412.635 308.007 C 402.945 305.817 393.065 304.817 383.165 304.437 C 373.325 304.637 363.565 305.797 353.885 307.497 M 257.635 331.277 C 257.575 335.617 257.515 339.967 257.485 344.317 C 269.265 348.937 283.225 347.127 293.025 338.957 C 287.925 334.967 283.415 329.777 277.105 327.697 C 270.505 327.927 264.015 329.617 257.635 331.277 M 377.525 328.417 C 372.265 330.957 368.495 335.587 364.225 339.437 C 367.785 341.797 371.445 344.117 375.585 345.327 C 383.245 347.827 391.565 346.927 399.105 344.437 C 399.305 339.927 399.305 335.417 399.265 330.907 C 394.625 329.857 389.985 328.787 385.275 328.167 C 382.705 327.907 379.915 326.997 377.525 328.417 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 480.055 322.907 C 482.575 312.807 485.815 302.907 487.995 292.737 C 487.895 304.347 485.275 315.737 483.685 327.207 C 488.165 332.447 493.275 337.327 496.555 343.457 C 498.465 347.307 498.795 351.797 497.715 355.947 C 494.065 370.857 490.115 385.797 483.945 399.897 C 481.105 405.497 479.125 411.817 474.375 416.187 C 467.965 422.397 458.985 424.397 451.125 428.027 C 449.215 431.047 448.865 434.957 447.615 438.337 C 458.115 443.617 468.555 449.007 478.765 454.837 C 465.075 492.707 444.085 527.267 425.245 562.697 C 413.985 558.847 403.005 554.207 392.605 548.407 C 384.865 544.137 377.185 539.767 369.385 535.607 C 356.925 537.357 344.655 540.427 332.095 541.497 C 316.855 542.287 302.195 537.177 287.155 535.817 C 275.415 541.597 264.345 548.747 252.435 554.207 C 245.745 557.727 238.515 559.987 231.485 562.687 C 221.585 546.237 212.965 529.067 204.135 512.037 C 194.575 493.377 183.715 475.157 177.855 454.887 C 189.045 448.637 200.285 442.467 211.475 436.207 C 204.575 429.217 195.835 424.627 187.495 419.637 C 182.145 416.477 177.965 411.567 175.275 405.997 C 169.655 394.507 166.375 382.077 162.355 369.977 C 160.275 361.917 157.675 353.507 159.035 345.137 C 162.545 338.477 168.095 333.187 173.015 327.577 C 172.155 318.877 169.605 310.327 169.825 301.557 C 175.785 315.807 179.895 330.747 184.335 345.517 C 179.135 346.087 173.945 346.647 168.735 347.027 C 172.545 369.037 180.385 390.067 189.335 410.457 C 195.285 409.077 201.235 407.697 207.175 406.277 C 211.215 414.777 215.155 423.317 219.215 431.807 C 228.495 432.367 237.755 433.227 247.045 433.777 C 254.355 423.487 261.155 412.807 269.415 403.227 C 281.735 388.667 296.375 375.497 314.075 367.897 C 321.425 364.167 330.135 364.297 337.985 366.217 C 350.095 369.527 360.355 377.307 369.515 385.627 C 385.455 399.277 398.135 416.227 409.485 433.757 C 418.865 433.227 428.225 432.437 437.605 431.777 C 441.545 423.257 445.515 414.737 449.565 406.267 C 455.485 407.707 461.425 409.057 467.355 410.467 C 476.295 390.217 484.035 369.287 487.825 347.407 C 483.365 346.897 478.915 346.407 474.475 345.797 C 476.315 338.157 478.085 330.507 480.055 322.907 M 317.365 375.117 C 303.055 381.917 290.665 392.227 280.065 403.907 C 269.225 415.537 260.625 428.937 251.665 441.997 C 265.435 469.187 279.115 496.407 292.485 523.797 C 313.035 533.347 338.005 535.597 358.865 525.757 C 361.925 523.927 366.085 522.807 367.315 519.067 C 378.385 492.707 392.065 467.567 404.815 442.007 C 394.575 425.827 383.135 410.257 369.215 397.037 C 361.375 389.367 353.035 381.927 343.275 376.777 C 335.435 372.757 325.445 370.557 317.365 375.117 M 229.485 440.377 C 221.485 442.477 214.475 447.157 206.725 449.977 C 222.265 480.567 237.745 511.197 253.715 541.567 C 262.215 536.987 272.595 534.467 278.555 526.317 C 282.335 521.347 280.225 514.777 277.405 509.907 C 266.775 487.867 256.435 465.577 243.065 445.027 C 240.115 440.747 234.445 438.887 229.485 440.377 M 401.195 464.987 C 396.605 474.647 391.485 484.037 386.665 493.587 C 382.665 501.117 378.345 508.637 376.265 516.967 C 374.345 523.147 378.815 529.117 383.715 532.397 C 389.775 536.247 396.515 538.857 402.845 542.237 C 418.555 511.367 435.025 480.877 450.015 449.647 C 439.625 445.727 429.715 438.667 418.095 439.827 C 410.755 446.937 405.875 456.017 401.195 464.987 M 189.075 458.537 C 195.165 472.897 202.095 486.877 209.265 500.727 C 218.005 517.427 226.615 534.197 235.685 550.707 C 239.665 549.347 243.705 548.117 247.745 546.927 C 232.395 515.977 216.655 485.227 200.915 454.477 C 196.965 455.827 193.035 457.207 189.075 458.537 M 409.265 546.857 C 413.225 547.997 417.165 549.177 421.105 550.397 C 436.545 520.117 452.805 490.237 466.955 459.327 C 463.335 457.717 459.715 456.127 456.105 454.517 C 440.395 485.247 424.215 515.737 409.265 546.857 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 447.955 299.787 C 453.285 309.107 455.365 319.917 456.175 330.517 C 451.865 320.787 448.355 310.507 447.955 299.787 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 243.695 306.947 C 263.215 303.767 283.255 303.507 302.685 307.417 C 283.945 314.707 262.215 315.227 243.695 306.947 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 353.885 307.497 C 363.565 305.797 373.325 304.637 383.165 304.437 C 393.065 304.817 402.945 305.817 412.635 308.007 C 393.765 314.437 372.545 314.847 353.885 307.497 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 257.635 331.277 C 264.015 329.617 270.505 327.927 277.105 327.697 C 283.415 329.777 287.925 334.967 293.025 338.957 C 283.225 347.127 269.265 348.937 257.485 344.317 C 257.515 339.967 257.575 335.617 257.635 331.277 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 377.525 328.417 C 379.915 326.997 382.705 327.907 385.275 328.167 C 389.985 328.787 394.625 329.857 399.265 330.907 C 399.305 335.417 399.305 339.927 399.105 344.437 C 391.565 346.927 383.245 347.827 375.585 345.327 C 371.445 344.117 367.785 341.797 364.225 339.437 C 368.495 335.587 372.265 330.957 377.525 328.417 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 317.365 375.117 C 325.445 370.557 335.435 372.757 343.275 376.777 C 353.035 381.927 361.375 389.367 369.215 397.037 C 383.135 410.257 394.575 425.827 404.815 442.007 C 392.065 467.567 378.385 492.707 367.315 519.067 C 366.085 522.807 361.925 523.927 358.865 525.757 C 338.005 535.597 313.035 533.347 292.485 523.797 C 279.115 496.407 265.435 469.187 251.665 441.997 C 260.625 428.937 269.225 415.537 280.065 403.907 C 290.665 392.227 303.055 381.917 317.365 375.117 M 320.585 420.607 C 300.445 423.987 284.615 442.777 283.845 463.017 C 283.675 478.547 291.975 493.777 304.935 502.287 C 312.395 507.087 321.225 510.327 330.215 509.817 C 346.125 509.357 360.925 499.297 368.605 485.577 C 373.325 477.247 374.655 467.337 373.195 457.947 C 370.625 433.597 344.345 415.307 320.585 420.607 Z"/>
<path fill="#000" stroke="#000" stroke-width="5" opacity="1.00" d="M 320.585 420.607 C 344.345 415.307 370.625 433.597 373.195 457.947 C 374.655 467.337 373.325 477.247 368.605 485.577 C 360.925 499.297 346.125 509.357 330.215 509.817 C 321.225 510.327 312.395 507.087 304.935 502.287 C 291.975 493.777 283.675 478.547 283.845 463.017 C 284.615 442.777 300.445 423.987 320.585 420.607 M 324.485 443.417 C 318.515 445.217 312.265 448.637 309.715 454.647 C 306.965 460.877 307.365 468.147 309.705 474.437 C 311.695 479.817 316.675 483.737 322.185 485.057 C 329.725 487.477 338.355 485.337 344.265 480.177 C 348.745 476.087 350.055 469.727 350.495 463.937 C 350.315 451.247 336.815 440.787 324.485 443.417 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 229.485 440.377 C 234.445 438.887 240.115 440.747 243.065 445.027 C 256.435 465.577 266.775 487.867 277.405 509.907 C 280.225 514.777 282.335 521.347 278.555 526.317 C 272.595 534.467 262.215 536.987 253.715 541.567 C 237.745 511.197 222.265 480.567 206.725 449.977 C 214.475 447.157 221.485 442.477 229.485 440.377 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 401.195 464.987 C 405.875 456.017 410.755 446.937 418.095 439.827 C 429.715 438.667 439.625 445.727 450.015 449.647 C 435.025 480.877 418.555 511.367 402.845 542.237 C 396.515 538.857 389.775 536.247 383.715 532.397 C 378.815 529.117 374.345 523.147 376.265 516.967 C 378.345 508.637 382.665 501.117 386.665 493.587 C 391.485 484.037 396.605 474.647 401.195 464.987 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 324.485 443.417 C 336.815 440.787 350.315 451.247 350.495 463.937 C 350.055 469.727 348.745 476.087 344.265 480.177 C 338.355 485.337 329.725 487.477 322.185 485.057 C 316.675 483.737 311.695 479.817 309.705 474.437 C 307.365 468.147 306.965 460.877 309.715 454.647 C 312.265 448.637 318.515 445.217 324.485 443.417 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 189.075 458.537 C 193.035 457.207 196.965 455.827 200.915 454.477 C 216.655 485.227 232.395 515.977 247.745 546.927 C 243.705 548.117 239.665 549.347 235.685 550.707 C 226.615 534.197 218.005 517.427 209.265 500.727 C 202.095 486.877 195.165 472.897 189.075 458.537 Z"/>
<path fill="#7e0023" stroke="#7e0023" stroke-width="5" opacity="1.00" d="M 409.265 546.857 C 424.215 515.737 440.395 485.247 456.105 454.517 C 459.715 456.127 463.335 457.717 466.955 459.327 C 452.805 490.237 436.545 520.117 421.105 550.397 C 417.165 549.177 413.225 547.997 409.265 546.857 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="moderate-air-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffff00" stroke="#ffff00" stroke-width="2" opacity="1.00" d="M 306.143 12.125 C 349.223 9.135 392.903 15.025 433.623 29.385 C 471.263 42.665 506.383 63.115 536.423 89.395 C 593.563 138.785 632.063 209.375 641.783 284.345 C 650.263 346.445 639.623 411.065 611.263 466.985 C 588.543 512.235 554.663 551.805 513.573 581.365 C 465.893 615.905 408.383 636.675 349.623 640.475 C 307.233 643.405 264.223 637.835 224.063 623.905 C 153.253 599.745 91.793 549.415 54.083 484.805 C 33.383 449.715 19.703 410.505 14.083 370.155 C 7.803 325.485 11.193 279.455 24.183 236.255 C 35.883 197.155 55.413 160.445 81.143 128.775 C 109.873 93.325 146.343 64.175 187.283 44.015 C 224.303 25.725 264.943 14.805 306.143 12.125 M 256.393 98.485 C 231.103 104.515 208.583 119.915 192.323 140.045 C 175.843 160.115 165.253 184.365 158.433 209.235 C 157.343 213.885 155.703 218.405 155.603 223.245 C 153.403 236.825 155.313 250.535 156.203 264.135 C 157.733 278.795 160.913 293.215 163.223 307.765 C 164.383 318.965 166.643 330.075 168.173 341.255 C 164.183 344.785 159.333 347.335 155.763 351.325 C 152.073 355.435 153.433 361.265 153.563 366.265 C 155.443 382.635 161.873 397.965 167.473 413.325 C 169.643 419.515 173.303 425.215 178.413 429.365 C 184.623 435.015 192.703 437.605 200.493 440.235 C 217.353 475.275 238.823 508.805 268.133 534.745 C 275.113 540.525 281.793 546.995 290.313 550.495 C 305.483 556.815 322.313 557.785 338.553 556.995 C 353.393 556.165 368.733 552.605 380.683 543.335 C 415.123 516.455 439.753 479.195 458.553 440.215 C 468.003 437.385 477.563 433.455 484.183 425.835 C 490.383 419.355 492.363 410.345 495.623 402.265 C 500.903 387.505 506.543 372.205 505.603 356.265 C 503.893 348.845 495.933 345.985 490.873 341.205 C 494.603 317.035 499.053 292.765 498.213 268.205 C 498.253 254.515 496.033 240.965 493.023 227.655 L 492.483 227.165 C 489.283 211.515 485.763 195.795 479.993 180.855 C 475.643 170.025 470.963 158.665 462.073 150.695 C 455.643 140.715 446.953 132.375 437.523 125.245 C 420.383 112.515 400.393 104.085 379.743 99.055 C 339.363 89.145 297.033 91.595 256.393 98.485 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 256.393 98.485 C 297.033 91.595 339.363 89.145 379.743 99.055 C 400.393 104.085 420.383 112.515 437.523 125.245 C 446.953 132.375 455.643 140.715 462.073 150.695 C 470.963 158.665 475.643 170.025 479.993 180.855 C 485.763 195.795 489.283 211.515 492.483 227.165 C 492.783 232.225 493.873 237.195 494.483 242.215 C 495.883 258.845 497.463 275.595 495.413 292.255 C 494.653 307.635 489.383 322.215 486.323 337.195 C 484.933 343.775 483.543 350.365 482.153 356.945 C 478.373 375.115 474.383 393.265 471.163 411.535 C 467.903 391.795 466.073 371.865 463.153 352.075 C 462.423 346.205 462.743 340.195 461.343 334.415 C 458.113 325.155 456.093 315.495 452.453 306.375 C 444.903 291.125 432.313 278.715 417.753 270.085 C 409.073 265.065 399.823 260.655 389.963 258.625 C 393.763 264.695 399.283 269.435 404.533 274.215 C 411.163 279.975 418.163 285.305 425.203 290.555 L 425.443 291.565 C 413.923 292.155 403.093 287.315 392.673 283.025 C 365.803 270.735 340.863 254.665 316.513 238.035 C 313.383 235.935 310.473 233.465 307.033 231.875 C 307.783 234.345 309.303 236.475 310.803 238.555 C 321.223 251.285 333.873 261.915 346.563 272.305 C 355.533 279.535 364.763 286.435 374.093 293.205 C 366.163 294.485 358.253 292.625 350.493 291.155 C 326.223 284.575 303.583 273.235 281.533 261.355 C 259.063 249.045 237.313 235.485 216.043 221.225 C 215.233 231.455 212.693 241.445 210.663 251.485 C 208.323 259.905 206.893 268.575 204.033 276.855 C 199.353 289.185 197.333 302.285 195.443 315.285 C 194.193 331.565 192.893 347.905 193.883 364.245 C 194.563 374.615 194.953 385.015 196.013 395.365 C 197.043 402.945 198.023 410.535 198.213 418.195 C 197.863 418.645 197.163 419.555 196.813 420.005 C 191.053 399.015 185.103 378.095 178.913 357.225 C 176.073 347.485 173.133 337.765 169.803 328.185 C 167.973 322.805 166.063 317.425 164.903 311.845 C 164.633 310.375 164.013 309.005 163.223 307.765 C 160.913 293.215 157.733 278.795 156.203 264.135 C 155.313 250.535 153.403 236.825 155.603 223.245 C 155.703 218.405 157.343 213.885 158.433 209.235 C 165.253 184.365 175.843 160.115 192.323 140.045 C 208.583 119.915 231.103 104.515 256.393 98.485 Z"/>
<path fill="#ffff00" stroke="#ffff00" stroke-width="2" opacity="1.00" d="M 216.043 221.225 C 237.313 235.485 259.063 249.045 281.533 261.355 C 303.583 273.235 326.223 284.575 350.493 291.155 C 358.253 292.625 366.163 294.485 374.093 293.205 C 364.763 286.435 355.533 279.535 346.563 272.305 C 333.873 261.915 321.223 251.285 310.803 238.555 C 312.663 238.075 314.593 237.985 316.513 238.035 C 340.863 254.665 365.803 270.735 392.673 283.025 C 403.093 287.315 413.923 292.155 425.443 291.565 L 425.203 290.555 C 418.163 285.305 411.163 279.975 404.533 274.215 C 399.283 269.435 393.763 264.695 389.963 258.625 C 399.823 260.655 409.073 265.065 417.753 270.085 C 432.313 278.715 444.903 291.125 452.453 306.375 C 456.093 315.495 458.113 325.155 461.343 334.415 C 462.743 340.195 462.423 346.205 463.153 352.075 C 466.073 371.865 467.903 391.795 471.163 411.535 C 474.383 393.265 478.373 375.115 482.153 356.945 C 486.953 357.055 491.763 357.215 496.573 357.415 C 491.993 380.715 487.673 405.715 471.553 424.205 C 466.643 422.325 461.813 420.245 456.893 418.415 C 444.513 445.885 431.043 473.215 412.223 496.925 C 403.443 507.755 394.043 518.145 383.593 527.385 C 376.153 534.455 367.353 540.215 357.663 543.655 C 345.053 547.645 331.543 548.365 318.443 547.185 C 306.403 545.975 294.553 542.065 284.623 535.065 C 249.783 507.735 225.163 469.595 207.103 429.625 C 205.283 425.925 203.993 421.925 201.863 418.415 C 201.073 418.745 199.513 419.415 198.723 419.745 C 198.593 419.355 198.343 418.585 198.213 418.195 C 198.023 410.535 197.043 402.945 196.013 395.365 C 194.953 385.015 194.563 374.615 193.883 364.245 C 192.893 347.905 194.193 331.565 195.443 315.285 C 197.333 302.285 199.353 289.185 204.033 276.855 C 206.893 268.575 208.323 259.905 210.663 251.485 C 212.693 241.445 215.233 231.455 216.043 221.225 M 242.153 316.475 C 248.643 320.145 256.073 321.845 263.413 322.875 C 275.963 324.215 289.053 322.625 300.463 317.035 L 300.613 316.335 C 281.183 314.445 261.573 314.395 242.153 316.475 M 358.043 316.575 C 367.033 321.405 377.493 323.015 387.613 323.295 C 397.613 323.095 408.063 321.565 416.823 316.505 C 397.293 314.345 377.573 314.515 358.043 316.575 M 264.893 339.775 C 261.573 340.625 258.853 342.865 255.953 344.595 C 253.473 348.035 255.793 352.645 258.593 355.115 C 261.203 357.885 265.223 357.055 268.523 358.265 C 276.563 359.445 284.073 354.695 289.213 348.915 C 289.863 347.875 291.753 347.285 291.283 345.835 C 283.043 342.295 274.123 337.285 264.893 339.775 M 368.093 345.375 C 368.863 349.305 372.593 351.825 375.623 354.165 C 381.783 357.845 389.593 359.485 396.503 357.025 C 401.263 355.275 404.143 350.395 404.353 345.465 C 400.223 340.825 393.693 338.815 387.583 339.255 C 380.633 339.515 374.333 342.635 368.093 345.375 M 308.583 406.395 C 312.153 409.815 316.703 412.115 321.523 413.145 C 330.843 415.315 340.343 413.025 349.403 410.745 C 349.393 410.485 349.383 409.955 349.383 409.695 C 336.563 403.885 322.183 406.175 308.583 406.395 M 283.243 463.925 C 293.423 466.725 303.983 467.775 314.523 467.565 C 331.213 467.455 347.983 468.315 364.603 466.405 C 368.153 465.415 372.283 465.295 375.463 463.655 C 367.893 460.435 359.653 459.575 351.533 459.025 C 336.543 458.865 321.563 458.815 306.573 459.045 C 298.663 459.775 290.503 460.385 283.243 463.925 M 313.333 482.135 C 313.663 484.975 314.063 487.795 314.453 490.625 C 322.003 490.625 329.633 490.795 337.123 489.635 C 341.413 488.675 346.253 487.405 349.413 484.345 C 337.483 482.415 325.383 482.365 313.333 482.135 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 492.483 227.165 L 493.023 227.655 C 496.033 240.965 498.253 254.515 498.213 268.205 C 499.053 292.765 494.603 317.035 490.873 341.205 C 495.933 345.985 503.893 348.845 505.603 356.265 C 506.543 372.205 500.903 387.505 495.623 402.265 C 492.363 410.345 490.383 419.355 484.183 425.835 C 477.563 433.455 468.003 437.385 458.553 440.215 C 439.753 479.195 415.123 516.455 380.683 543.335 C 368.733 552.605 353.393 556.165 338.553 556.995 C 322.313 557.785 305.483 556.815 290.313 550.495 C 281.793 546.995 275.113 540.525 268.133 534.745 C 238.823 508.805 217.353 475.275 200.493 440.235 C 192.703 437.605 184.623 435.015 178.413 429.365 C 173.303 425.215 169.643 419.515 167.473 413.325 C 161.873 397.965 155.443 382.635 153.563 366.265 C 153.433 361.265 152.073 355.435 155.763 351.325 C 159.333 347.335 164.183 344.785 168.173 341.255 C 166.643 330.075 164.383 318.965 163.223 307.765 C 164.013 309.005 164.633 310.375 164.903 311.845 C 166.063 317.425 167.973 322.805 169.803 328.185 C 173.133 337.765 176.073 347.485 178.913 357.225 C 173.373 357.075 167.843 356.735 162.303 356.615 C 165.873 374.065 168.823 391.975 176.683 408.115 C 179.613 413.925 183.273 419.325 187.583 424.205 C 190.583 422.705 194.253 422.185 196.813 420.005 C 197.163 419.555 197.863 418.645 198.213 418.195 C 198.343 418.585 198.593 419.355 198.723 419.745 C 199.513 419.415 201.073 418.745 201.863 418.415 C 203.993 421.925 205.283 425.925 207.103 429.625 C 225.163 469.595 249.783 507.735 284.623 535.065 C 294.553 542.065 306.403 545.975 318.443 547.185 C 331.543 548.365 345.053 547.645 357.663 543.655 C 367.353 540.215 376.153 534.455 383.593 527.385 C 394.043 518.145 403.443 507.755 412.223 496.925 C 431.043 473.215 444.513 445.885 456.893 418.415 C 461.813 420.245 466.643 422.325 471.553 424.205 C 487.673 405.715 491.993 380.715 496.573 357.415 C 491.763 357.215 486.953 357.055 482.153 356.945 C 483.543 350.365 484.933 343.775 486.323 337.195 C 489.383 322.215 494.653 307.635 495.413 292.255 C 497.463 275.595 495.883 258.845 494.483 242.215 C 493.873 237.195 492.783 232.225 492.483 227.165 M 197.593 420.415 C 196.903 421.595 197.003 424.205 198.193 424.865 C 198.543 423.615 199.423 420.645 197.593 420.415 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 307.033 231.875 C 310.473 233.465 313.383 235.935 316.513 238.035 C 314.593 237.985 312.663 238.075 310.803 238.555 C 309.303 236.475 307.783 234.345 307.033 231.875 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 242.153 316.475 C 261.573 314.395 281.183 314.445 300.613 316.335 L 300.463 317.035 C 289.053 322.625 275.963 324.215 263.413 322.875 C 256.073 321.845 248.643 320.145 242.153 316.475 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 358.043 316.575 C 377.573 314.515 397.293 314.345 416.823 316.505 C 408.063 321.565 397.613 323.095 387.613 323.295 C 377.493 323.015 367.033 321.405 358.043 316.575 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 264.893 339.775 C 274.123 337.285 283.043 342.295 291.283 345.835 C 291.753 347.285 289.863 347.875 289.213 348.915 C 284.073 354.695 276.563 359.445 268.523 358.265 C 265.223 357.055 261.203 357.885 258.593 355.115 C 255.793 352.645 253.473 348.035 255.953 344.595 C 258.853 342.865 261.573 340.625 264.893 339.775 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 368.093 345.375 C 374.333 342.635 380.633 339.515 387.583 339.255 C 393.693 338.815 400.223 340.825 404.353 345.465 C 404.143 350.395 401.263 355.275 396.503 357.025 C 389.593 359.485 381.783 357.845 375.623 354.165 C 372.593 351.825 368.863 349.305 368.093 345.375 Z"/>
<path fill="#ffff00" stroke="#ffff00" stroke-width="2" opacity="1.00" d="M 162.303 356.615 C 167.843 356.735 173.373 357.075 178.913 357.225 C 185.103 378.095 191.053 399.015 196.813 420.005 C 194.253 422.185 190.583 422.705 187.583 424.205 C 183.273 419.325 179.613 413.925 176.683 408.115 C 168.823 391.975 165.873 374.065 162.303 356.615 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 308.583 406.395 C 322.183 406.175 336.563 403.885 349.383 409.695 C 349.383 409.955 349.393 410.485 349.403 410.745 C 340.343 413.025 330.843 415.315 321.523 413.145 C 316.703 412.115 312.153 409.815 308.583 406.395 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 197.593 420.415 C 199.423 420.645 198.543 423.615 198.193 424.865 C 197.003 424.205 196.903 421.595 197.593 420.415 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 283.243 463.925 C 290.503 460.385 298.663 459.775 306.573 459.045 C 321.563 458.815 336.543 458.865 351.533 459.025 C 359.653 459.575 367.893 460.435 375.463 463.655 C 372.283 465.295 368.153 465.415 364.603 466.405 C 347.983 468.315 331.213 467.455 314.523 467.565 C 303.983 467.775 293.423 466.725 283.243 463.925 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 313.333 482.135 C 325.383 482.365 337.483 482.415 349.413 484.345 C 346.253 487.405 341.413 488.675 337.123 489.635 C 329.633 490.795 322.003 490.625 314.453 490.625 C 314.063 487.795 313.663 484.975 313.333 482.135 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="unhealthy-air-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#ff0000" stroke="#ff0000" stroke-width="5" opacity="1.00" d="M 303.023 8.993 C 349.903 5.933 397.483 13.243 441.123 30.683 C 515.803 60.023 578.303 118.983 611.753 191.933 C 645.123 263.373 649.693 347.603 624.553 422.303 C 607.123 474.953 575.213 522.623 533.553 559.183 C 494.063 593.983 445.783 618.773 394.413 630.293 C 317.923 647.853 234.993 635.933 166.883 596.823 C 100.763 559.523 48.853 497.613 24.213 425.763 C 7.893 378.993 3.173 328.313 10.073 279.293 C 16.463 233.443 33.323 189.123 58.903 150.553 C 78.103 121.503 102.133 95.633 129.743 74.393 C 179.253 35.943 240.463 12.863 303.023 8.993 M 251.533 117.223 C 232.073 121.183 213.863 130.603 199.063 143.803 C 174.963 165.113 160.223 195.183 152.533 226.023 C 149.973 243.353 151.333 261.003 153.923 278.243 C 154.813 285.863 156.613 293.353 157.213 301.023 C 158.493 315.503 161.273 329.793 163.343 344.173 C 159.383 347.803 154.563 350.533 151.213 354.783 C 148.013 358.843 148.963 364.313 149.213 369.093 C 150.783 383.533 156.013 397.213 160.913 410.783 C 163.203 417.553 165.983 424.453 171.333 429.433 C 177.603 435.933 186.163 439.283 194.603 441.953 C 202.633 458.533 211.333 474.823 221.513 490.203 C 232.373 506.593 245.393 521.483 259.663 534.993 C 270.073 544.623 282.533 552.893 296.843 554.963 C 316.423 557.973 337.293 558.373 355.823 550.433 C 365.003 546.653 372.503 540.003 379.763 533.413 C 407.303 507.973 427.503 475.663 443.373 441.953 C 448.383 440.103 453.613 438.693 458.193 435.863 C 465.503 431.613 471.683 425.123 474.623 417.103 C 478.023 408.133 481.243 399.083 484.223 389.963 C 486.953 379.943 489.873 369.623 488.743 359.143 C 486.923 352.013 479.513 348.843 474.633 344.073 C 478.263 320.603 482.493 297.043 481.893 273.193 C 481.913 256.623 479.053 240.163 474.443 224.283 C 470.303 207.713 465.613 190.893 456.383 176.343 C 453.423 171.423 448.713 167.953 445.593 163.153 C 434.143 148.663 418.373 138.153 401.763 130.433 C 401.673 129.983 401.483 129.073 401.393 128.623 C 399.743 129.023 398.063 128.983 396.533 128.233 C 381.453 121.783 365.393 117.993 349.243 115.433 C 316.803 111.073 283.833 112.403 251.533 117.223 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 251.533 117.223 C 283.833 112.403 316.803 111.073 349.243 115.433 C 365.393 117.993 381.453 121.783 396.533 128.233 C 398.063 128.983 399.743 129.023 401.393 128.623 C 401.483 129.073 401.673 129.983 401.763 130.433 C 418.373 138.153 434.143 148.663 445.593 163.153 C 448.713 167.953 453.423 171.423 456.383 176.343 C 465.613 190.893 470.303 207.713 474.443 224.283 C 479.053 240.163 481.913 256.623 481.893 273.193 C 482.493 297.043 478.263 320.603 474.633 344.073 C 479.513 348.843 486.923 352.013 488.743 359.143 C 489.873 369.623 486.953 379.943 484.223 389.963 C 481.243 399.083 478.023 408.133 474.623 417.103 C 471.683 425.123 465.503 431.613 458.193 435.863 C 453.613 438.693 448.383 440.103 443.373 441.953 C 427.503 475.663 407.303 507.973 379.763 533.413 C 372.503 540.003 365.003 546.653 355.823 550.433 C 337.293 558.373 316.423 557.973 296.843 554.963 C 282.533 552.893 270.073 544.623 259.663 534.993 C 245.393 521.483 232.373 506.593 221.513 490.203 C 211.333 474.823 202.633 458.533 194.603 441.953 C 186.163 439.283 177.603 435.933 171.333 429.433 C 165.983 424.453 163.203 417.553 160.913 410.783 C 156.013 397.213 150.783 383.533 149.213 369.093 C 148.963 364.313 148.013 358.843 151.213 354.783 C 154.563 350.533 159.383 347.803 163.343 344.173 C 161.273 329.793 158.493 315.503 157.213 301.023 C 156.613 293.353 154.813 285.863 153.923 278.243 C 151.333 261.003 149.973 243.353 152.533 226.023 C 160.223 195.183 174.963 165.113 199.063 143.803 C 213.863 130.603 232.073 121.183 251.533 117.223 M 210.493 227.813 C 209.643 236.653 207.483 245.283 205.263 253.863 C 202.683 263.083 200.683 272.503 196.983 281.373 C 191.973 295.273 189.443 309.983 188.343 324.693 C 186.223 350.823 187.293 377.173 191.053 403.083 C 194.163 413.243 198.593 422.943 202.463 432.833 C 226.413 425.023 246.353 409.083 267.823 396.463 C 279.353 389.893 291.393 383.763 304.533 381.243 C 320.323 377.783 336.923 380.633 351.623 386.953 C 380.983 399.633 404.823 422.833 435.473 432.833 C 439.953 421.133 444.593 409.493 449.013 397.773 C 450.073 394.313 451.903 390.883 451.553 387.163 C 450.183 371.103 447.083 355.203 446.453 339.083 C 444.833 331.173 441.663 323.673 439.673 315.853 C 436.823 303.603 428.173 293.793 419.093 285.553 C 407.183 275.473 392.933 267.933 377.733 264.283 C 386.883 276.813 400.213 285.363 412.413 294.623 C 407.233 295.133 402.103 294.133 397.133 292.743 C 383.463 288.773 370.623 282.393 358.103 275.693 C 340.363 266.113 323.273 255.383 306.533 244.163 C 304.793 242.763 302.523 243.073 300.463 243.073 C 305.193 249.653 311.183 255.173 317.093 260.663 C 331.263 273.173 346.433 284.493 361.863 295.403 C 347.333 296.653 333.163 291.893 319.723 286.943 C 297.583 278.413 276.493 267.413 255.883 255.723 C 240.503 246.833 225.263 237.673 210.493 227.813 M 164.053 355.223 C 162.073 361.963 160.363 369.023 161.323 376.083 C 162.393 382.283 164.683 388.183 166.503 394.183 C 169.683 404.133 173.133 414.523 180.533 422.213 C 183.243 425.173 186.693 427.283 190.143 429.283 C 181.953 404.423 174.343 379.323 164.053 355.223 M 447.743 429.243 C 454.513 424.973 461.633 420.023 464.783 412.353 C 468.583 402.043 472.753 391.833 475.543 381.193 C 477.663 372.583 477.343 363.353 473.863 355.143 C 463.073 379.063 455.713 404.293 447.743 429.243 M 290.893 394.553 C 261.713 408.423 236.703 430.043 206.293 441.473 C 223.603 476.883 245.893 511.023 277.543 535.253 C 303.003 551.233 338.133 550.963 362.823 533.523 C 393.043 509.153 414.813 476.033 431.633 441.423 C 404.793 431.363 382.083 413.373 357.133 399.693 C 349.933 395.973 342.743 392.013 334.823 390.083 C 320.253 386.333 304.393 387.923 290.893 394.553 Z"/>
<path fill="#ff0000" stroke="#ff0000" stroke-width="5" opacity="1.00" d="M 210.493 227.813 C 225.263 237.673 240.503 246.833 255.883 255.723 C 276.493 267.413 297.583 278.413 319.723 286.943 C 333.163 291.893 347.333 296.653 361.863 295.403 C 346.433 284.493 331.263 273.173 317.093 260.663 C 311.183 255.173 305.193 249.653 300.463 243.073 C 302.523 243.073 304.793 242.763 306.533 244.163 C 323.273 255.383 340.363 266.113 358.103 275.693 C 370.623 282.393 383.463 288.773 397.133 292.743 C 402.103 294.133 407.233 295.133 412.413 294.623 C 400.213 285.363 386.883 276.813 377.733 264.283 C 392.933 267.933 407.183 275.473 419.093 285.553 C 428.173 293.793 436.823 303.603 439.673 315.853 C 441.663 323.673 444.833 331.173 446.453 339.083 C 447.083 355.203 450.183 371.103 451.553 387.163 C 451.903 390.883 450.073 394.313 449.013 397.773 C 444.593 409.493 439.953 421.133 435.473 432.833 C 404.823 422.833 380.983 399.633 351.623 386.953 C 336.923 380.633 320.323 377.783 304.533 381.243 C 291.393 383.763 279.353 389.893 267.823 396.463 C 246.353 409.083 226.413 425.023 202.463 432.833 C 198.593 422.943 194.163 413.243 191.053 403.083 C 187.293 377.173 186.223 350.823 188.343 324.693 C 189.443 309.983 191.973 295.273 196.983 281.373 C 200.683 272.503 202.683 263.083 205.263 253.863 C 207.483 245.283 209.643 236.653 210.493 227.813 M 233.533 320.473 C 250.363 328.833 269.573 325.373 287.613 325.633 C 287.743 323.333 287.883 321.033 288.033 318.743 C 269.853 318.703 251.603 318.003 233.533 320.473 M 350.343 318.793 C 350.293 321.093 350.253 323.393 350.213 325.703 C 368.443 325.833 386.793 326.603 404.863 323.573 C 387.623 316.153 368.533 319.023 350.343 318.793 M 248.953 347.643 C 246.223 350.563 247.123 355.233 249.623 357.993 C 254.273 360.823 260.043 361.503 265.363 360.633 C 272.443 359.443 278.533 355.003 283.023 349.553 C 276.613 346.913 270.403 343.363 263.453 342.393 C 258.243 342.193 252.573 343.723 248.953 347.643 M 355.533 348.993 C 360.833 358.343 373.133 363.653 383.243 359.113 C 388.493 357.083 391.873 352.203 393.523 347.013 C 381.483 341.523 366.833 341.793 355.533 348.993 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 233.533 320.473 C 251.603 318.003 269.853 318.703 288.033 318.743 C 287.883 321.033 287.743 323.333 287.613 325.633 C 269.573 325.373 250.363 328.833 233.533 320.473 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 350.343 318.793 C 368.533 319.023 387.623 316.153 404.863 323.573 C 386.793 326.603 368.443 325.833 350.213 325.703 C 350.253 323.393 350.293 321.093 350.343 318.793 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 248.953 347.643 C 252.573 343.723 258.243 342.193 263.453 342.393 C 270.403 343.363 276.613 346.913 283.023 349.553 C 278.533 355.003 272.443 359.443 265.363 360.633 C 260.043 361.503 254.273 360.823 249.623 357.993 C 247.123 355.233 246.223 350.563 248.953 347.643 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 355.533 348.993 C 366.833 341.793 381.483 341.523 393.523 347.013 C 391.873 352.203 388.493 357.083 383.243 359.113 C 373.133 363.653 360.833 358.343 355.533 348.993 Z"/>
<path fill="#ff0000" stroke="#ff0000" stroke-width="5" opacity="1.00" d="M 164.053 355.223 C 174.343 379.323 181.953 404.423 190.143 429.283 C 186.693 427.283 183.243 425.173 180.533 422.213 C 173.133 414.523 169.683 404.133 166.503 394.183 C 164.683 388.183 162.393 382.283 161.323 376.083 C 160.363 369.023 162.073 361.963 164.053 355.223 Z"/>
<path fill="#ff0000" stroke="#ff0000" stroke-width="5" opacity="1.00" d="M 447.743 429.243 C 455.713 404.293 463.073 379.063 473.863 355.143 C 477.343 363.353 477.663 372.583 475.543 381.193 C 472.753 391.833 468.583 402.043 464.783 412.353 C 461.633 420.023 454.513 424.973 447.743 429.243 Z"/>
<path fill="#ff0000" stroke="#ff0000" stroke-width="5" opacity="1.00" d="M 290.893 394.553 C 304.393 387.923 320.253 386.333 334.823 390.083 C 342.743 392.013 349.933 395.973 357.133 399.693 C 382.083 413.373 404.793 431.363 431.633 441.423 C 414.813 476.033 393.043 509.153 362.823 533.523 C 338.133 550.963 303.003 551.233 277.543 535.253 C 245.893 511.023 223.603 476.883 206.293 441.473 C 236.703 430.043 261.713 408.423 290.893 394.553 M 273.033 447.823 C 283.293 453.293 295.283 452.943 306.573 452.953 C 326.123 452.673 345.903 453.823 365.173 449.763 C 359.523 445.593 352.593 443.473 345.603 443.333 C 330.583 443.123 315.543 443.663 300.523 443.193 C 291.183 442.983 281.623 443.913 273.033 447.823 M 280.833 464.783 C 278.863 467.923 276.873 471.053 274.893 474.193 C 304.223 476.393 333.693 476.403 363.023 474.153 C 361.023 471.013 359.023 467.883 356.993 464.773 C 331.613 464.803 306.223 464.773 280.833 464.783 M 271.573 491.723 C 301.863 490.923 332.173 491.793 362.463 490.773 C 354.723 488.243 346.573 487.443 338.473 487.243 C 327.503 487.183 316.523 487.013 305.543 487.083 C 294.113 487.183 282.413 487.713 271.573 491.723 M 280.083 493.493 C 280.033 495.003 279.993 496.523 279.963 498.043 C 307.443 496.733 335.473 501.683 362.463 494.403 C 335.013 493.183 307.533 494.363 280.083 493.493 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 273.033 447.823 C 281.623 443.913 291.183 442.983 300.523 443.193 C 315.543 443.663 330.583 443.123 345.603 443.333 C 352.593 443.473 359.523 445.593 365.173 449.763 C 345.903 453.823 326.123 452.673 306.573 452.953 C 295.283 452.943 283.293 453.293 273.033 447.823 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 280.833 464.783 C 306.223 464.773 331.613 464.803 356.993 464.773 C 359.023 467.883 361.023 471.013 363.023 474.153 C 333.693 476.403 304.223 476.393 274.893 474.193 C 276.873 471.053 278.863 467.923 280.833 464.783 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 271.573 491.723 C 282.413 487.713 294.113 487.183 305.543 487.083 C 316.523 487.013 327.503 487.183 338.473 487.243 C 346.573 487.443 354.723 488.243 362.463 490.773 C 332.173 491.793 301.863 490.923 271.573 491.723 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 280.083 493.493 C 307.533 494.363 335.013 493.183 362.463 494.403 C 335.473 501.683 307.443 496.733 279.963 498.043 C 279.993 496.523 280.033 495.003 280.083 493.493 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="unhealthy-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#ff7e00" stroke="#ff7e00" stroke-width="2" opacity="1.00" d="M 316.007 10.138 C 365.027 8.368 414.467 18.068 459.067 38.548 C 527.987 69.788 584.957 126.638 615.997 195.688 C 648.837 267.368 652.787 351.658 627.067 426.158 C 609.427 478.148 577.627 525.168 536.297 561.258 C 494.117 598.318 441.897 623.838 386.707 634.208 C 347.267 641.698 306.427 641.658 266.997 634.138 C 227.337 626.568 189.137 611.328 155.237 589.408 C 91.907 548.938 43.757 485.178 22.867 412.928 C 9.577 367.808 6.837 319.678 14.507 273.288 C 22.177 226.838 40.597 182.208 67.887 143.848 C 105.807 90.048 160.967 48.618 223.237 27.278 C 253.057 16.948 284.457 11.178 316.007 10.138 M 255.587 118.688 C 224.477 124.618 197.317 144.868 180.077 171.088 C 168.797 188.048 161.007 207.168 155.977 226.868 C 153.627 240.738 154.337 254.918 155.677 268.868 C 157.787 285.748 160.497 302.648 165.477 318.958 C 168.377 327.558 171.397 336.118 173.957 344.828 C 170.237 347.618 166.507 350.398 162.767 353.138 C 163.757 371.798 169.497 389.818 176.717 406.928 C 178.677 412.158 181.347 417.278 185.567 421.048 C 191.317 426.668 199.077 429.348 206.767 431.078 C 220.607 458.998 237.387 485.888 259.667 507.888 C 269.417 517.388 279.667 527.128 292.637 531.998 C 310.907 538.938 331.087 538.488 350.027 534.928 C 361.767 533.168 371.887 526.298 380.857 518.908 C 408.707 495.088 429.427 464.048 445.367 431.298 C 452.687 429.408 460.147 427.018 465.887 421.868 C 470.687 417.958 473.577 412.328 475.707 406.628 C 482.027 391.298 487.707 375.428 489.347 358.838 C 488.787 350.678 480.767 346.408 475.227 341.608 C 476.447 333.658 477.377 325.668 478.877 317.768 C 481.317 306.898 483.257 295.858 483.427 284.688 C 483.907 256.938 479.977 229.018 471.117 202.678 C 466.737 190.428 461.587 177.848 451.917 168.768 C 444.337 158.048 434.357 149.218 423.387 142.098 C 394.167 123.378 359.097 115.848 324.787 114.718 C 301.647 113.508 278.497 115.558 255.587 118.688 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 255.587 118.688 C 278.497 115.558 301.647 113.508 324.787 114.718 C 359.097 115.848 394.167 123.378 423.387 142.098 C 434.357 149.218 444.337 158.048 451.917 168.768 C 461.587 177.848 466.737 190.428 471.117 202.678 C 479.977 229.018 483.907 256.938 483.427 284.688 C 483.257 295.858 481.317 306.898 478.877 317.768 C 477.377 325.668 476.447 333.658 475.227 341.608 C 480.767 346.408 488.787 350.678 489.347 358.838 C 487.707 375.428 482.027 391.298 475.707 406.628 C 473.577 412.328 470.687 417.958 465.887 421.868 C 460.147 427.018 452.687 429.408 445.367 431.298 C 429.427 464.048 408.707 495.088 380.857 518.908 C 371.887 526.298 361.767 533.168 350.027 534.928 C 331.087 538.488 310.907 538.938 292.637 531.998 C 279.667 527.128 269.417 517.388 259.667 507.888 C 237.387 485.888 220.607 458.998 206.767 431.078 C 199.077 429.348 191.317 426.668 185.567 421.048 C 181.347 417.278 178.677 412.158 176.717 406.928 C 169.497 389.818 163.757 371.798 162.767 353.138 C 166.507 350.398 170.237 347.618 173.957 344.828 C 171.397 336.118 168.377 327.558 165.477 318.958 C 160.497 302.648 157.787 285.748 155.677 268.868 C 154.337 254.918 153.627 240.738 155.977 226.868 C 161.007 207.168 168.797 188.048 180.077 171.088 C 197.317 144.868 224.477 124.618 255.587 118.688 M 213.767 229.428 C 212.917 238.278 210.747 246.918 208.537 255.508 C 205.977 264.718 203.937 274.118 200.267 282.968 C 191.987 305.938 190.597 330.668 190.637 354.878 C 190.187 364.538 191.397 374.148 191.827 383.788 C 193.027 393.998 194.377 404.228 194.217 414.538 C 188.537 395.428 183.287 376.178 177.687 357.038 C 176.237 357.148 174.787 357.288 173.337 357.418 C 177.017 378.618 182.937 400.058 195.787 417.688 C 199.167 415.808 202.307 413.478 205.837 411.888 C 209.217 411.298 210.897 414.948 212.197 417.398 C 219.497 432.458 226.687 447.648 235.977 461.618 C 247.227 479.218 260.827 495.288 276.347 509.268 C 288.657 521.228 305.857 527.158 322.817 527.628 C 337.567 528.238 352.557 524.818 365.357 517.388 C 399.227 492.628 422.317 456.188 439.537 418.528 C 440.837 415.858 442.077 411.808 445.777 412.088 C 449.927 412.568 453.307 415.388 456.617 417.708 C 469.367 400.028 475.387 378.618 478.967 357.388 C 475.457 357.248 471.947 357.128 468.447 357.048 C 464.537 373.278 460.837 389.558 457.067 405.818 C 454.517 384.878 451.837 363.908 449.487 342.978 C 444.507 331.258 441.297 318.928 437.587 306.788 C 435.817 300.178 430.587 295.388 426.057 290.588 C 413.577 278.618 397.787 269.978 380.987 265.928 C 390.167 278.428 403.487 286.898 415.597 296.258 C 409.847 296.828 404.217 295.438 398.727 293.908 C 382.877 288.868 368.047 281.118 353.577 273.008 C 335.267 262.778 317.817 251.108 300.337 239.538 C 305.417 248.368 312.937 255.398 320.347 262.258 C 334.507 274.778 349.687 286.088 365.117 297.008 C 350.907 298.278 337.017 293.648 323.847 288.878 C 300.297 279.878 277.927 268.108 256.097 255.578 C 241.777 247.198 227.547 238.658 213.767 229.428 Z"/>
<path fill="#ff7e00" stroke="#ff7e00" stroke-width="2" opacity="1.00" d="M 213.767 229.428 C 227.547 238.658 241.777 247.198 256.097 255.578 C 277.927 268.108 300.297 279.878 323.847 288.878 C 337.017 293.648 350.907 298.278 365.117 297.008 C 349.687 286.088 334.507 274.778 320.347 262.258 C 312.937 255.398 305.417 248.368 300.337 239.538 C 317.817 251.108 335.267 262.778 353.577 273.008 C 368.047 281.118 382.877 288.868 398.727 293.908 C 404.217 295.438 409.847 296.828 415.597 296.258 C 403.487 286.898 390.167 278.428 380.987 265.928 C 397.787 269.978 413.577 278.618 426.057 290.588 C 430.587 295.388 435.817 300.178 437.587 306.788 C 441.297 318.928 444.507 331.258 449.487 342.978 C 451.837 363.908 454.517 384.878 457.067 405.818 C 460.837 389.558 464.537 373.278 468.447 357.048 C 471.947 357.128 475.457 357.248 478.967 357.388 C 475.387 378.618 469.367 400.028 456.617 417.708 C 453.307 415.388 449.927 412.568 445.777 412.088 C 442.077 411.808 440.837 415.858 439.537 418.528 C 422.317 456.188 399.227 492.628 365.357 517.388 C 352.557 524.818 337.567 528.238 322.817 527.628 C 305.857 527.158 288.657 521.228 276.347 509.268 C 260.827 495.288 247.227 479.218 235.977 461.618 C 226.687 447.648 219.497 432.458 212.197 417.398 C 210.897 414.948 209.217 411.298 205.837 411.888 C 202.307 413.478 199.167 415.808 195.787 417.688 C 182.937 400.058 177.017 378.618 173.337 357.418 C 174.787 357.288 176.237 357.148 177.687 357.038 C 183.287 376.178 188.537 395.428 194.217 414.538 C 194.377 404.228 193.027 393.998 191.827 383.788 C 191.397 374.148 190.187 364.538 190.637 354.878 C 190.597 330.668 191.987 305.938 200.267 282.968 C 203.937 274.118 205.977 264.718 208.537 255.508 C 210.747 246.918 212.917 238.278 213.767 229.428 M 353.377 306.828 C 353.787 312.018 356.257 316.998 360.497 320.108 C 367.557 325.448 376.287 327.918 384.707 330.218 C 393.307 332.498 403.637 332.038 410.137 325.228 C 390.197 323.228 370.427 317.548 353.377 306.828 M 242.157 325.388 C 249.517 332.278 260.507 332.448 269.707 329.828 C 278.177 327.268 286.757 324.288 293.927 318.958 C 297.427 316.398 298.407 311.868 297.447 307.808 C 280.747 318.018 261.497 323.288 242.157 325.388 M 360.847 346.958 C 364.727 352.768 370.837 357.288 377.867 358.218 C 383.897 359.168 390.097 356.548 394.327 352.328 C 397.347 346.888 391.447 341.228 386.317 340.188 C 377.197 336.748 368.547 342.818 360.847 346.958 M 258.357 345.288 C 255.937 347.838 257.647 351.728 259.807 353.838 C 263.557 358.018 269.587 358.578 274.857 358.458 C 281.887 357.678 287.117 352.278 291.877 347.538 C 285.827 343.598 279.337 339.448 271.857 339.378 C 266.697 339.188 262.147 342.108 258.357 345.288 M 306.407 404.408 C 318.897 409.698 333.187 409.168 345.737 404.368 C 333.757 397.708 318.307 397.458 306.407 404.408 M 296.117 451.248 C 289.937 454.298 285.107 459.638 282.497 465.998 C 290.487 463.408 298.177 460.058 305.927 456.878 C 315.207 453.068 325.637 451.878 335.427 454.328 C 345.637 457.148 355.007 462.238 365.047 465.538 C 363.967 460.968 361.677 456.588 357.737 453.868 C 349.627 448.028 339.347 446.728 329.717 445.308 C 318.327 445.238 306.437 445.938 296.117 451.248 M 314.597 468.138 C 313.317 470.968 311.987 473.778 310.647 476.578 C 321.977 476.488 333.407 476.588 344.547 474.188 C 336.007 467.768 324.747 468.198 314.597 468.138 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 353.377 306.828 C 370.427 317.548 390.197 323.228 410.137 325.228 C 403.637 332.038 393.307 332.498 384.707 330.218 C 376.287 327.918 367.557 325.448 360.497 320.108 C 356.257 316.998 353.787 312.018 353.377 306.828 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 242.157 325.388 C 261.497 323.288 280.747 318.018 297.447 307.808 C 298.407 311.868 297.427 316.398 293.927 318.958 C 286.757 324.288 278.177 327.268 269.707 329.828 C 260.507 332.448 249.517 332.278 242.157 325.388 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 360.847 346.958 C 368.547 342.818 377.197 336.748 386.317 340.188 C 391.447 341.228 397.347 346.888 394.327 352.328 C 390.097 356.548 383.897 359.168 377.867 358.218 C 370.837 357.288 364.727 352.768 360.847 346.958 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 258.357 345.288 C 262.147 342.108 266.697 339.188 271.857 339.378 C 279.337 339.448 285.827 343.598 291.877 347.538 C 287.117 352.278 281.887 357.678 274.857 358.458 C 269.587 358.578 263.557 358.018 259.807 353.838 C 257.647 351.728 255.937 347.838 258.357 345.288 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 306.407 404.408 C 318.307 397.458 333.757 397.708 345.737 404.368 C 333.187 409.168 318.897 409.698 306.407 404.408 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 296.117 451.248 C 306.437 445.938 318.327 445.238 329.717 445.308 C 339.347 446.728 349.627 448.028 357.737 453.868 C 361.677 456.588 363.967 460.968 365.047 465.538 C 355.007 462.238 345.637 457.148 335.427 454.328 C 325.637 451.878 315.207 453.068 305.927 456.878 C 298.177 460.058 290.487 463.408 282.497 465.998 C 285.107 459.638 289.937 454.298 296.117 451.248 Z"/>
<path fill="#555" stroke="#555" stroke-width="2" opacity="1.00" d="M 314.597 468.138 C 324.747 468.198 336.007 467.768 344.547 474.188 C 333.407 476.588 321.977 476.488 310.647 476.578 C 311.987 473.778 313.317 470.968 314.597 468.138 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="very-unhealthy-air-quality" width="1024pt" height="1024pt" viewBox="0 0 650 650" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 300.083 11.549 C 347.253 8.309 395.173 15.619 439.103 33.159 C 514.263 62.689 577.103 122.209 610.383 195.809 C 643.173 266.889 647.513 350.449 622.613 424.629 C 606.063 474.649 576.483 520.209 537.843 555.989 C 497.623 593.389 447.513 620.139 393.953 632.419 C 317.053 650.489 233.473 638.689 164.913 599.319 C 98.763 562.009 46.833 500.079 22.193 428.189 C 6.673 383.679 1.643 335.599 7.173 288.809 C 13.093 237.599 32.123 188.019 61.783 145.879 C 82.163 116.789 107.493 91.169 136.343 70.459 C 183.983 36.019 241.423 15.349 300.083 11.549 M 249.023 119.819 C 217.703 126.189 190.603 147.069 173.613 173.779 C 162.863 190.519 155.313 209.219 150.543 228.499 C 147.973 245.849 149.343 263.519 151.933 280.779 C 153.663 294.469 156.893 307.909 160.213 321.289 C 160.563 329.609 162.703 337.709 163.933 345.919 C 160.273 346.809 156.623 347.719 152.973 348.629 C 151.123 352.809 149.393 357.129 149.183 361.759 C 148.183 373.589 153.473 384.539 156.943 395.529 C 160.213 404.479 162.293 414.209 168.503 421.739 C 174.643 429.729 184.133 434.769 194.123 435.819 C 195.883 439.569 197.633 443.309 199.393 447.059 C 189.283 452.009 178.803 456.389 169.703 463.129 C 167.823 472.959 174.333 481.389 178.453 489.709 C 189.523 510.279 200.563 530.869 211.643 551.439 C 213.813 555.219 215.863 559.159 218.823 562.399 C 221.183 565.019 225.113 565.869 228.393 564.599 C 245.273 558.149 261.143 549.289 276.383 539.629 C 300.873 547.699 327.633 547.929 352.523 541.469 C 354.323 541.039 356.253 540.469 358.063 541.239 C 364.713 543.359 370.823 546.829 376.793 550.389 C 388.273 557.189 400.373 564.749 414.233 564.369 C 420.103 556.959 424.893 548.789 429.073 540.319 C 433.413 531.689 438.233 523.299 442.653 514.709 C 450.453 500.119 459.193 485.919 465.153 470.449 C 465.973 465.309 462.283 460.629 458.153 458.049 C 450.973 453.669 442.633 451.669 435.423 447.329 C 437.173 443.499 438.943 439.689 440.743 435.889 C 448.463 433.639 457.183 432.189 462.933 426.069 C 473.333 413.899 477.103 397.929 482.043 383.089 C 483.723 377.379 486.513 371.779 486.153 365.679 C 486.163 359.759 484.203 354.089 482.083 348.629 C 478.383 347.709 474.673 346.789 470.973 345.879 C 472.823 334.399 475.513 322.969 475.993 311.339 C 479.283 290.599 479.173 269.359 476.173 248.589 C 473.573 229.899 469.403 211.289 462.133 193.819 C 458.673 185.849 454.793 177.779 448.493 171.629 C 445.623 168.809 443.573 165.289 440.883 162.309 C 430.373 150.439 417.063 141.329 402.883 134.399 C 383.013 124.839 361.333 119.429 339.483 116.979 C 309.373 113.789 278.913 115.269 249.023 119.819 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 249.023 119.819 C 278.913 115.269 309.373 113.789 339.483 116.979 C 361.333 119.429 383.013 124.839 402.883 134.399 C 417.063 141.329 430.373 150.439 440.883 162.309 C 443.573 165.289 445.623 168.809 448.493 171.629 C 454.793 177.779 458.673 185.849 462.133 193.819 C 469.403 211.289 473.573 229.899 476.173 248.589 C 479.173 269.359 479.283 290.599 475.993 311.339 C 470.223 326.619 467.053 342.729 463.063 358.529 C 459.103 374.529 455.583 390.639 451.763 406.669 C 449.353 384.999 446.003 363.419 444.293 341.689 C 443.703 338.669 442.523 335.829 441.513 332.949 C 439.073 326.289 437.333 319.399 435.233 312.639 C 432.863 304.199 426.733 297.569 420.773 291.449 C 408.273 279.429 392.443 270.879 375.633 266.719 C 384.893 279.259 398.223 287.859 410.463 297.189 C 400.983 297.729 391.843 294.519 383.163 291.099 C 359.233 281.269 337.073 267.719 315.363 253.789 C 310.383 250.849 305.823 245.919 299.473 247.059 C 309.493 259.269 321.893 269.189 334.203 278.969 C 342.573 285.549 351.253 291.719 359.893 297.939 C 341.113 299.189 323.253 291.719 306.223 284.759 C 272.003 269.809 239.763 250.689 208.503 230.339 C 206.573 246.459 201.813 262.079 197.313 277.629 C 192.083 290.439 188.913 303.999 187.213 317.719 C 185.143 337.949 184.553 358.389 186.193 378.669 C 186.893 391.129 189.253 403.469 188.993 415.989 C 183.523 396.949 178.313 377.839 172.613 358.869 C 168.733 346.269 165.513 333.389 160.213 321.289 C 156.893 307.909 153.663 294.469 151.933 280.779 C 149.343 263.519 147.973 245.849 150.543 228.499 C 155.313 209.219 162.863 190.519 173.613 173.779 C 190.603 147.069 217.703 126.189 249.023 119.819 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 208.503 230.339 C 239.763 250.689 272.003 269.809 306.223 284.759 C 323.253 291.719 341.113 299.189 359.893 297.939 C 351.253 291.719 342.573 285.549 334.203 278.969 C 321.893 269.189 309.493 259.269 299.473 247.059 C 305.823 245.919 310.383 250.849 315.363 253.789 C 337.073 267.719 359.233 281.269 383.163 291.099 C 391.843 294.519 400.983 297.729 410.463 297.189 C 398.223 287.859 384.893 279.259 375.633 266.719 C 392.443 270.879 408.273 279.429 420.773 291.449 C 426.733 297.569 432.863 304.199 435.233 312.639 C 437.333 319.399 439.073 326.289 441.513 332.949 C 442.523 335.829 443.703 338.669 444.293 341.689 C 446.003 363.419 449.353 384.999 451.763 406.669 C 455.583 390.639 459.103 374.529 463.063 358.529 C 467.343 358.489 471.633 358.459 475.913 358.459 C 473.013 381.029 466.753 403.889 452.703 422.219 C 448.333 420.059 443.963 417.889 439.603 415.729 C 434.833 424.119 430.433 432.709 426.253 441.419 C 417.003 441.799 407.783 442.559 398.593 443.699 C 389.763 431.309 381.043 418.759 370.313 407.899 C 359.843 397.019 347.773 387.579 334.253 380.789 C 324.943 376.319 313.743 375.419 304.073 379.269 C 286.643 386.819 271.753 399.259 259.273 413.429 C 250.723 422.889 243.483 433.399 235.953 443.669 C 226.903 442.629 217.843 441.769 208.743 441.339 C 204.473 432.629 199.973 424.029 195.153 415.609 C 190.833 417.799 186.523 420.009 182.213 422.209 C 168.103 403.889 162.193 380.929 158.693 358.469 C 163.333 358.579 167.973 358.719 172.613 358.869 C 178.313 377.839 183.523 396.949 188.993 415.989 C 189.253 403.469 186.893 391.129 186.193 378.669 C 184.553 358.389 185.143 337.949 187.213 317.719 C 188.913 303.999 192.083 290.439 197.313 277.629 C 201.813 262.079 206.573 246.459 208.503 230.339 M 234.203 320.169 C 237.943 323.989 243.113 326.109 248.453 326.229 C 258.123 326.549 267.843 326.839 277.503 326.039 C 282.443 325.689 287.103 323.639 290.893 320.499 C 272.053 318.679 253.063 318.749 234.203 320.169 M 344.613 320.389 C 348.133 323.749 352.733 325.879 357.623 326.139 C 366.903 326.779 376.213 326.499 385.513 326.289 C 391.133 326.319 396.773 324.369 400.593 320.129 C 381.963 318.769 363.223 318.729 344.613 320.389 M 247.853 347.059 C 245.843 349.249 245.823 352.989 248.113 354.999 C 256.863 362.969 272.733 361.829 279.123 351.399 C 274.653 338.879 255.993 337.739 247.853 347.059 M 360.933 344.029 C 356.753 346.619 355.923 353.219 359.693 356.499 C 368.973 362.419 383.073 361.259 389.633 351.779 C 388.003 346.079 382.853 342.119 377.073 341.309 C 371.633 340.429 365.683 341.089 360.933 344.029 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 463.063 358.529 C 467.053 342.729 470.223 326.619 475.993 311.339 C 475.513 322.969 472.823 334.399 470.973 345.879 C 474.673 346.789 478.383 347.709 482.083 348.629 C 484.203 354.089 486.163 359.759 486.153 365.679 C 486.513 371.779 483.723 377.379 482.043 383.089 C 477.103 397.929 473.333 413.899 462.933 426.069 C 457.183 432.189 448.463 433.639 440.743 435.889 C 438.943 439.689 437.173 443.499 435.423 447.329 C 442.633 451.669 450.973 453.669 458.153 458.049 C 462.283 460.629 465.973 465.309 465.153 470.449 C 459.193 485.919 450.453 500.119 442.653 514.709 C 438.233 523.299 433.413 531.689 429.073 540.319 C 424.893 548.789 420.103 556.959 414.233 564.369 C 400.373 564.749 388.273 557.189 376.793 550.389 C 370.823 546.829 364.713 543.359 358.063 541.239 C 356.253 540.469 354.323 541.039 352.523 541.469 C 327.633 547.929 300.873 547.699 276.383 539.629 C 261.143 549.289 245.273 558.149 228.393 564.599 C 225.113 565.869 221.183 565.019 218.823 562.399 C 215.863 559.159 213.813 555.219 211.643 551.439 C 200.563 530.869 189.523 510.279 178.453 489.709 C 174.333 481.389 167.823 472.959 169.703 463.129 C 178.803 456.389 189.283 452.009 199.393 447.059 C 197.633 443.309 195.883 439.569 194.123 435.819 C 184.133 434.769 174.643 429.729 168.503 421.739 C 162.293 414.209 160.213 404.479 156.943 395.529 C 153.473 384.539 148.183 373.589 149.183 361.759 C 149.393 357.129 151.123 352.809 152.973 348.629 C 156.623 347.719 160.273 346.809 163.933 345.919 C 162.703 337.709 160.563 329.609 160.213 321.289 C 165.513 333.389 168.733 346.269 172.613 358.869 C 167.973 358.719 163.333 358.579 158.693 358.469 C 162.193 380.929 168.103 403.889 182.213 422.209 C 186.523 420.009 190.833 417.799 195.153 415.609 C 199.973 424.029 204.473 432.629 208.743 441.339 C 217.843 441.769 226.903 442.629 235.953 443.669 C 243.483 433.399 250.723 422.889 259.273 413.429 C 271.753 399.259 286.643 386.819 304.073 379.269 C 313.743 375.419 324.943 376.319 334.253 380.789 C 347.773 387.579 359.843 397.019 370.313 407.899 C 381.043 418.759 389.763 431.309 398.593 443.699 C 407.783 442.559 417.003 441.799 426.253 441.419 C 430.433 432.709 434.833 424.119 439.603 415.729 C 443.963 417.889 448.333 420.059 452.703 422.219 C 466.753 403.889 473.013 381.029 475.913 358.459 C 471.633 358.459 467.343 358.489 463.063 358.529 M 298.463 390.529 C 274.033 405.129 255.543 427.709 240.823 451.699 C 246.073 461.639 251.233 471.639 256.883 481.359 C 265.553 496.429 273.713 511.979 278.303 528.839 C 302.823 540.149 332.173 540.199 356.663 528.779 C 361.093 511.329 369.763 495.399 378.683 479.889 C 384.033 470.719 388.843 461.249 393.793 451.869 C 381.893 430.849 365.293 412.809 346.773 397.449 C 337.943 390.769 327.983 384.119 316.503 383.959 C 309.833 383.639 303.933 387.169 298.463 390.529 M 196.013 458.459 C 211.563 488.339 226.933 518.329 244.073 547.339 C 249.953 544.159 256.043 541.339 261.793 537.919 C 266.833 534.869 271.103 529.249 270.133 523.079 C 266.983 515.199 263.033 507.649 259.043 500.159 C 251.843 486.749 244.923 473.189 237.323 459.999 C 234.463 454.879 230.193 450.549 224.793 448.149 C 214.573 449.559 205.363 454.389 196.013 458.459 M 405.723 449.769 C 397.523 456.799 393.213 467.059 388.213 476.369 C 380.673 491.429 371.533 505.709 365.253 521.379 C 362.693 527.749 366.873 534.619 372.303 537.949 C 378.343 541.619 384.843 544.479 391.163 547.659 C 398.893 533.579 406.643 519.519 414.383 505.449 C 422.673 489.769 430.753 473.979 438.943 458.249 C 433.303 455.989 427.823 453.379 422.253 450.969 C 417.103 448.969 410.993 447.499 405.723 449.769 M 444.903 462.209 C 429.983 492.229 411.743 520.599 399.013 551.689 C 402.663 553.099 406.323 554.489 410.003 555.859 C 425.213 526.849 440.563 497.899 456.023 469.019 C 452.323 466.739 448.623 464.459 444.903 462.209 M 178.223 466.099 C 191.563 496.969 209.003 525.779 224.683 555.469 C 228.933 554.359 233.173 553.249 237.423 552.139 C 222.553 522.089 206.343 492.739 190.843 463.009 C 186.633 464.029 182.423 465.059 178.223 466.099 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 234.203 320.169 C 253.063 318.749 272.053 318.679 290.893 320.499 C 287.103 323.639 282.443 325.689 277.503 326.039 C 267.843 326.839 258.123 326.549 248.453 326.229 C 243.113 326.109 237.943 323.989 234.203 320.169 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 344.613 320.389 C 363.223 318.729 381.963 318.769 400.593 320.129 C 396.773 324.369 391.133 326.319 385.513 326.289 C 376.213 326.499 366.903 326.779 357.623 326.139 C 352.733 325.879 348.133 323.749 344.613 320.389 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 247.853 347.059 C 255.993 337.739 274.653 338.879 279.123 351.399 C 272.733 361.829 256.863 362.969 248.113 354.999 C 245.823 352.989 245.843 349.249 247.853 347.059 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 360.933 344.029 C 365.683 341.089 371.633 340.429 377.073 341.309 C 382.853 342.119 388.003 346.079 389.633 351.779 C 383.073 361.259 368.973 362.419 359.693 356.499 C 355.923 353.219 356.753 346.619 360.933 344.029 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 298.463 390.529 C 303.933 387.169 309.833 383.639 316.503 383.959 C 327.983 384.119 337.943 390.769 346.773 397.449 C 365.293 412.809 381.893 430.849 393.793 451.869 C 388.843 461.249 384.033 470.719 378.683 479.889 C 369.763 495.399 361.093 511.329 356.663 528.779 C 332.173 540.199 302.823 540.149 278.303 528.839 C 273.713 511.979 265.553 496.429 256.883 481.359 C 251.233 471.639 246.073 461.639 240.823 451.699 C 255.543 427.709 274.033 405.129 298.463 390.529 M 310.003 430.329 C 295.753 433.049 282.913 442.809 276.953 456.099 C 273.643 463.299 273.063 471.489 273.963 479.279 C 277.963 503.119 304.253 520.699 327.793 514.839 C 340.503 511.489 351.913 502.939 358.153 491.299 C 363.253 481.889 363.353 470.549 360.783 460.379 C 354.493 439.599 331.083 426.479 310.003 430.329 Z"/>
<path fill="#555" stroke="#555" stroke-width="5" opacity="1.00" d="M 310.003 430.329 C 331.083 426.479 354.493 439.599 360.783 460.379 C 363.353 470.549 363.253 481.889 358.153 491.299 C 351.913 502.939 340.503 511.489 327.793 514.839 C 304.253 520.699 277.963 503.119 273.963 479.279 C 273.063 471.489 273.643 463.299 276.953 456.099 C 282.913 442.809 295.753 433.049 310.003 430.329 M 312.873 452.229 C 307.023 454.129 300.883 457.529 298.453 463.509 C 296.103 469.269 296.403 475.869 298.443 481.679 C 300.213 486.889 304.903 490.749 310.113 492.249 C 318.233 494.889 327.833 493.169 334.083 487.219 C 338.033 483.119 339.353 477.169 339.703 471.679 C 339.173 458.859 325.063 449.339 312.873 452.229 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 196.013 458.459 C 205.363 454.389 214.573 449.559 224.793 448.149 C 230.193 450.549 234.463 454.879 237.323 459.999 C 244.923 473.189 251.843 486.749 259.043 500.159 C 263.033 507.649 266.983 515.199 270.133 523.079 C 271.103 529.249 266.833 534.869 261.793 537.919 C 256.043 541.339 249.953 544.159 244.073 547.339 C 226.933 518.329 211.563 488.339 196.013 458.459 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 405.723 449.769 C 410.993 447.499 417.103 448.969 422.253 450.969 C 427.823 453.379 433.303 455.989 438.943 458.249 C 430.753 473.979 422.673 489.769 414.383 505.449 C 406.643 519.519 398.893 533.579 391.163 547.659 C 384.843 544.479 378.343 541.619 372.303 537.949 C 366.873 534.619 362.693 527.749 365.253 521.379 C 371.533 505.709 380.673 491.429 388.213 476.369 C 393.213 467.059 397.523 456.799 405.723 449.769 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 312.873 452.229 C 325.063 449.339 339.173 458.859 339.703 471.679 C 339.353 477.169 338.033 483.119 334.083 487.219 C 327.833 493.169 318.233 494.889 310.113 492.249 C 304.903 490.749 300.213 486.889 298.443 481.679 C 296.403 475.869 296.103 469.269 298.453 463.509 C 300.883 457.529 307.023 454.129 312.873 452.229 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 444.903 462.209 C 448.623 464.459 452.323 466.739 456.023 469.019 C 440.563 497.899 425.213 526.849 410.003 555.859 C 406.323 554.489 402.663 553.099 399.013 551.689 C 411.743 520.599 429.983 492.229 444.903 462.209 Z"/>
<path fill="#8f3f97" stroke="#8f3f97" stroke-width="5" opacity="1.00" d="M 178.223 466.099 C 182.423 465.059 186.633 464.029 190.843 463.009 C 206.343 492.739 222.553 522.089 237.423 552.139 C 233.173 553.249 228.933 554.359 224.683 555.469 C 209.003 525.779 191.563 496.969 178.223 466.099 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB