Adding Air Quality index
Signed-off-by: Gaël L'hopital <gael@lhopital.org>
@ -47,6 +47,7 @@ public class AirParifBindingConstants {
|
||||
public static final String CHANNEL_TOMORROW = "tomorrow";
|
||||
public static final String CHANNEL_TIMESTAMP = "timestamp";
|
||||
public static final String CHANNEL_VALUE = "value";
|
||||
public static final String CHANNEL_ALERT = "alert";
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(APIBRIDGE_THING_TYPE,
|
||||
LOCATION_THING_TYPE);
|
||||
|
@ -41,14 +41,12 @@ import org.osgi.service.component.annotations.Reference;
|
||||
public class AirParifHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final AirParifDeserializer deserializer;
|
||||
private final HttpClient httpClient;
|
||||
private final AirParifIconProvider iconProvider;
|
||||
|
||||
@Activate
|
||||
public AirParifHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference AirParifDeserializer deserializer, final @Reference AirParifIconProvider iconProvider) {
|
||||
final @Reference AirParifDeserializer deserializer) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.deserializer = deserializer;
|
||||
this.iconProvider = iconProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,7 +59,7 @@ public class AirParifHandlerFactory extends BaseThingHandlerFactory {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
return APIBRIDGE_THING_TYPE.equals(thingTypeUID)
|
||||
? new AirParifBridgeHandler((Bridge) thing, httpClient, deserializer, iconProvider)
|
||||
? new AirParifBridgeHandler((Bridge) thing, httpClient, deserializer)
|
||||
: LOCATION_THING_TYPE.equals(thingTypeUID) ? new LocationHandler(thing) : null;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.util.Set;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
|
||||
import org.openhab.binding.airparif.internal.api.ColorMap;
|
||||
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.ui.icon.IconProvider;
|
||||
@ -50,15 +49,14 @@ import org.slf4j.LoggerFactory;
|
||||
public class AirParifIconProvider implements IconProvider {
|
||||
private static final String NEUTRAL_COLOR = "#3d3c3c";
|
||||
private static final String DEFAULT_LABEL = "Air Parif Icons";
|
||||
private static final String AQ_ICON = "aq";
|
||||
private static final String DEFAULT_DESCRIPTION = "Icons illustrating air quality levels provided by AirParif";
|
||||
private static final List<String> ICONS = List.of("average", "bad", "degrated", "extremely-bad", "good", "pollen");
|
||||
private static final List<String> POLLEN_ICONS = Pollen.AS_SET.stream().map(Pollen::name).map(String::toLowerCase)
|
||||
.toList();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AirParifIconProvider.class);
|
||||
private final TranslationProvider i18nProvider;
|
||||
private final Bundle bundle;
|
||||
private @Nullable ColorMap colorMap;
|
||||
|
||||
@Activate
|
||||
public AirParifIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
|
||||
@ -87,24 +85,31 @@ public class AirParifIconProvider implements IconProvider {
|
||||
@Override
|
||||
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
|
||||
return Format.SVG.equals(format) && iconSetId.equals(BINDING_ID)
|
||||
&& (ICONS.contains(category) || POLLEN_ICONS.contains(category)) ? 0 : null;
|
||||
&& (category.equals(AQ_ICON) || POLLEN_ICONS.contains(category)) ? 0 : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) {
|
||||
URL iconResource = bundle.getEntry("icon/%s.svg".formatted(category));
|
||||
int ordinal = -1;
|
||||
try {
|
||||
ordinal = state != null ? Integer.valueOf(state) : -1;
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
|
||||
String iconName = "icon/%s.svg".formatted(category);
|
||||
if (category.equals(AQ_ICON) && ordinal != -1) {
|
||||
iconName = iconName.replace(".", "-%d.".formatted(ordinal));
|
||||
}
|
||||
|
||||
URL iconResource = bundle.getEntry(iconName);
|
||||
|
||||
String result;
|
||||
try (InputStream stream = iconResource.openStream()) {
|
||||
result = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
|
||||
if (POLLEN_ICONS.contains(category) && state != null) {
|
||||
try {
|
||||
int ordinal = Integer.valueOf(state);
|
||||
PollenAlertLevel alertLevel = PollenAlertLevel.valueOf(ordinal);
|
||||
result = result.replaceAll(NEUTRAL_COLOR, alertLevel.color);
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
if (POLLEN_ICONS.contains(category)) {
|
||||
PollenAlertLevel alertLevel = PollenAlertLevel.valueOf(ordinal);
|
||||
result = result.replaceAll(NEUTRAL_COLOR, alertLevel.color);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("Unable to load ressource '{}': {}", iconResource.getPath(), e.getMessage());
|
||||
@ -113,8 +118,4 @@ public class AirParifIconProvider implements IconProvider {
|
||||
|
||||
return result.isEmpty() ? null : new ByteArrayInputStream(result.getBytes());
|
||||
}
|
||||
|
||||
public void setColorMap(ColorMap map) {
|
||||
this.colorMap = map;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class AirParifApi {
|
||||
AVERAGE("Moyen"),
|
||||
DEGRATED("Dégradé"),
|
||||
BAD("Mauvais"),
|
||||
REALLY_BAD("Très Mauvais"),
|
||||
VERY_BAD("Très Mauvais"),
|
||||
EXTREMELY_BAD("Extrêmement Mauvais"),
|
||||
UNKNOWN("");
|
||||
|
||||
|
@ -27,10 +27,13 @@ import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Scope;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
@ -63,6 +66,22 @@ public class AirParifDto {
|
||||
Pollutant pollutant, //
|
||||
int min, //
|
||||
int max) {
|
||||
|
||||
private State getQuantity(int value) {
|
||||
Unit<?> unit = pollutant.unit;
|
||||
if (unit != null) {
|
||||
return new QuantityType<>(value, unit);
|
||||
}
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
public State getMin() {
|
||||
return getQuantity(min);
|
||||
}
|
||||
|
||||
public State getMax() {
|
||||
return getQuantity(max);
|
||||
}
|
||||
}
|
||||
|
||||
public record PollutantEpisode(//
|
||||
@ -185,9 +204,18 @@ public class AirParifDto {
|
||||
return message != null ? new StringType(message.fr()) : UnDefType.NULL;
|
||||
}
|
||||
|
||||
public State getQuantity() {
|
||||
Unit<?> unit = pollutant.unit;
|
||||
return unit != null ? new QuantityType<>(getValue(), unit) : UnDefType.NULL;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
public int getAlertLevel() {
|
||||
return pollutant.getAppreciation(getValue()).ordinal();
|
||||
}
|
||||
}
|
||||
|
||||
public record Route(//
|
||||
|
@ -17,6 +17,8 @@ import java.util.EnumSet;
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Appreciation;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
@ -28,29 +30,37 @@ import com.google.gson.annotations.SerializedName;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum Pollutant {
|
||||
// Concentration thresholds per pollutant are available here:
|
||||
// https://www.airparif.fr/sites/default/files/pdf/guide_calcul_nouvel_indice_fedeAtmo_14122020.pdf
|
||||
|
||||
@SerializedName("pm25")
|
||||
PM25(Units.MICROGRAM_PER_CUBICMETRE),
|
||||
PM25(Units.MICROGRAM_PER_CUBICMETRE, new int[] { 10, 20, 25, 50, 75 }),
|
||||
|
||||
@SerializedName("pm10")
|
||||
PM10(Units.MICROGRAM_PER_CUBICMETRE),
|
||||
PM10(Units.MICROGRAM_PER_CUBICMETRE, new int[] { 20, 40, 50, 100, 150 }),
|
||||
|
||||
@SerializedName("no2")
|
||||
NO2(Units.PARTS_PER_BILLION),
|
||||
NO2(Units.MICROGRAM_PER_CUBICMETRE, new int[] { 40, 90, 120, 230, 340 }),
|
||||
|
||||
@SerializedName("o3")
|
||||
O3(Units.PARTS_PER_BILLION),
|
||||
O3(Units.MICROGRAM_PER_CUBICMETRE, new int[] { 50, 100, 130, 240, 380 }),
|
||||
|
||||
@SerializedName("so2")
|
||||
SO2(Units.MICROGRAM_PER_CUBICMETRE, new int[] { 100, 200, 350, 500, 750 }),
|
||||
|
||||
@SerializedName("indice")
|
||||
INDICE(Units.PERCENT),
|
||||
INDICE(null, new int[] {}),
|
||||
|
||||
UNKNOWN(Units.PERCENT);
|
||||
UNKNOWN(null, new int[] {});
|
||||
|
||||
public static final EnumSet<Pollutant> AS_SET = EnumSet.allOf(Pollutant.class);
|
||||
|
||||
public final Unit<?> unit;
|
||||
public final @Nullable Unit<?> unit;
|
||||
private final int[] thresholds;
|
||||
|
||||
Pollutant(Unit<?> unit) {
|
||||
Pollutant(@Nullable Unit<?> unit, int[] thresholds) {
|
||||
this.unit = unit;
|
||||
this.thresholds = thresholds;
|
||||
}
|
||||
|
||||
public static Pollutant safeValueOf(String searched) {
|
||||
@ -60,4 +70,21 @@ public enum Pollutant {
|
||||
return Pollutant.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasUnit() {
|
||||
return unit != null;
|
||||
}
|
||||
|
||||
public Appreciation getAppreciation(double concentration) {
|
||||
if (thresholds.length == 0) {
|
||||
return Appreciation.UNKNOWN;
|
||||
}
|
||||
|
||||
for (int i = 0; i < thresholds.length; i++) {
|
||||
if (concentration <= thresholds[i]) {
|
||||
return Appreciation.values()[i];
|
||||
}
|
||||
}
|
||||
return Appreciation.EXTREMELY_BAD;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpStatus.Code;
|
||||
import org.openhab.binding.airparif.internal.AirParifException;
|
||||
import org.openhab.binding.airparif.internal.AirParifIconProvider;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Bulletin;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Episode;
|
||||
@ -57,13 +56,11 @@ import org.openhab.binding.airparif.internal.api.AirParifDto.KeyInfo;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.PollensResponse;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Route;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Version;
|
||||
import org.openhab.binding.airparif.internal.api.ColorMap;
|
||||
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
|
||||
import org.openhab.binding.airparif.internal.api.Pollutant;
|
||||
import org.openhab.binding.airparif.internal.config.BridgeConfiguration;
|
||||
import org.openhab.binding.airparif.internal.deserialization.AirParifDeserializer;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
@ -97,17 +94,14 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
||||
private final Logger logger = LoggerFactory.getLogger(AirParifBridgeHandler.class);
|
||||
private final Map<String, ScheduledFuture<?>> jobs = new HashMap<>();
|
||||
private final AirParifDeserializer deserializer;
|
||||
private final AirParifIconProvider iconProvider;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private BridgeConfiguration config = new BridgeConfiguration();
|
||||
private @Nullable PollensResponse pollens;
|
||||
|
||||
public AirParifBridgeHandler(Bridge bridge, HttpClient httpClient, AirParifDeserializer deserializer,
|
||||
AirParifIconProvider iconProvider) {
|
||||
public AirParifBridgeHandler(Bridge bridge, HttpClient httpClient, AirParifDeserializer deserializer) {
|
||||
super(bridge);
|
||||
this.deserializer = deserializer;
|
||||
this.iconProvider = iconProvider;
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@ -202,14 +196,6 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
||||
logger.info("The api key is valid until {}", keyInfo.expiration().toString());
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
try {
|
||||
ColorMap map = executeUri(PREV_COLORS_URI, ColorMap.class);
|
||||
logger.debug("The color map is {}", map.toString());
|
||||
iconProvider.setColorMap(map);
|
||||
} catch (AirParifException e) {
|
||||
logger.warn("Error reading ColorMap: {}", e.getMessage());
|
||||
}
|
||||
|
||||
ThingUID thingUID = thing.getUID();
|
||||
|
||||
schedule(POLLENS_JOB, () -> updatePollens(new ChannelGroupUID(thingUID, GROUP_POLLENS)), Duration.ofSeconds(1));
|
||||
@ -257,15 +243,15 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
||||
Set.of(bulletin.today(), bulletin.tomorrow()).stream().forEach(aq -> {
|
||||
ChannelGroupUID groupUID = aq.isToday() ? todayGroupUID : tomorrowGroupUID;
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_COMMENT),
|
||||
!aq.available() ? UnDefType.UNDEF : new StringType(aq.bulletin().fr()));
|
||||
!aq.available() ? UnDefType.NULL : new StringType(aq.bulletin().fr()));
|
||||
|
||||
aq.concentrations().forEach(measure -> {
|
||||
Pollutant pollutant = measure.pollutant();
|
||||
String cName = pollutant.name().toLowerCase() + "-";
|
||||
updateState(new ChannelUID(groupUID, cName + "min"),
|
||||
aq.available() ? new QuantityType<>(measure.min(), pollutant.unit) : UnDefType.UNDEF);
|
||||
aq.available() ? measure.getMin() : UnDefType.NULL);
|
||||
updateState(new ChannelUID(groupUID, cName + "max"),
|
||||
aq.available() ? new QuantityType<>(measure.max(), pollutant.unit) : UnDefType.UNDEF);
|
||||
aq.available() ? measure.getMax() : UnDefType.NULL);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -15,6 +15,7 @@ package org.openhab.binding.airparif.internal.handler;
|
||||
import static org.openhab.binding.airparif.internal.AirParifBindingConstants.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -23,13 +24,14 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifApi.Pollen;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Concentration;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.PollensResponse;
|
||||
import org.openhab.binding.airparif.internal.api.AirParifDto.Route;
|
||||
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
|
||||
import org.openhab.binding.airparif.internal.api.Pollutant;
|
||||
import org.openhab.binding.airparif.internal.config.LocationConfiguration;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@ -100,14 +102,22 @@ public class LocationHandler extends BaseThingHandler implements HandlerUtils {
|
||||
|
||||
Route route = apiHandler.getConcentrations(local.location);
|
||||
if (route != null) {
|
||||
route.concentrations().forEach(concentration -> {
|
||||
ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(),
|
||||
concentration.pollutant().name().toLowerCase());
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_TIMESTAMP), new DateTimeType(concentration.date()));
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_MESSAGE), concentration.getMessage());
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_VALUE),
|
||||
new QuantityType<>(concentration.getValue(), concentration.pollutant().unit));
|
||||
int maxAlert = route.concentrations().stream().filter(conc -> conc.pollutant().hasUnit())
|
||||
.map(Concentration::getAlertLevel).max(Comparator.comparing(Integer::valueOf)).get();
|
||||
|
||||
route.concentrations().stream().forEach(concentration -> {
|
||||
Pollutant pollutant = concentration.pollutant();
|
||||
ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), pollutant.name().toLowerCase());
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_MESSAGE), concentration.getMessage());
|
||||
if (!pollutant.hasUnit()) {
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_TIMESTAMP),
|
||||
new DateTimeType(concentration.date()));
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_ALERT), new DecimalType(maxAlert));
|
||||
} else {
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_VALUE), concentration.getQuantity());
|
||||
updateState(new ChannelUID(groupUID, CHANNEL_ALERT),
|
||||
new DecimalType(concentration.getAlertLevel()));
|
||||
}
|
||||
});
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
@ -67,26 +67,19 @@ channel-group-type.airparif.daily.channel.tomorrow.label = Tomorrow
|
||||
channel-group-type.airparif.daily.channel.tomorrow.description = Current bulletin validity start
|
||||
channel-group-type.airparif.dept-pollens.label = Pollen information for the department
|
||||
channel-group-type.airparif.pollutant-mpc.label = Pollutant Concentration Information
|
||||
channel-group-type.airparif.pollutant-mpc.channel.alert.label = Alert Level
|
||||
channel-group-type.airparif.pollutant-mpc.channel.alert.description = Alert Level associated to pollutant concentration
|
||||
channel-group-type.airparif.pollutant-mpc.channel.message.label = Message
|
||||
channel-group-type.airparif.pollutant-mpc.channel.message.description = Polllutant concentration alert message
|
||||
channel-group-type.airparif.pollutant-mpc.channel.timestamp.label = Timestamp
|
||||
channel-group-type.airparif.pollutant-mpc.channel.timestamp.description = Timestamp of the measure
|
||||
channel-group-type.airparif.pollutant-mpc.channel.value.label = Concentration
|
||||
channel-group-type.airparif.pollutant-mpc.channel.value.description = Concentration of the given pollutant
|
||||
channel-group-type.airparif.pollutant-ndx.label = Global Pollutant Index
|
||||
channel-group-type.airparif.pollutant-ndx.channel.alert.label = Alert Level
|
||||
channel-group-type.airparif.pollutant-ndx.channel.alert.description = Alert Level associated to highest pollutant concentration
|
||||
channel-group-type.airparif.pollutant-ndx.channel.message.label = Message
|
||||
channel-group-type.airparif.pollutant-ndx.channel.message.description = Alert message associated to the value of the index
|
||||
channel-group-type.airparif.pollutant-ndx.channel.timestamp.label = Timestamp
|
||||
channel-group-type.airparif.pollutant-ndx.channel.timestamp.description = Timestamp of the evaluation
|
||||
channel-group-type.airparif.pollutant-ndx.channel.value.label = Value
|
||||
channel-group-type.airparif.pollutant-ndx.channel.value.description = Value of the global Index
|
||||
channel-group-type.airparif.pollutant-ppb.label = Pollutant Concentration Information
|
||||
channel-group-type.airparif.pollutant-ppb.channel.message.label = Message
|
||||
channel-group-type.airparif.pollutant-ppb.channel.message.description = Polllutant concentration alert message
|
||||
channel-group-type.airparif.pollutant-ppb.channel.timestamp.label = Timestamp
|
||||
channel-group-type.airparif.pollutant-ppb.channel.timestamp.description = Timestamp of the measure
|
||||
channel-group-type.airparif.pollutant-ppb.channel.value.label = Concentration
|
||||
channel-group-type.airparif.pollutant-ppb.channel.value.description = Concentration of the given pollutant
|
||||
|
||||
# channel types
|
||||
|
||||
@ -95,6 +88,13 @@ channel-type.airparif.alder-level.state.option.0 = None
|
||||
channel-type.airparif.alder-level.state.option.1 = Low
|
||||
channel-type.airparif.alder-level.state.option.2 = Average
|
||||
channel-type.airparif.alder-level.state.option.3 = High
|
||||
channel-type.airparif.appreciation.label = Air Quality
|
||||
channel-type.airparif.appreciation.state.option.0 = Good
|
||||
channel-type.airparif.appreciation.state.option.1 = Average
|
||||
channel-type.airparif.appreciation.state.option.2 = Degrated
|
||||
channel-type.airparif.appreciation.state.option.3 = Bad
|
||||
channel-type.airparif.appreciation.state.option.4 = Very Bad
|
||||
channel-type.airparif.appreciation.state.option.5 = Extremely Bad
|
||||
channel-type.airparif.ash-level.label = Ash
|
||||
channel-type.airparif.ash-level.state.option.0 = None
|
||||
channel-type.airparif.ash-level.state.option.1 = Low
|
||||
@ -163,7 +163,6 @@ channel-type.airparif.poplar-level.state.option.0 = None
|
||||
channel-type.airparif.poplar-level.state.option.1 = Low
|
||||
channel-type.airparif.poplar-level.state.option.2 = Average
|
||||
channel-type.airparif.poplar-level.state.option.3 = High
|
||||
channel-type.airparif.ppb-value.label = Measure
|
||||
channel-type.airparif.ragweed-level.label = Ragweed
|
||||
channel-type.airparif.ragweed-level.state.option.0 = None
|
||||
channel-type.airparif.ragweed-level.state.option.1 = Low
|
||||
@ -191,6 +190,24 @@ channel-type.airparif.wormwood-level.state.option.1 = Low
|
||||
channel-type.airparif.wormwood-level.state.option.2 = Average
|
||||
channel-type.airparif.wormwood-level.state.option.3 = High
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.airparif.pollutant-mpc.channel.timestamp.label = Timestamp
|
||||
channel-group-type.airparif.pollutant-mpc.channel.timestamp.description = Timestamp of the measure
|
||||
channel-group-type.airparif.pollutant-ndx.channel.value.label = Value
|
||||
channel-group-type.airparif.pollutant-ndx.channel.value.description = Value of the global Index
|
||||
channel-group-type.airparif.pollutant-ppb.label = Pollutant Concentration Information
|
||||
channel-group-type.airparif.pollutant-ppb.channel.message.label = Message
|
||||
channel-group-type.airparif.pollutant-ppb.channel.message.description = Polllutant concentration alert message
|
||||
channel-group-type.airparif.pollutant-ppb.channel.timestamp.label = Timestamp
|
||||
channel-group-type.airparif.pollutant-ppb.channel.timestamp.description = Timestamp of the measure
|
||||
channel-group-type.airparif.pollutant-ppb.channel.value.label = Concentration
|
||||
channel-group-type.airparif.pollutant-ppb.channel.value.description = Concentration of the given pollutant
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.airparif.ppb-value.label = Measure
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.airparif.location.channel.end-validity.label = End Of Validity
|
||||
|
@ -29,19 +29,19 @@
|
||||
<label>Message</label>
|
||||
<description>General message for the air quality bulletin</description>
|
||||
</channel>
|
||||
<channel id="no2-min" typeId="ppb-value">
|
||||
<channel id="no2-min" typeId="mpc-value">
|
||||
<label>NO2 Min</label>
|
||||
<description>Minimum level of NO2 concentation</description>
|
||||
</channel>
|
||||
<channel id="no2-max" typeId="ppb-value">
|
||||
<channel id="no2-max" typeId="mpc-value">
|
||||
<label>NO2 Max</label>
|
||||
<description>Maximum level of NO2 concentation</description>
|
||||
</channel>
|
||||
<channel id="o3-min" typeId="ppb-value">
|
||||
<channel id="o3-min" typeId="mpc-value">
|
||||
<label>O3 Min</label>
|
||||
<description>Minimum level of O3 concentation</description>
|
||||
</channel>
|
||||
<channel id="o3-max" typeId="ppb-value">
|
||||
<channel id="o3-max" typeId="mpc-value">
|
||||
<label>O3 Max</label>
|
||||
<description>Maximum level of O3 concentation</description>
|
||||
</channel>
|
||||
@ -71,31 +71,13 @@
|
||||
<label>Message</label>
|
||||
<description>Polllutant concentration alert message</description>
|
||||
</channel>
|
||||
<channel id="timestamp" typeId="timestamp">
|
||||
<label>Timestamp</label>
|
||||
<description>Timestamp of the measure</description>
|
||||
</channel>
|
||||
<channel id="value" typeId="mpc-value">
|
||||
<label>Concentration</label>
|
||||
<description>Concentration of the given pollutant</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="pollutant-ppb">
|
||||
<label>Pollutant Concentration Information</label>
|
||||
<channels>
|
||||
<channel id="message" typeId="comment">
|
||||
<label>Message</label>
|
||||
<description>Polllutant concentration alert message</description>
|
||||
</channel>
|
||||
<channel id="timestamp" typeId="timestamp">
|
||||
<label>Timestamp</label>
|
||||
<description>Timestamp of the measure</description>
|
||||
</channel>
|
||||
<channel id="value" typeId="ppb-value">
|
||||
<label>Concentration</label>
|
||||
<description>Concentration of the given pollutant</description>
|
||||
<channel id="alert" typeId="appreciation">
|
||||
<label>Alert Level</label>
|
||||
<description>Alert Level associated to pollutant concentration</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
@ -111,9 +93,9 @@
|
||||
<label>Timestamp</label>
|
||||
<description>Timestamp of the evaluation</description>
|
||||
</channel>
|
||||
<channel id="value" typeId="ndx-value">
|
||||
<label>Value</label>
|
||||
<description>Value of the global Index</description>
|
||||
<channel id="alert" typeId="appreciation">
|
||||
<label>Alert Level</label>
|
||||
<description>Alert Level associated to highest pollutant concentration</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
@ -291,12 +291,6 @@
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ppb-value">
|
||||
<item-type unitHint="ppb">Number:Dimensionless</item-type>
|
||||
<label>Measure</label>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ndx-value">
|
||||
<item-type>Number</item-type>
|
||||
<label>Measure</label>
|
||||
@ -309,5 +303,21 @@
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="appreciation">
|
||||
<item-type>Number</item-type>
|
||||
<label>Air Quality</label>
|
||||
<category>oh:airparif:aq</category>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">Good</option>
|
||||
<option value="1">Average</option>
|
||||
<option value="2">Degrated</option>
|
||||
<option value="3">Bad</option>
|
||||
<option value="4">Very Bad</option>
|
||||
<option value="5">Extremely Bad</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
@ -15,10 +15,10 @@
|
||||
<channel-groups>
|
||||
<channel-group id="pollens" typeId="dept-pollens"/>
|
||||
<channel-group id="indice" typeId="pollutant-ndx"/>
|
||||
<channel-group id="o3" typeId="pollutant-ppb">
|
||||
<channel-group id="o3" typeId="pollutant-mpc">
|
||||
<label>Ozone Concentration Information</label>
|
||||
</channel-group>
|
||||
<channel-group id="no2" typeId="pollutant-ppb">
|
||||
<channel-group id="no2" typeId="pollutant-mpc">
|
||||
<label>NO2 Concentration Information</label>
|
||||
</channel-group>
|
||||
<channel-group id="pm25" typeId="pollutant-mpc">
|
||||
|
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/alder.svg
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
8
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-0.svg
Executable file
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke="#8bd1f5" stroke-miterlimit="10" stroke-width="14" />
|
||||
<path
|
||||
d="M47.45 65.74c-4.39 0-9.68-2.37-12.06-6.57-2.18-3.85 2.56-3.66 2.56-3.66h20.84s4.81-.25 2.56 3.66c-2.41 4.19-7.68 6.57-12.06 6.57h-1.83z"
|
||||
fill="none" stroke="#1e1e1c" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
|
||||
<circle cx="58.74" cy="41.74" r="3.57" class="prefix__prefix__cls-3" />
|
||||
<circle cx="36.27" cy="41.74" r="3.57" class="prefix__prefix__cls-3" />
|
||||
</svg>
|
After Width: | Height: | Size: 585 B |
7
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-1.svg
Executable file
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke="#65bba2" stroke-miterlimit="10" stroke-width="14" />
|
||||
<circle cx="59.23" cy="41.74" r="3.57" class="prefix__prefix__cls-2" />
|
||||
<circle cx="36.76" cy="41.74" r="3.57" class="prefix__prefix__cls-2" />
|
||||
<path d="M35.27 59.3l26.81-.05" fill="none" stroke="#1e1e1c" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="3" />
|
||||
</svg>
|
After Width: | Height: | Size: 467 B |
7
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-2.svg
Executable file
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke="#ffe00a" stroke-miterlimit="10" stroke-width="14" />
|
||||
<path d="M34.29 63.64s4.34-8.68 13.28-8.68 12.34 4.86 14.81 8.43" fill="none" stroke="#1e1e1c" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="3" />
|
||||
<circle cx="59.32" cy="41.74" r="3.57" class="prefix__cls-2" />
|
||||
<circle cx="36.85" cy="41.74" r="3.57" class="prefix__cls-2" />
|
||||
</svg>
|
After Width: | Height: | Size: 485 B |
9
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-3.svg
Executable file
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" stroke-miterlimit="10" fill="#fff" stroke="#ea5553" stroke-width="14" />
|
||||
<path d="M34.46 63.64s4.34-8.68 13.28-8.68 12.34 4.86 14.81 8.43" fill="none" stroke="#1e1e1c"
|
||||
stroke-linecap="round" stroke-width="3" stroke-linejoin="round" />
|
||||
<circle cx="59.48" cy="41.74" r="3.57" class="prefix__cls-3" />
|
||||
<circle cx="37.01" cy="41.74" r="3.57" class="prefix__cls-3" />
|
||||
<path d="M32.51 30.33l16.13 7.93 15.59-7.93" stroke-miterlimit="10" fill="none" stroke="#1e1e1c"
|
||||
stroke-linecap="round" stroke-width="3" />
|
||||
</svg>
|
After Width: | Height: | Size: 667 B |
9
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-4.svg
Executable file
@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 98.15 95" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke="#961638" stroke-miterlimit="10" stroke-width="14" />
|
||||
<path
|
||||
d="M41.99 35.85c3.181 4.84-5.864 10.379-8.17 6.64m18.58-6.64c-2.401 5.325 6.835 9.434 8.17 6.64m3.61 17.43s-2.68-3.27-7.14-3.27c-4.46 0-5.48 6.864-9.05 7.164-3.57.3-4.63-7.164-8.2-7.164-3.57 0-4.025 1.431-8.62 3.57"
|
||||
class="prefix__cls-1" />
|
||||
<path
|
||||
d="M67.67 17.15c-2.39 2.82-6.78 8.61-6.78 11.52 0 3.78 3.04 6.86 6.78 6.86s6.78-3.08 6.78-6.86c0-2.91-4.39-8.7-6.78-11.52z"
|
||||
fill="#fff" stroke="#1e1e1c" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
|
||||
</svg>
|
After Width: | Height: | Size: 678 B |
6
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq-5.svg
Executable file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke="#7b2681" stroke-miterlimit="10" stroke-width="14" />
|
||||
<path
|
||||
d="M63.81 60.51l-4.15-4.06-4.15 3.99-4.07-4.23-3.83 4.23h-.01l-4.15-4.07-4.15 3.99-4.07-4.23-3.83 4.23m3.96-27.64l8.17 5.62-8.17 6.64m24.75-12.26l-8.17 5.62 8.17 6.64"
|
||||
class="prefix__cls-1" />
|
||||
</svg>
|
After Width: | Height: | Size: 394 B |
7
bundles/org.openhab.binding.airparif/src/main/resources/icon/aq.svg
Executable file
@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 98.15 95" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" fill="#fff" stroke-miterlimit="10" stroke-width="14" stroke="#adadad" />
|
||||
<circle cx="59.23" cy="41.74" r="3.57" class="prefix__prefix__prefix__prefix__cls-2" />
|
||||
<circle cx="36.76" cy="41.74" r="3.57" class="prefix__prefix__prefix__prefix__cls-2" />
|
||||
<path d="M35.27 59.3l26.81-.05" fill="none" stroke="#1e1e1c" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="3" />
|
||||
</svg>
|
After Width: | Height: | Size: 499 B |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/ash.svg
Normal file → Executable file
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" style="fill:#fff;stroke:#65bba2;stroke-miterlimit:10;stroke-width:14px"/>
|
||||
<circle cx="59.23" cy="41.74" r="3.57" class="cls-2"/>
|
||||
<circle cx="36.76" cy="41.74" r="3.57" class="cls-2"/>
|
||||
<path d="m35.27 59.3 26.81-.05"
|
||||
style="fill:none;stroke:#1e1e1c;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 452 B |
@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" style="stroke-miterlimit:10;fill:#fff;stroke:#ea5553;stroke-width:14px"/>
|
||||
<path d="M34.46 63.64s4.34-8.68 13.28-8.68 12.34 4.86 14.81 8.43"
|
||||
style="fill:none;stroke:#1e1e1c;stroke-linecap:round;stroke-width:3px;stroke-linejoin:round"/>
|
||||
<circle cx="59.48" cy="41.74" r="3.57" class="cls-3"/>
|
||||
<circle cx="37.01" cy="41.74" r="3.57" class="cls-3"/>
|
||||
<path d="m32.51 30.33 16.13 7.93 15.59-7.93"
|
||||
style="stroke-miterlimit:10;fill:none;stroke:#1e1e1c;stroke-linecap:round;stroke-width:3px"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 635 B |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/birch.svg
Normal file → Executable file
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/chestnut.svg
Normal file → Executable file
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/cypress.svg
Normal file → Executable file
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" style="fill:#fff;stroke:#ffe00a;stroke-miterlimit:10;stroke-width:14px"/>
|
||||
<path d="M34.29 63.64s4.34-8.68 13.28-8.68 12.34 4.86 14.81 8.43"
|
||||
style="fill:none;stroke:#1e1e1c;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px"/>
|
||||
<circle cx="59.32" cy="41.74" r="3.57" class="cls-2"/>
|
||||
<circle cx="36.85" cy="41.74" r="3.57" class="cls-2"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 486 B |
@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" style="fill:#fff;stroke:#7b2681;stroke-miterlimit:10;stroke-width:14px"/>
|
||||
<path
|
||||
d="m63.81 60.51-4.15-4.06-4.15 3.99-4.07-4.23-3.83 4.23h-.01l-4.15-4.07-4.15 3.99-4.07-4.23-3.83 4.23M35.36 32.72l8.17 5.62-8.17 6.64M60.11 32.72l-8.17 5.62 8.17 6.64"
|
||||
class="cls-1"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 401 B |
@ -1,8 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 98.15 95">
|
||||
<circle cx="47.85" cy="48.04" r="34.72" style="fill:#fff;stroke:#8bd1f5;stroke-miterlimit:10;stroke-width:14px"/>
|
||||
<path
|
||||
d="M47.45 65.74c-4.39 0-9.68-2.37-12.06-6.57-2.18-3.85 2.56-3.66 2.56-3.66h20.84s4.81-.25 2.56 3.66c-2.41 4.19-7.68 6.57-12.06 6.57h-1.83Z"
|
||||
style="fill:none;stroke:#1e1e1c;stroke-linecap:round;stroke-linejoin:round;stroke-width:3px"/>
|
||||
<circle cx="58.74" cy="41.74" r="3.57" class="cls-3"/>
|
||||
<circle cx="36.27" cy="41.74" r="3.57" class="cls-3"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 566 B |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/grasses.svg
Normal file → Executable file
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/hazel.svg
Normal file → Executable file
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/hornbeam.svg
Normal file → Executable file
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/linden.svg
Normal file → Executable file
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/oak.svg
Normal file → Executable file
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/olive.svg
Normal file → Executable file
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/plane.svg
Normal file → Executable file
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/plantain.svg
Normal file → Executable file
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/pollen.svg
Normal file → Executable file
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/poplar.svg
Normal file → Executable file
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/ragweed.svg
Normal file → Executable file
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/rumex.svg
Normal file → Executable file
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/urticaceae.svg
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/willow.svg
Normal file → Executable file
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
0
bundles/org.openhab.binding.airparif/src/main/resources/icon/wormwood.svg
Normal file → Executable file
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |