2 * Copyright (c) 2010-2021 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
13 package org.openhab.binding.openweathermap.internal.handler;
15 import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
16 import static org.openhab.core.library.unit.MetricPrefix.*;
17 import static org.openhab.core.library.unit.SIUnits.*;
18 import static org.openhab.core.library.unit.Units.*;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapOneCallHistoryConfiguration;
26 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
27 import org.openhab.binding.openweathermap.internal.dto.onecallhist.Hourly;
28 import org.openhab.binding.openweathermap.internal.dto.onecallhist.OpenWeatherMapOneCallHistAPIData;
29 import org.openhab.binding.openweathermap.internal.dto.onecallhist.Rain;
30 import org.openhab.binding.openweathermap.internal.dto.onecallhist.Snow;
31 import org.openhab.core.i18n.CommunicationException;
32 import org.openhab.core.i18n.ConfigurationException;
33 import org.openhab.core.i18n.TimeZoneProvider;
34 import org.openhab.core.library.types.QuantityType;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.types.State;
39 import org.openhab.core.types.UnDefType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import com.google.gson.JsonSyntaxException;
46 * The {@link OpenWeatherMapOneCallHistoryHandler} is responsible for handling commands, which are sent to one of
49 * @author Wolfgang Klimt - Initial contribution
52 public class OpenWeatherMapOneCallHistoryHandler extends AbstractOpenWeatherMapHandler {
54 private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapOneCallHistoryHandler.class);
56 private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "historyHours";
57 private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
58 .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
60 private @Nullable OpenWeatherMapOneCallHistAPIData weatherData;
62 // the relative day in history.
65 public OpenWeatherMapOneCallHistoryHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
66 super(thing, timeZoneProvider);
70 public void initialize() {
72 OpenWeatherMapOneCallHistoryConfiguration config = getConfigAs(OpenWeatherMapOneCallHistoryConfiguration.class);
73 if (config.historyDay <= 0) {
74 logger.warn("historyDay value of {} is not supported", config.historyDay);
78 * As of now, only 5 days in history are supported by the one call API. As this may change in the future,
79 * we allow any value here and only log a warning if the value exceeds 5.
81 if (config.historyDay > 5) {
82 logger.warn("History configuration of {} days may cause errors. You have been warned :-)",
85 day = config.historyDay;
86 logger.debug("Initialize OpenWeatherMapOneCallHistoryHandler handler '{}' with historyDay {}.",
87 getThing().getUID(), day);
88 updateStatus(ThingStatus.ONLINE);
92 protected boolean requestData(OpenWeatherMapConnection connection)
93 throws CommunicationException, ConfigurationException {
94 logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
96 weatherData = connection.getOneCallHistAPIData(location, day);
98 } catch (JsonSyntaxException e) {
99 logger.debug("JsonSyntaxException occurred during execution: {}", e.getMessage(), e);
105 protected void updateChannel(ChannelUID channelUID) {
106 String channelGroupId = channelUID.getGroupId();
107 switch (channelGroupId) {
108 case CHANNEL_GROUP_ONECALL_HISTORY:
109 updateHistoryCurrentChannel(channelUID);
114 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
115 if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 1
117 updateHourlyHistoryChannel(channelUID, i - 1);
125 * Update the channel from the last OpenWeatherMap data retrieved.
127 * @param channelUID the id identifying the channel to be updated
129 private void updateHistoryCurrentChannel(ChannelUID channelUID) {
130 String channelId = channelUID.getIdWithoutGroup();
131 String channelGroupId = channelUID.getGroupId();
132 OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
133 if (localWeatherData != null) {
134 State state = UnDefType.UNDEF;
136 case CHANNEL_STATION_LOCATION:
137 state = getPointTypeState(localWeatherData.getLat(), localWeatherData.getLon());
139 case CHANNEL_TIME_STAMP:
140 state = getDateTimeTypeState(localWeatherData.getCurrent().getDt());
142 case CHANNEL_SUNRISE:
143 state = getDateTimeTypeState(localWeatherData.getCurrent().getSunrise());
146 state = getDateTimeTypeState(localWeatherData.getCurrent().getSunset());
148 case CHANNEL_CONDITION:
149 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
150 state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getDescription());
153 case CHANNEL_CONDITION_ID:
154 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
155 state = getStringTypeState(
156 Integer.toString(localWeatherData.getCurrent().getWeather().get(0).getId()));
159 case CHANNEL_CONDITION_ICON:
160 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
161 state = getRawTypeState(OpenWeatherMapConnection
162 .getWeatherIcon(localWeatherData.getCurrent().getWeather().get(0).getIcon()));
165 case CHANNEL_CONDITION_ICON_ID:
166 if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
167 state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getIcon());
170 case CHANNEL_TEMPERATURE:
171 state = getQuantityTypeState(localWeatherData.getCurrent().getTemp(), CELSIUS);
173 case CHANNEL_APPARENT_TEMPERATURE:
174 state = getQuantityTypeState(localWeatherData.getCurrent().getFeelsLike(), CELSIUS);
176 case CHANNEL_PRESSURE:
177 state = getQuantityTypeState(localWeatherData.getCurrent().getPressure(), HECTO(PASCAL));
179 case CHANNEL_HUMIDITY:
180 state = getQuantityTypeState(localWeatherData.getCurrent().getHumidity(), PERCENT);
182 case CHANNEL_DEW_POINT:
183 state = getQuantityTypeState(localWeatherData.getCurrent().getDewPoint(), CELSIUS);
185 case CHANNEL_WIND_SPEED:
186 state = getQuantityTypeState(localWeatherData.getCurrent().getWindSpeed(), METRE_PER_SECOND);
188 case CHANNEL_WIND_DIRECTION:
189 state = getQuantityTypeState(localWeatherData.getCurrent().getWindDeg(), DEGREE_ANGLE);
191 case CHANNEL_GUST_SPEED:
192 state = getQuantityTypeState(localWeatherData.getCurrent().getWindGust(), METRE_PER_SECOND);
194 case CHANNEL_CLOUDINESS:
195 state = getQuantityTypeState(localWeatherData.getCurrent().getClouds(), PERCENT);
197 case CHANNEL_UVINDEX:
198 state = getDecimalTypeState(localWeatherData.getCurrent().getUvi());
201 Rain rain = localWeatherData.getCurrent().getRain();
202 state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
205 Snow snow = localWeatherData.getCurrent().getSnow();
206 state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
208 case CHANNEL_VISIBILITY:
210 State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
211 .toUnit(KILO(METRE));
212 state = (tempstate == null ? state : tempstate);
215 // This should not happen
216 logger.warn("Unknown channel id {} in onecall current weather data", channelId);
219 logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
220 updateState(channelUID, state);
222 logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
227 * Update the channel from the last OpenWeatherMap data retrieved.
229 * @param channelUID the id identifying the channel to be updated
230 * @param count the number of the hour referenced by the channel
232 private void updateHourlyHistoryChannel(ChannelUID channelUID, int count) {
233 String channelId = channelUID.getIdWithoutGroup();
234 String channelGroupId = channelUID.getGroupId();
235 logger.debug("Updating hourly history data for channel {}, group {}, count {}", channelId, channelGroupId,
237 OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
238 if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
239 Hourly historyData = localWeatherData.getHourly().get(count);
240 State state = UnDefType.UNDEF;
242 case CHANNEL_TIME_STAMP:
243 state = getDateTimeTypeState(historyData.getDt());
245 case CHANNEL_CONDITION:
246 if (!historyData.getWeather().isEmpty()) {
247 state = getStringTypeState(historyData.getWeather().get(0).getDescription());
250 case CHANNEL_CONDITION_ID:
251 if (!historyData.getWeather().isEmpty()) {
252 state = getStringTypeState(Integer.toString(historyData.getWeather().get(0).getId()));
255 case CHANNEL_CONDITION_ICON:
256 if (!historyData.getWeather().isEmpty()) {
257 state = getRawTypeState(
258 OpenWeatherMapConnection.getWeatherIcon(historyData.getWeather().get(0).getIcon()));
261 case CHANNEL_CONDITION_ICON_ID:
262 if (!historyData.getWeather().isEmpty()) {
263 state = getStringTypeState(historyData.getWeather().get(0).getIcon());
266 case CHANNEL_TEMPERATURE:
267 state = getQuantityTypeState(historyData.getTemp(), CELSIUS);
269 case CHANNEL_APPARENT_TEMPERATURE:
270 state = getQuantityTypeState(historyData.getFeelsLike(), CELSIUS);
272 case CHANNEL_PRESSURE:
273 state = getQuantityTypeState(historyData.getPressure(), HECTO(PASCAL));
275 case CHANNEL_HUMIDITY:
276 state = getQuantityTypeState(historyData.getHumidity(), PERCENT);
278 case CHANNEL_DEW_POINT:
279 state = getQuantityTypeState(historyData.getDewPoint(), CELSIUS);
281 case CHANNEL_WIND_SPEED:
282 state = getQuantityTypeState(historyData.getWindSpeed(), METRE_PER_SECOND);
284 case CHANNEL_WIND_DIRECTION:
285 state = getQuantityTypeState(historyData.getWindDeg(), DEGREE_ANGLE);
287 case CHANNEL_GUST_SPEED:
288 state = getQuantityTypeState(historyData.getWindGust(), METRE_PER_SECOND);
290 case CHANNEL_CLOUDINESS:
291 state = getQuantityTypeState(historyData.getClouds(), PERCENT);
293 case CHANNEL_VISIBILITY:
295 State tempstate = new QuantityType<>(historyData.getVisibility(), METRE).toUnit(KILO(METRE));
296 state = (tempstate == null ? state : tempstate);
298 Rain rain = historyData.getRain();
299 state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
302 Snow snow = historyData.getSnow();
303 state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
306 // This should not happen
307 logger.warn("Unknown channel id {} in onecall hourly weather data", channelId);
310 logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
311 updateState(channelUID, state);
313 logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);