2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
14 package org.openhab.binding.openweathermap.internal.handler;
16 import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
17 import static org.openhab.core.library.unit.MetricPrefix.HECTO;
18 import static org.openhab.core.library.unit.MetricPrefix.KILO;
19 import static org.openhab.core.library.unit.MetricPrefix.MILLI;
20 import static org.openhab.core.library.unit.SIUnits.*;
21 import static org.openhab.core.library.unit.Units.*;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapOneCallHistoryConfiguration;
29 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
30 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
31 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
32 import org.openhab.binding.openweathermap.internal.dto.onecallhist.*;
33 import org.openhab.binding.openweathermap.internal.dto.onecallhist.OpenWeatherMapOneCallHistAPIData;
34 import org.openhab.core.i18n.TimeZoneProvider;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.types.State;
40 import org.openhab.core.types.UnDefType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import com.google.gson.JsonSyntaxException;
47 * The {@link OpenWeatherMapOneCallHistoryHandler} is responsible for handling commands, which are sent to one of
50 * @author Wolfgang Klimt - Initial contribution
53 public class OpenWeatherMapOneCallHistoryHandler extends AbstractOpenWeatherMapHandler {
55 private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapOneCallHistoryHandler.class);
57 private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "historyHours";
58 private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
59 .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
61 private @Nullable OpenWeatherMapOneCallHistAPIData weatherData;
63 // the relative day in history.
66 public OpenWeatherMapOneCallHistoryHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
67 super(thing, timeZoneProvider);
71 public void initialize() {
73 OpenWeatherMapOneCallHistoryConfiguration config = getConfigAs(OpenWeatherMapOneCallHistoryConfiguration.class);
74 if (config.historyDay <= 0) {
75 logger.warn("historyDay value of {} is not supported", config.historyDay);
79 * As of now, only 5 days in history are supported by the one call API. As this may change in the future,
80 * we allow any value here and only log a warning if the value exceeds 5.
82 if (config.historyDay > 5) {
83 logger.warn("History configuration of {} days may cause errors. You have been warned :-)",
86 day = config.historyDay;
87 logger.debug("Initialize OpenWeatherMapOneCallHistoryHandler handler '{}' with historyDay {}.",
88 getThing().getUID(), day);
89 updateStatus(ThingStatus.ONLINE);
93 protected boolean requestData(OpenWeatherMapConnection connection)
94 throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
95 logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
97 weatherData = connection.getOneCallHistAPIData(location, day);
99 } catch (JsonSyntaxException e) {
100 logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
106 protected void updateChannel(ChannelUID channelUID) {
107 String channelGroupId = channelUID.getGroupId();
108 switch (channelGroupId) {
109 case CHANNEL_GROUP_ONECALL_HISTORY:
110 updateHistoryCurrentChannel(channelUID);
115 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
116 if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 1
118 updateHourlyHistoryChannel(channelUID, i - 1);
126 * Update the channel from the last OpenWeatherMap data retrieved.
128 * @param channelUID the id identifying the channel to be updated
130 private void updateHistoryCurrentChannel(ChannelUID channelUID) {
131 String channelId = channelUID.getIdWithoutGroup();
132 String channelGroupId = channelUID.getGroupId();
133 OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
134 if (localWeatherData != null) {
135 State state = UnDefType.UNDEF;
137 case CHANNEL_STATION_LOCATION:
138 state = getPointTypeState(localWeatherData.getLat(), localWeatherData.getLon());
140 case CHANNEL_TIME_STAMP:
141 state = getDateTimeTypeState(localWeatherData.getCurrent().getDt());
143 case CHANNEL_SUNRISE:
144 state = getDateTimeTypeState(localWeatherData.getCurrent().getSunrise());
147 state = getDateTimeTypeState(localWeatherData.getCurrent().getSunset());
149 case CHANNEL_CONDITION:
150 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
151 state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getDescription());
154 case CHANNEL_CONDITION_ID:
155 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
156 state = getStringTypeState(
157 Integer.toString(localWeatherData.getCurrent().getWeather().get(0).getId()));
160 case CHANNEL_CONDITION_ICON:
161 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
162 state = getRawTypeState(OpenWeatherMapConnection
163 .getWeatherIcon(localWeatherData.getCurrent().getWeather().get(0).getIcon()));
166 case CHANNEL_CONDITION_ICON_ID:
167 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
168 state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getIcon());
171 case CHANNEL_TEMPERATURE:
172 state = getQuantityTypeState(localWeatherData.getCurrent().getTemp(), CELSIUS);
174 case CHANNEL_APPARENT_TEMPERATURE:
175 state = getQuantityTypeState(localWeatherData.getCurrent().getFeelsLike(), CELSIUS);
177 case CHANNEL_PRESSURE:
178 state = getQuantityTypeState(localWeatherData.getCurrent().getPressure(), HECTO(PASCAL));
180 case CHANNEL_HUMIDITY:
181 state = getQuantityTypeState(localWeatherData.getCurrent().getHumidity(), PERCENT);
183 case CHANNEL_DEW_POINT:
184 state = getQuantityTypeState(localWeatherData.getCurrent().getDewPoint(), CELSIUS);
186 case CHANNEL_WIND_SPEED:
187 state = getQuantityTypeState(localWeatherData.getCurrent().getWindSpeed(), METRE_PER_SECOND);
189 case CHANNEL_WIND_DIRECTION:
190 state = getQuantityTypeState(localWeatherData.getCurrent().getWindDeg(), DEGREE_ANGLE);
192 case CHANNEL_GUST_SPEED:
193 state = getQuantityTypeState(localWeatherData.getCurrent().getWindGust(), METRE_PER_SECOND);
195 case CHANNEL_CLOUDINESS:
196 state = getQuantityTypeState(localWeatherData.getCurrent().getClouds(), PERCENT);
198 case CHANNEL_UVINDEX:
199 state = getDecimalTypeState(localWeatherData.getCurrent().getUvi());
202 Rain rain = localWeatherData.getCurrent().getRain();
203 state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
206 Snow snow = localWeatherData.getCurrent().getSnow();
207 state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
209 case CHANNEL_VISIBILITY:
211 State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
212 .toUnit(KILO(METRE));
213 state = (tempstate == null ? state : tempstate);
216 // This should not happen
217 logger.warn("Unknown channel id {} in onecall current weather data", channelId);
220 logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
221 updateState(channelUID, state);
223 logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
228 * Update the channel from the last OpenWeatherMap data retrieved.
230 * @param channelUID the id identifying the channel to be updated
231 * @param count the number of the hour referenced by the channel
233 private void updateHourlyHistoryChannel(ChannelUID channelUID, int count) {
234 String channelId = channelUID.getIdWithoutGroup();
235 String channelGroupId = channelUID.getGroupId();
236 logger.debug("Updating hourly history data for channel {}, group {}, count {}", channelId, channelGroupId,
238 OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
239 if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
240 Hourly historyData = localWeatherData.getHourly().get(count);
241 State state = UnDefType.UNDEF;
243 case CHANNEL_TIME_STAMP:
244 state = getDateTimeTypeState(historyData.getDt());
246 case CHANNEL_CONDITION:
247 if (!historyData.getWeather().isEmpty()) {
248 state = getStringTypeState(historyData.getWeather().get(0).getDescription());
251 case CHANNEL_CONDITION_ID:
252 if (!historyData.getWeather().isEmpty()) {
253 state = getStringTypeState(Integer.toString(historyData.getWeather().get(0).getId()));
256 case CHANNEL_CONDITION_ICON:
257 if (!historyData.getWeather().isEmpty()) {
258 state = getRawTypeState(
259 OpenWeatherMapConnection.getWeatherIcon(historyData.getWeather().get(0).getIcon()));
262 case CHANNEL_CONDITION_ICON_ID:
263 if (!historyData.getWeather().isEmpty()) {
264 state = getStringTypeState(historyData.getWeather().get(0).getIcon());
267 case CHANNEL_TEMPERATURE:
268 state = getQuantityTypeState(historyData.getTemp(), CELSIUS);
270 case CHANNEL_APPARENT_TEMPERATURE:
271 state = getQuantityTypeState(historyData.getFeelsLike(), CELSIUS);
273 case CHANNEL_PRESSURE:
274 state = getQuantityTypeState(historyData.getPressure(), HECTO(PASCAL));
276 case CHANNEL_HUMIDITY:
277 state = getQuantityTypeState(historyData.getHumidity(), PERCENT);
279 case CHANNEL_DEW_POINT:
280 state = getQuantityTypeState(historyData.getDewPoint(), CELSIUS);
282 case CHANNEL_WIND_SPEED:
283 state = getQuantityTypeState(historyData.getWindSpeed(), METRE_PER_SECOND);
285 case CHANNEL_WIND_DIRECTION:
286 state = getQuantityTypeState(historyData.getWindDeg(), DEGREE_ANGLE);
288 case CHANNEL_GUST_SPEED:
289 state = getQuantityTypeState(historyData.getWindGust(), METRE_PER_SECOND);
291 case CHANNEL_CLOUDINESS:
292 state = getQuantityTypeState(historyData.getClouds(), PERCENT);
294 case CHANNEL_VISIBILITY:
296 State tempstate = new QuantityType<>(historyData.getVisibility(), METRE).toUnit(KILO(METRE));
297 state = (tempstate == null ? state : tempstate);
299 Rain rain = historyData.getRain();
300 state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
303 Snow snow = historyData.getSnow();
304 state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
307 // This should not happen
308 logger.warn("Unknown channel id {} in onecall hourly weather data", channelId);
311 logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
312 updateState(channelUID, state);
314 logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);