## Supported Things
-There are five supported things.
+There are six supported things.
### OpenWeatherMap Account
It requires coordinates of the location of your interest.
You can add as much `uvindex` things for different locations to your setup as you like to observe.
+### Current And Forecasted Air Pollution
+
+Another thing is the `air-pollution` which provides the [current air pollution](https://openweathermap.org/api/air-pollution) and [forecasted air pollution](https://openweathermap.org/api/air-pollution#forecast) for a specific location.
+It requires coordinates of the location of your interest.
+Air pollution forecast is available for 5 days with hourly granularity.
+You can add as much `air-pollution` things for different locations to your setup as you like to observe.
+
### One Call API Weather and Forecast
The thing `onecall` supports the [current and forecast weather data](https://openweathermap.org/api/one-call-api#how) for a specific location using the One Call API.
### Current UV Index And Forecast
+| Parameter | Description |
+|----------------|----------------------------------------------------------------------------------------------------------------------------------|
+| location | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory** |
+| forecastDays | Number of days for UV Index forecast (including todays forecast). Optional, the default value is 6 (min="1", max="8", step="1"). |
+
+Once the parameter `forecastDays` will be changed, the available channel groups on the thing will be created or removed accordingly.
+
+### Current Air Pollution And Forecast
+
| Parameter | Description |
|----------------|--------------------------------------------------------------------------------------------------------------------------------|
| location | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory** |
-| forecastDays | Number of days for UV Index forecast (including todays forecast). Optional, the default value is 6 (min="1", max="8", step="1"). |
+| forecastHours | Number of hours for air pollution forecast. Optional, the default value is 0 (min="0", max="120", step="1"). |
-Once the parameter `forecastDays` will be changed, the available channel groups on the thing will be created or removed accordingly.
+Once the parameter `forecastHours` will be changed, the available channel groups on the thing will be created or removed accordingly.
### One Call API Weather and Forecast
| Channel Group ID | Channel ID | Item Type | Description |
|------------------------------------------------------------------|----------------------|----------------------|-----------------------------------------------------------------------------------|
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp | DateTime | Date of data forecasted. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunrise | DateTime | Time of sunrise for the given day. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | sunset | DateTime | Time of sunset for the given day. |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition | String | Forecast weather condition. |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id | String | Id of the forecasted weather condition. **Advanced** |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon | Image | Icon representing the forecasted weather condition. |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow | Number:Length | Expected snow volume of a day. |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | dew-point | Number:Temperature | Expected dew-point. Only available in the One Call API |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | uvindex | Number | Forecasted Midday UV Index. Only available in the One Call API |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | precip-probability | Number:Dimensionless | Precipitation probability. |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | morning-temperature | Number:Temperature | Expected morning temperature. Only available in the One Call API |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | day-temperature | Number:Temperature | Expected day-temperature. Only available in the One Call API |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | evening-temperature | Number:Temperature | Expected evening-temperature. Only available in the One Call API |
The `uvindex` channel is also available in the current data and the daily forecast of the One Call API.
+### Air Pollution
+
+| Channel Group ID | Channel ID | Item Type | Description |
+|-----------------------------------------------------------------|------------------------|----------------|--------------------------------------------------------------------------|
+| current, forecastHours01, forecastHours02, ... forecastHours120 | time-stamp | DateTime | Date of data observation / forecast. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | airQualityIndex | Number | Current or forecasted air quality index. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | particulateMatter2dot5 | Number:Density | Current or forecasted density of particles less than 2.5 µm in diameter. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | particulateMatter10 | Number:Density | Current or forecasted density of particles less than 10 µm in diameter. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | carbonMonoxide | Number:Density | Current or forecasted concentration of carbon monoxide. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | nitrogenMonoxide | Number:Density | Current or forecasted concentration of nitrogen monoxide. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | nitrogenDioxide | Number:Density | Current or forecasted concentration of nitrogen dioxide. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | ozone | Number:Density | Current or forecasted concentration of ozone. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | sulphurDioxide | Number:Density | Current or forecasted concentration of sulphur dioxide. |
+| current, forecastHours01, forecastHours02, ... forecastHours120 | ammonia | Number:Density | Current or forecasted concentration of ammonia. |
+
## Full Example
### Things
public static final ThingTypeUID THING_TYPE_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID,
"weather-and-forecast");
public static final ThingTypeUID THING_TYPE_UVINDEX = new ThingTypeUID(BINDING_ID, "uvindex");
+ public static final ThingTypeUID THING_TYPE_AIR_POLLUTION = new ThingTypeUID(BINDING_ID, "air-pollution");
// One Call API forecast
public static final ThingTypeUID THING_TYPE_ONECALL_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID, "onecall");
// One Call API historical data
"dailyForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_UVINDEX_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
"uvindexForecast");
+ public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_AIR_POLLUTION_FORECAST = new ChannelGroupTypeUID(
+ BINDING_ID, "airPollutionForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_MINUTELY_FORECAST = new ChannelGroupTypeUID(
BINDING_ID, "oneCallMinutely");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_HOURLY_FORECAST = new ChannelGroupTypeUID(
public static final String CHANNEL_GROUP_FORECAST_TODAY = "forecastToday";
public static final String CHANNEL_GROUP_FORECAST_TOMORROW = "forecastTomorrow";
public static final String CHANNEL_GROUP_CURRENT_UVINDEX = "current";
+ public static final String CHANNEL_GROUP_CURRENT_AIR_POLLUTION = "current";
public static final String CHANNEL_GROUP_ONECALL_CURRENT = "current";
public static final String CHANNEL_GROUP_ONECALL_HISTORY = "history";
public static final String CHANNEL_GROUP_ONECALL_TODAY = "forecastToday";
public static final String CHANNEL_SNOW = "snow";
public static final String CHANNEL_VISIBILITY = "visibility";
public static final String CHANNEL_UVINDEX = "uvindex";
+ public static final String CHANNEL_AIR_QUALITY_INDEX = "airQualityIndex";
+ public static final String CHANNEL_PARTICULATE_MATTER_2_5 = "particulateMatter2dot5";
+ public static final String CHANNEL_PARTICULATE_MATTER_10 = "particulateMatter10";
+ public static final String CHANNEL_CARBON_MONOXIDE = "carbonMonoxide";
+ public static final String CHANNEL_NITROGEN_MONOXIDE = "nitrogenMonoxide";
+ public static final String CHANNEL_NITROGEN_DIOXIDE = "nitrogenDioxide";
+ public static final String CHANNEL_OZONE = "ozone";
+ public static final String CHANNEL_SULPHUR_DIOXIDE = "sulphurDioxide";
+ public static final String CHANNEL_AMMONIA = "ammonia";
public static final String CHANNEL_PRECIPITATION = "precipitation";
// List of all configuration
--- /dev/null
+/**
+ * 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.openweathermap.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler;
+
+/**
+ * The {@link OpenWeatherMapAirPollutionConfiguration} is the class used to match the
+ * {@link OpenWeatherMapAirPollutionHandler}s configuration.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWeatherMapAirPollutionConfiguration extends OpenWeatherMapLocationConfiguration {
+ public int forecastHours;
+}
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
+import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonAirPollutionData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonDailyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonUVIndexData;
// UV Index (see https://openweathermap.org/api/uvi)
private static final String UVINDEX_URL = "https://api.openweathermap.org/data/2.5/uvi";
private static final String UVINDEX_FORECAST_URL = "https://api.openweathermap.org/data/2.5/uvi/forecast";
+ // Air Pollution (see https://openweathermap.org/api/air-pollution)
+ private static final String AIR_POLLUTION_URL = "https://api.openweathermap.org/data/2.5/air_pollution";
+ private static final String AIR_POLLUTION_FORECAST_URL = "https://api.openweathermap.org/data/2.5/air_pollution/forecast";
// Weather icons (see https://openweathermap.org/weather-conditions)
private static final String ICON_URL = "https://openweathermap.org/img/w/%s.png";
// One Call API (see https://openweathermap.org/api/one-call-api )
}
/**
- * Requests the UV Index data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
+ * Requests the UV Index data for the given location (see https://openweathermap.org/api/uvi).
*
* @param location location represented as {@link PointType}
* @return the UV Index data
}
/**
- * Requests the UV Index forecast data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
+ * Requests the UV Index forecast data for the given location (see https://openweathermap.org/api/uvi).
*
* @param location location represented as {@link PointType}
* @return the UV Index forecast data
OpenWeatherMapJsonUVIndexData[].class));
}
+ /**
+ * Requests the Air Pollution data for the given location (see https://openweathermap.org/api/air-pollution).
+ *
+ * @param location location represented as {@link PointType}
+ * @return the Air Pollution data
+ * @throws JsonSyntaxException
+ * @throws OpenWeatherMapCommunicationException
+ * @throws OpenWeatherMapConfigurationException
+ */
+ public synchronized @Nullable OpenWeatherMapJsonAirPollutionData getAirPollutionData(@Nullable PointType location)
+ throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+ return gson.fromJson(
+ getResponseFromCache(
+ buildURL(AIR_POLLUTION_URL, getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
+ OpenWeatherMapJsonAirPollutionData.class);
+ }
+
+ /**
+ * Requests the Air Pollution forecast data for the given location (see
+ * https://openweathermap.org/api/air-pollution).
+ *
+ * @param location location represented as {@link PointType}
+ * @return the Air Pollution forecast data
+ * @throws JsonSyntaxException
+ * @throws OpenWeatherMapCommunicationException
+ * @throws OpenWeatherMapConfigurationException
+ */
+ public synchronized @Nullable OpenWeatherMapJsonAirPollutionData getAirPollutionForecastData(
+ @Nullable PointType location)
+ throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+ return gson.fromJson(
+ getResponseFromCache(buildURL(AIR_POLLUTION_FORECAST_URL,
+ getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
+ OpenWeatherMapJsonAirPollutionData.class);
+ }
+
/**
* Downloads the icon for the given icon id (see https://openweathermap.org/weather-conditions).
*
}
Map<String, String> params = new HashMap<>();
- // API key (see http://openweathermap.org/appid)
+ // API key (see https://openweathermap.org/appid)
String apikey = config.apikey;
if (apikey == null || (apikey = apikey.trim()).isEmpty()) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-apikey");
String locationString = location.toFullString();
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
createWeatherAndForecastResult(locationString, bridgeUID);
+ createAirPollutionResult(locationString, bridgeUID);
createOneCallResult(locationString, bridgeUID);
createOneCallHistoryResult(locationString, bridgeUID);
}
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}
+ private void createAirPollutionResult(String location, ThingUID bridgeUID) {
+ thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_AIR_POLLUTION, bridgeUID, LOCAL))
+ .withLabel("Local Air Pollution").withProperty(CONFIG_LOCATION, location)
+ .withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
+ }
+
private void createOneCallResult(String location, ThingUID bridgeUID) {
thingDiscovered(
DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ONECALL_WEATHER_AND_FORECAST, bridgeUID, LOCAL))
--- /dev/null
+/**
+ * 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.openweathermap.internal.dto;
+
+import org.openhab.binding.openweathermap.internal.dto.base.Coord;
+
+/**
+ * The {@link OpenWeatherMapJsonAirPollutionData} is the Java class used to map the JSON response to an OpenWeatherMap
+ * request.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class OpenWeatherMapJsonAirPollutionData {
+ public Coord coord;
+ public java.util.List<org.openhab.binding.openweathermap.internal.dto.airpollution.List> list;
+}
--- /dev/null
+/**
+ * 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link Components} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class Components {
+ @SerializedName("co")
+ public double carbonMonoxide;
+ @SerializedName("no")
+ public double nitrogenMonoxide;
+ @SerializedName("no2")
+ public double nitrogenDioxide;
+ @SerializedName("o2")
+ public double ozone;
+ @SerializedName("so2")
+ public double sulphurDioxide;
+ @SerializedName("pm2_5")
+ public double particulateMatter2dot5;
+ @SerializedName("pm10")
+ public double particulateMatter10;
+ @SerializedName("nh3")
+ public double ammonia;
+}
--- /dev/null
+/**
+ * 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link List} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class List {
+ public int dt;
+ @SerializedName("main")
+ public Main airQualityIndex;
+ @SerializedName("components")
+ public Components measurements;
+}
--- /dev/null
+/**
+ * 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.openweathermap.internal.dto.airpollution;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Generated Plain Old Java Objects class for {@link Main} from JSON.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+public class Main {
+ @SerializedName("aqi")
+ public int index;
+}
*/
public class List {
private Integer dt;
+ private @Nullable Integer sunrise;
+ private @Nullable Integer sunset;
private Temp temp;
@SerializedName("feels_like")
private @Nullable FeelsLikeTemp feelsLikeTemp;
private Integer clouds;
private @Nullable Double rain;
private @Nullable Double snow;
+ private @Nullable Double pop;
public Integer getDt() {
return dt;
this.dt = dt;
}
+ public @Nullable Integer getSunrise() {
+ return sunrise;
+ }
+
+ public void setSunrise(Integer sunrise) {
+ this.sunrise = sunrise;
+ }
+
+ public @Nullable Integer getSunset() {
+ return sunset;
+ }
+
+ public void setSunset(Integer sunset) {
+ this.sunset = sunset;
+ }
+
public Temp getTemp() {
return temp;
}
public void setSnow(Double snow) {
this.snow = snow;
}
+
+ public @Nullable Double getPop() {
+ return pop;
+ }
+
+ public void setPop(Double pop) {
+ this.pop = pop;
+ }
}
import java.util.Collections;
import java.util.HashMap;
-import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.openweathermap.internal.discovery.OpenWeatherMapDiscoveryService;
-import org.openhab.binding.openweathermap.internal.handler.*;
+import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHistoryHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
+import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
// register discovery service
OpenWeatherMapDiscoveryService discoveryService = new OpenWeatherMapDiscoveryService(handler,
locationProvider, localeProvider, i18nProvider);
- discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext
- .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
+ discoveryServiceRegs.put(handler.getThing().getUID(),
+ bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, null));
return handler;
} else if (THING_TYPE_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
return new OpenWeatherMapWeatherAndForecastHandler(thing, timeZoneProvider);
} else if (THING_TYPE_UVINDEX.equals(thingTypeUID)) {
return new OpenWeatherMapUVIndexHandler(thing, timeZoneProvider);
+ } else if (THING_TYPE_AIR_POLLUTION.equals(thingTypeUID)) {
+ return new OpenWeatherMapAirPollutionHandler(thing, timeZoneProvider);
} else if (THING_TYPE_ONECALL_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
return new OpenWeatherMapOneCallHandler(thing, timeZoneProvider);
} else if (THING_TYPE_ONECALL_HISTORY.equals(thingTypeUID)) {
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import javax.measure.Unit;
private final Logger logger = LoggerFactory.getLogger(AbstractOpenWeatherMapHandler.class);
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
- .unmodifiableSet(Stream.of(THING_TYPE_WEATHER_AND_FORECAST, THING_TYPE_UVINDEX,
- THING_TYPE_ONECALL_WEATHER_AND_FORECAST, THING_TYPE_ONECALL_HISTORY).collect(Collectors.toSet()));
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_WEATHER_AND_FORECAST,
+ THING_TYPE_UVINDEX, THING_TYPE_AIR_POLLUTION, THING_TYPE_ONECALL_WEATHER_AND_FORECAST,
+ THING_TYPE_ONECALL_HISTORY);
private final TimeZoneProvider timeZoneProvider;
import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
-import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.util.ThingHandlerHelper;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAPIHandler.class);
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_WEATHER_API);
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_WEATHER_API);
private static final long INITIAL_DELAY_IN_SECONDS = 15;
config = getConfigAs(OpenWeatherMapAPIConfiguration.class);
boolean configValid = true;
- if (config.apikey == null || config.apikey.trim().isEmpty()) {
+ if (config.apikey.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-missing-apikey");
configValid = false;
}
private ThingStatus updateThing(@Nullable AbstractOpenWeatherMapHandler handler, Thing thing) {
- if (handler != null && connection != null) {
+ if (handler != null && ThingHandlerHelper.isHandlerInitialized(handler) && connection != null) {
handler.updateData(connection);
return thing.getStatus();
} else {
--- /dev/null
+/**
+ * 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.openweathermap.internal.handler;
+
+import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAirPollutionConfiguration;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
+import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
+import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonAirPollutionData;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.unit.Units;
+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.builder.ThingBuilder;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link OpenWeatherMapAirPollutionHandler} is responsible for handling commands, which are sent to one of the
+ * channels.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWeatherMapAirPollutionHandler extends AbstractOpenWeatherMapHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAirPollutionHandler.class);
+
+ private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
+ private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
+ .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
+
+ // keeps track of the parsed count
+ private int forecastHours = 0;
+
+ private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionData;
+ private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionForecastData;
+
+ public OpenWeatherMapAirPollutionHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
+ super(thing, timeZoneProvider);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+ logger.debug("Initialize OpenWeatherMapAirPollutionHandler handler '{}'.", getThing().getUID());
+ OpenWeatherMapAirPollutionConfiguration config = getConfigAs(OpenWeatherMapAirPollutionConfiguration.class);
+
+ boolean configValid = true;
+ int newForecastHours = config.forecastHours;
+ if (newForecastHours < 0 || newForecastHours > 120) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.conf-error-not-supported-air-pollution-number-of-hours");
+ configValid = false;
+ }
+
+ if (configValid) {
+ logger.debug("Rebuilding thing '{}'.", getThing().getUID());
+ List<Channel> toBeAddedChannels = new ArrayList<>();
+ List<Channel> toBeRemovedChannels = new ArrayList<>();
+ if (forecastHours != newForecastHours) {
+ logger.debug("Rebuilding air pollution channel groups.");
+ if (forecastHours > newForecastHours) {
+ for (int i = newForecastHours + 1; i <= forecastHours; i++) {
+ toBeRemovedChannels.addAll(removeChannelsOfGroup(
+ CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
+ }
+ } else {
+ for (int i = forecastHours + 1; i <= newForecastHours; i++) {
+ toBeAddedChannels.addAll(createChannelsForGroup(
+ CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
+ CHANNEL_GROUP_TYPE_AIR_POLLUTION_FORECAST));
+ }
+ }
+ forecastHours = newForecastHours;
+ }
+ ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
+ for (Channel channel : toBeAddedChannels) {
+ builder.withChannel(channel);
+ }
+ updateThing(builder.build());
+ }
+ }
+
+ @Override
+ protected boolean requestData(OpenWeatherMapConnection connection)
+ throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
+ logger.debug("Update air pollution data of thing '{}'.", getThing().getUID());
+ try {
+ airPollutionData = connection.getAirPollutionData(location);
+ if (forecastHours > 0) {
+ airPollutionForecastData = connection.getAirPollutionForecastData(location);
+ }
+ return true;
+ } catch (JsonSyntaxException e) {
+ logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void updateChannel(ChannelUID channelUID) {
+ switch (channelUID.getGroupId()) {
+ case CHANNEL_GROUP_CURRENT_AIR_POLLUTION:
+ updateCurrentAirPollutionChannel(channelUID);
+ break;
+ default:
+ Matcher m = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelUID.getGroupId());
+ int i;
+ if (m.find() && (i = Integer.parseInt(m.group(1))) > 0 && i <= 120) {
+ updateHourlyForecastChannel(channelUID, i);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Update the channel from the last OpenWeatherMap data retrieved.
+ *
+ * @param channelUID the id identifying the channel to be updated
+ */
+ private void updateCurrentAirPollutionChannel(ChannelUID channelUID) {
+ String channelId = channelUID.getIdWithoutGroup();
+ String channelGroupId = channelUID.getGroupId();
+ OpenWeatherMapJsonAirPollutionData localAirPollutionData = airPollutionData;
+ if (localAirPollutionData != null && !localAirPollutionData.list.isEmpty()) {
+ org.openhab.binding.openweathermap.internal.dto.airpollution.List currentData = localAirPollutionData.list
+ .get(0);
+ State state = UnDefType.UNDEF;
+ switch (channelId) {
+ case CHANNEL_TIME_STAMP:
+ state = getDateTimeTypeState(currentData.dt);
+ break;
+ case CHANNEL_AIR_QUALITY_INDEX:
+ state = new DecimalType(currentData.airQualityIndex.index);
+ break;
+ case CHANNEL_PARTICULATE_MATTER_2_5:
+ state = getQuantityTypeState(currentData.measurements.particulateMatter2dot5,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_PARTICULATE_MATTER_10:
+ state = getQuantityTypeState(currentData.measurements.particulateMatter10,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_CARBON_MONOXIDE:
+ state = getQuantityTypeState(currentData.measurements.carbonMonoxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_NITROGEN_MONOXIDE:
+ state = getQuantityTypeState(currentData.measurements.nitrogenMonoxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_NITROGEN_DIOXIDE:
+ state = getQuantityTypeState(currentData.measurements.nitrogenDioxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_OZONE:
+ state = getQuantityTypeState(currentData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_SULPHUR_DIOXIDE:
+ state = getQuantityTypeState(currentData.measurements.sulphurDioxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_AMMONIA:
+ state = getQuantityTypeState(currentData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ }
+ logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
+ updateState(channelUID, state);
+ } else {
+ logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
+ channelGroupId);
+ }
+ }
+
+ /**
+ * Update the channel from the last OpenWeatherMap data retrieved.
+ *
+ * @param channelUID the id identifying the channel to be updated
+ * @param count
+ */
+ private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
+ String channelId = channelUID.getIdWithoutGroup();
+ String channelGroupId = channelUID.getGroupId();
+ OpenWeatherMapJsonAirPollutionData localAirPollutionForecastData = airPollutionForecastData;
+ if (localAirPollutionForecastData != null && localAirPollutionForecastData.list.size() >= count) {
+ org.openhab.binding.openweathermap.internal.dto.airpollution.List forecastData = localAirPollutionForecastData.list
+ .get(count - 1);
+ State state = UnDefType.UNDEF;
+ switch (channelId) {
+ case CHANNEL_TIME_STAMP:
+ state = getDateTimeTypeState(forecastData.dt);
+ break;
+ case CHANNEL_AIR_QUALITY_INDEX:
+ state = new DecimalType(forecastData.airQualityIndex.index);
+ break;
+ case CHANNEL_PARTICULATE_MATTER_2_5:
+ state = getQuantityTypeState(forecastData.measurements.particulateMatter2dot5,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_PARTICULATE_MATTER_10:
+ state = getQuantityTypeState(forecastData.measurements.particulateMatter10,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_CARBON_MONOXIDE:
+ state = getQuantityTypeState(forecastData.measurements.carbonMonoxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_NITROGEN_MONOXIDE:
+ state = getQuantityTypeState(forecastData.measurements.nitrogenMonoxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_NITROGEN_DIOXIDE:
+ state = getQuantityTypeState(forecastData.measurements.nitrogenDioxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_OZONE:
+ state = getQuantityTypeState(forecastData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_SULPHUR_DIOXIDE:
+ state = getQuantityTypeState(forecastData.measurements.sulphurDioxide,
+ Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ case CHANNEL_AMMONIA:
+ state = getQuantityTypeState(forecastData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
+ break;
+ }
+ logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
+ updateState(channelUID, state);
+ } else {
+ logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
+ channelGroupId);
+ }
+ }
+}
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(forecastData.getDt());
break;
+ case CHANNEL_SUNRISE:
+ state = getDateTimeTypeState(forecastData.getSunrise());
+ break;
+ case CHANNEL_SUNSET:
+ state = getDateTimeTypeState(forecastData.getSunset());
+ break;
case CHANNEL_CONDITION:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
Double snow = forecastData.getSnow();
state = getQuantityTypeState(snow == null ? 0 : snow, MILLI(METRE));
break;
+ case CHANNEL_PRECIP_PROBABILITY:
+ Double probability = forecastData.getPop();
+ state = getQuantityTypeState(probability == null ? 0 : probability * 100.0, PERCENT);
+ break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
</parameter>
</config-description>
+ <config-description uri="thing-type:openweathermap:air-pollution">
+ <parameter name="location" type="text" required="true">
+ <context>location</context>
+ <label>Location of Weather</label>
+ <description>Location of weather in geographical coordinates (latitude/longitude/altitude).</description>
+ </parameter>
+ <parameter name="forecastHours" type="integer" min="0" max="120" step="1">
+ <label>Number of Hours</label>
+ <description>Number of hours for air pollution forecast.</description>
+ <default>0</default>
+ </parameter>
+ </config-description>
+
<config-description uri="thing-type:openweathermap:onecall">
<parameter name="location" type="text" required="true">
<context>location</context>
thing-type.openweathermap.uvindex.label = UV-Index
thing-type.openweathermap.uvindex.description = Ermöglicht die Anzeige des aktuellen UV-Index.
+thing-type.openweathermap.air-pollution.label = Luftqualität
+thing-type.openweathermap.air-pollution.description = Ermöglicht die Anzeige der aktuellen Luftqualität.
+
# thing types config
thing-type.config.openweathermap.weather-and-forecast.location.label = Ort der Wetterdaten
thing-type.config.openweathermap.weather-and-forecast.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
thing-type.config.openweathermap.uvindex.forecastDays.label = Tage
thing-type.config.openweathermap.uvindex.forecastDays.description = Anzahl der Tage für die UV-Index Vorhersage.
+thing-type.config.openweathermap.air-pollution.location.label = Ort der Wetterdaten
+thing-type.config.openweathermap.air-pollution.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
+
+thing-type.config.openweathermap.air-pollution.forecastHours.label = Stunden
+thing-type.config.openweathermap.air-pollution.forecastHours.description = Anzahl der Stunden für die Vorhersage der Luftqualität.
+
# channel group types
channel-group-type.openweathermap.station.label = Wetterstation
channel-group-type.openweathermap.station.description = Fasst Daten über die Wetterstation oder den Ort zusammen.
channel-group-type.openweathermap.uvindexForecast.label = UV-Index Vorhersage
channel-group-type.openweathermap.uvindexForecast.description = Fasst Daten der UV-Index Vorhersage zusammen.
+channel-group-type.openweathermap.airPollution.label = Aktuelle Luftqualität
+channel-group-type.openweathermap.airPollution.description = Fasst Daten über die aktuelle Luftqualität zusammen.
+
+channel-group-type.openweathermap.airPollutionForecast.label = Vorhersage der Luftqualität
+channel-group-type.openweathermap.airPollutionForecast.description = Fasst Daten über die vorhergesagte Luftqualität zusammen.
+
# channel groups
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.label = Wettervorhersage für 3 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.description = Fasst Daten der Wettervorhersage in den nächsten drei Stunden zusammen.
channel-type.openweathermap.daily-forecast-time-stamp.description = Zeigt das Datum der Vorhersage an.
channel-type.openweathermap.daily-forecast-time-stamp.state.pattern = %1$td.%1$tm.%1$tY
+channel-type.openweathermap.sunrise.label = Sonnenaufgang
+channel-type.openweathermap.sunrise.description = Zeigt den Zeitpunkt des Sonnenaufgangs an.
+channel-type.openweathermap.sunrise.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
+
+channel-type.openweathermap.sunset.label = Sonnenuntergang
+channel-type.openweathermap.sunset.description = Zeigt den Zeitpunkt des Sonnenuntergangs an.
+channel-type.openweathermap.sunset.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
+
channel-type.openweathermap.condition.label = Wetterlage
channel-type.openweathermap.condition.description = Zeigt die aktuelle Wetterlage an.
channel-type.openweathermap.forecasted-snow.label = Vorhergesagter Schnee
channel-type.openweathermap.forecasted-snow.description = Zeigt die vorhergesagte Schneemenge an.
+channel-type.openweathermap.precip-probability.label = Vorhergesagte Niederschlagswahrscheinlichkeit
+channel-type.openweathermap.precip-probability.description = Zeigt die vorhergesagte Niederschlagswahrscheinlichkeit an.
+
channel-type.openweathermap.uvindex.label = UV-Index
channel-type.openweathermap.uvindex.description = Zeigt den aktuellen UV-Index an.
channel-type.openweathermap.forecasted-uvindex.label = Vorhergesagter UV-Index
channel-type.openweathermap.forecasted-uvindex.description = Zeigt den vorhergesagten UV-Index an.
+channel-type.openweathermap.air-quality-index.label = Luftqualitätsindex
+channel-type.openweathermap.air-quality-index.description = Zeigt den aktuellen Luftqualitätsindex an.
+channel-type.openweathermap.air-quality-index.state.option.1 = Gut
+channel-type.openweathermap.air-quality-index.state.option.2 = Befriedigend
+channel-type.openweathermap.air-quality-index.state.option.3 = Moderat
+channel-type.openweathermap.air-quality-index.state.option.4 = Ungesund
+channel-type.openweathermap.air-quality-index.state.option.5 = Äußerst Ungesund
+
+channel-type.openweathermap.forecasted-air-quality-index.label = Vorhergesagter Luftqualitätsindex
+channel-type.openweathermap.forecasted-air-quality-index.description = Zeigt den vorhergesagten Luftqualitätsindex an.
+channel-type.openweathermap.forecasted-air-quality-index.state.option.1 = Gut
+channel-type.openweathermap.forecasted-air-quality-index.state.option.2 = Befriedigend
+channel-type.openweathermap.forecasted-air-quality-index.state.option.3 = Moderat
+channel-type.openweathermap.forecasted-air-quality-index.state.option.4 = Ungesund
+channel-type.openweathermap.forecasted-air-quality-index.state.option.5 = Äußerst Ungesund
+
+channel-type.openweathermap.particulate-matter-2dot5.label = Feinstaub - PM2.5
+channel-type.openweathermap.particulate-matter-2dot5.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 2,5 µm.
+
+channel-type.openweathermap.forecasted-particulate-matter-2dot5.label = Vorhergesagter Feinstaub - PM2.5
+channel-type.openweathermap.forecasted-particulate-matter-2dot5.description = Vorhergesagte Dichte von Teilchen mit Partikelgröße unter 2,5 µm.
+
+channel-type.openweathermap.particulate-matter-10.label = Feinstaub - PM10
+channel-type.openweathermap.particulate-matter-10.description = Aktuelle Dichte von Teilchen mit Partikelgröße unter 10 µm.
+
+channel-type.openweathermap.forecasted-particulate-matter-10.label = Vorhergesagter Feinstaub - PM10
+channel-type.openweathermap.forecasted-particulate-matter-10.description = Vorhergesagte Dichte von Teilchen mit Partikelgröße unter 10 µm.
+
+channel-type.openweathermap.carbon-monoxide.label = Kohlenmonoxid
+channel-type.openweathermap.carbon-monoxide.description = Aktuelle Konzentration an Kohlenmonoxid.
+
+channel-type.openweathermap.forecasted-carbon-monoxide.label = Vorhergesagter Kohlenmonoxid
+channel-type.openweathermap.forecasted-carbon-monoxide.description = Vorhergesagte Konzentration an Kohlenmonoxid.
+
+channel-type.openweathermap.nitrogen-monoxide.label = Stickstoffmonoxid
+channel-type.openweathermap.nitrogen-monoxide.description = Aktuelle Konzentration an Stickstoffmonoxid.
+
+channel-type.openweathermap.forecasted-nitrogen-monoxide.label = Vorhergesagter Stickstoffmonoxid
+channel-type.openweathermap.forecasted-nitrogen-monoxide.description = Vorhergesagte Konzentration an Stickstoffmonoxid.
+
+channel-type.openweathermap.nitrogen-dioxide.label = Stickstoffdioxid
+channel-type.openweathermap.nitrogen-dioxide.description = Aktuelle Konzentration an Stickstoffdioxid.
+
+channel-type.openweathermap.forecasted-nitrogen-dioxide.label = Vorhergesagter Stickstoffdioxid
+channel-type.openweathermap.forecasted-nitrogen-dioxide.description = Vorhergesagte Konzentration an Stickstoffdioxid.
+
+channel-type.openweathermap.ozone.label = Ozon
+channel-type.openweathermap.ozone.description = Aktuelle Konzentration an Ozon.
+
+channel-type.openweathermap.forecasted-ozone.label = Vorhergesagter Ozon
+channel-type.openweathermap.forecasted-ozone.description = Vorhergesagte Konzentration an Ozon.
+
+channel-type.openweathermap.sulphur-dioxide.label = Schwefeldioxid
+channel-type.openweathermap.sulphur-dioxide.description = Aktuelle Konzentration an Schwefeldioxid.
+
+channel-type.openweathermap.forecasted-sulphur-dioxide.label = Vorhergesagter Schwefeldioxid
+channel-type.openweathermap.forecasted-sulphur-dioxide.description = Vorhergesagte Konzentration an Schwefeldioxid.
+
+channel-type.openweathermap.ammonia.label = Ammoniak
+channel-type.openweathermap.ammonia.description = Aktuelle Konzentration an Ammoniak.
+
+channel-type.openweathermap.forecasted-ammonia.label = Vorhergesagter Ammoniak
+channel-type.openweathermap.forecasted-ammonia.description = Vorhergesagte Konzentration an Ammoniak.
+
# thing status
offline.conf-error-missing-apikey = Der Parameter 'API Schlüssel' muss konfiguriert werden.
offline.conf-error-invalid-apikey = Ungültiger 'API Schlüssel'. Mehr Infos unter https://openweathermap.org/faq#error401.
offline.conf-error-not-supported-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 120 liegen - Schrittweite: 3.
offline.conf-error-not-supported-number-of-days = Der Parameter 'forecastDays' muss zwischen 0 und 16 liegen.
offline.conf-error-not-supported-uvindex-number-of-days = Der Parameter 'forecastDays' muss zwischen 1 und 8 liegen.
+offline.conf-error-not-supported-air-pollution-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 120 liegen - Schrittweite: 1.
offline.conf-error-not-supported-onecall-number-of-minutes = Der Parameter 'forecastMinutes' muss zwischen 0 and 60 liegen.
offline.conf-error-not-supported-onecall-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 48 liegen.
# discovery result
discovery.openweathermap.weather-and-forecast.api.local.label = Lokales Wetter und Wettervorhersage
discovery.openweathermap.uvindex.api.local.label = Lokaler UV-Index
+discovery.openweathermap.air-pollution.api.local.label = Lokale Luftqualität
<channel id="cloudiness" typeId="forecasted-cloudiness"/>
<channel id="rain" typeId="forecasted-rain"/>
<channel id="snow" typeId="forecasted-snow"/>
+ <channel id="precip-probability" typeId="precip-probability"/>
+ <channel id="sunrise" typeId="sunrise"/>
+ <channel id="sunset" typeId="sunset"/>
</channels>
</channel-group-type>
</channels>
</channel-group-type>
+ <channel-group-type id="airPollution">
+ <label>Current Air Pollution</label>
+ <description>This is the current air pollution.</description>
+ <channels>
+ <channel id="time-stamp" typeId="time-stamp"/>
+ <channel id="airQualityIndex" typeId="air-quality-index"/>
+ <channel id="particulateMatter2dot5" typeId="particulate-matter-2dot5"/>
+ <channel id="particulateMatter10" typeId="particulate-matter-10"/>
+ <channel id="carbonMonoxide" typeId="carbon-monoxide"/>
+ <channel id="nitrogenMonoxide" typeId="nitrogen-monoxide"/>
+ <channel id="nitrogenDioxide" typeId="nitrogen-dioxide"/>
+ <channel id="ozone" typeId="ozone"/>
+ <channel id="sulphurDioxide" typeId="sulphur-dioxide"/>
+ <channel id="ammonia" typeId="ammonia"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-group-type id="airPollutionForecast">
+ <label>Forecasted Air Pollution</label>
+ <description>This is the forecasted air pollution.</description>
+ <channels>
+ <channel id="time-stamp" typeId="hourly-forecast-time-stamp"/>
+ <channel id="airQualityIndex" typeId="forecasted-air-quality-index"/>
+ <channel id="particulateMatter2dot5" typeId="forecasted-particulate-matter-2dot5"/>
+ <channel id="particulateMatter10" typeId="forecasted-particulate-matter-10"/>
+ <channel id="carbonMonoxide" typeId="forecasted-carbon-monoxide"/>
+ <channel id="nitrogenMonoxide" typeId="forecasted-nitrogen-monoxide"/>
+ <channel id="nitrogenDioxide" typeId="forecasted-nitrogen-dioxide"/>
+ <channel id="ozone" typeId="forecasted-ozone"/>
+ <channel id="sulphurDioxide" typeId="forecasted-sulphur-dioxide"/>
+ <channel id="ammonia" typeId="forecasted-ammonia"/>
+ </channels>
+ </channel-group-type>
+
<channel-group-type id="oneCallCurrent">
<label>One Call API Current Weather</label>
<description>Current weather data from the One Call API.</description>
<state readOnly="true" pattern="%.1f"/>
</channel-type>
+ <channel-type id="air-quality-index">
+ <item-type>Number</item-type>
+ <label>Air Quality Index</label>
+ <description>Current air quality index.</description>
+ <state readOnly="true" pattern="%.0f">
+ <options>
+ <option value="1">Good</option>
+ <option value="2">Fair</option>
+ <option value="3">Moderate</option>
+ <option value="4">Poor</option>
+ <option value="5">Very Poor</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="forecasted-air-quality-index">
+ <item-type>Number</item-type>
+ <label>Forecasted Air Quality Index</label>
+ <description>Forecasted air quality index.</description>
+ <state readOnly="true" pattern="%.0f">
+ <options>
+ <option value="1">Good</option>
+ <option value="2">Fair</option>
+ <option value="3">Moderate</option>
+ <option value="4">Poor</option>
+ <option value="5">Very Poor</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="particulate-matter-2dot5">
+ <item-type>Number:Density</item-type>
+ <label>Particulate Matter - PM2.5</label>
+ <description>Current density of particles less than 2.5 µm in diameter.</description>
+ <state readOnly="true" pattern="%.2f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-particulate-matter-2dot5">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Particulate Matter - PM2.5</label>
+ <description>Forecasted density of particles less than 2.5 µm in diameter.</description>
+ <state readOnly="true" pattern="%.2f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-particulate-matter-10">
+ <item-type>Number:Density</item-type>
+ <label>Particulate Matter - PM10</label>
+ <description>Current density of particles less than 10 µm in diameter.</description>
+ <state readOnly="true" pattern="%.2f %unit%"/>
+ </channel-type>
+
+ <channel-type id="particulate-matter-10">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Particulate Matter - PM10</label>
+ <description>Forecasted density of particles less than 10 µm in diameter.</description>
+ <state readOnly="true" pattern="%.2f %unit%"/>
+ </channel-type>
+
+ <channel-type id="carbon-monoxide">
+ <item-type>Number:Density</item-type>
+ <label>Carbon Monoxide</label>
+ <description>Current concentration of carbon monoxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-carbon-monoxide">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Carbon Monoxide</label>
+ <description>Forecasted concentration of carbon monoxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="nitrogen-monoxide">
+ <item-type>Number:Density</item-type>
+ <label>Nitrogen Monoxide</label>
+ <description>Current concentration of nitrogen monoxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-nitrogen-monoxide">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Nitrogen Monoxide</label>
+ <description>Forecasted concentration of nitrogen monoxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="nitrogen-dioxide">
+ <item-type>Number:Density</item-type>
+ <label>Nitrogen Dioxide</label>
+ <description>Current concentration of nitrogen dioxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-nitrogen-dioxide">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Nitrogen Dioxide</label>
+ <description>Forecasted concentration of nitrogen dioxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="ozone">
+ <item-type>Number:Density</item-type>
+ <label>Ozone</label>
+ <description>Current concentration of ozone.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-ozone">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Ozone</label>
+ <description>Forecasted concentration of ozone.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="sulphur-dioxide">
+ <item-type>Number:Density</item-type>
+ <label>Sulphur Dioxide</label>
+ <description>Current concentration of sulphur dioxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-sulphur-dioxide">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Sulphur Dioxide</label>
+ <description>Forecasted concentration of sulphur dioxide.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="ammonia">
+ <item-type>Number:Density</item-type>
+ <label>Ammonia</label>
+ <description>Current concentration of ammonia.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
+ <channel-type id="forecasted-ammonia">
+ <item-type>Number:Density</item-type>
+ <label>Forecasted Ammonia</label>
+ <description>Forecasted concentration of ammonia.</description>
+ <state readOnly="true" pattern="%.1f %unit%"/>
+ </channel-type>
+
<channel-type id="visibility">
<item-type>Number:Length</item-type>
<label>Visibility</label>
<channel-type id="precip-probability">
<item-type>Number:Dimensionless</item-type>
- <label>Probability</label>
+ <label>Precipitation Probability</label>
<description>Forecasted precipitation probability.</description>
+ <category>Rain</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
-
</thing:thing-descriptions>
<config-description-ref uri="thing-type:openweathermap:uvindex"/>
</thing-type>
+ <thing-type id="air-pollution">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="weather-api"/>
+ </supported-bridge-type-refs>
+
+ <label>Air Pollution</label>
+ <description>Provides air pollution data from the OpenWeatherMap API.</description>
+
+ <channel-groups>
+ <channel-group id="current" typeId="airPollution"/>
+ </channel-groups>
+
+ <representation-property>location</representation-property>
+
+ <config-description-ref uri="thing-type:openweathermap:air-pollution"/>
+ </thing-type>
+
<thing-type id="onecall">
<supported-bridge-type-refs>
<bridge-type-ref id="weather-api"/>