]> git.basschouten.com Git - openhab-addons.git/blob
54356724767661ed8365f22f249986ef9cb1dbc8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13
14 package org.openhab.binding.openweathermap.internal.handler;
15
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.*;
22
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25
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;
43
44 import com.google.gson.JsonSyntaxException;
45
46 /**
47  * The {@link OpenWeatherMapOneCallHistoryHandler} is responsible for handling commands, which are sent to one of
48  * the channels.
49  *
50  * @author Wolfgang Klimt - Initial contribution
51  */
52 @NonNullByDefault
53 public class OpenWeatherMapOneCallHistoryHandler extends AbstractOpenWeatherMapHandler {
54
55     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapOneCallHistoryHandler.class);
56
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]*)");
60
61     private @Nullable OpenWeatherMapOneCallHistAPIData weatherData;
62
63     // the relative day in history.
64     private int day = 0;
65
66     public OpenWeatherMapOneCallHistoryHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
67         super(thing, timeZoneProvider);
68     }
69
70     @Override
71     public void initialize() {
72         super.initialize();
73         OpenWeatherMapOneCallHistoryConfiguration config = getConfigAs(OpenWeatherMapOneCallHistoryConfiguration.class);
74         if (config.historyDay <= 0) {
75             logger.warn("historyDay value of {} is not supported", config.historyDay);
76             return;
77         }
78         /*
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.
81          */
82         if (config.historyDay > 5) {
83             logger.warn("History configuration of {} days may cause errors. You have been warned :-)",
84                     config.historyDay);
85         }
86         day = config.historyDay;
87         logger.debug("Initialize OpenWeatherMapOneCallHistoryHandler handler '{}' with historyDay {}.",
88                 getThing().getUID(), day);
89         updateStatus(ThingStatus.ONLINE);
90     }
91
92     @Override
93     protected boolean requestData(OpenWeatherMapConnection connection)
94             throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
95         logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
96         try {
97             weatherData = connection.getOneCallHistAPIData(location, day);
98             return true;
99         } catch (JsonSyntaxException e) {
100             logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
101             return false;
102         }
103     }
104
105     @Override
106     protected void updateChannel(ChannelUID channelUID) {
107         String channelGroupId = channelUID.getGroupId();
108         switch (channelGroupId) {
109             case CHANNEL_GROUP_ONECALL_HISTORY:
110                 updateHistoryCurrentChannel(channelUID);
111                 break;
112
113             default:
114                 int i;
115                 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
116                 if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 1
117                         && i <= 48) {
118                     updateHourlyHistoryChannel(channelUID, i - 1);
119                     break;
120                 }
121                 break;
122         }
123     }
124
125     /**
126      * Update the channel from the last OpenWeatherMap data retrieved.
127      *
128      * @param channelUID the id identifying the channel to be updated
129      */
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;
136             switch (channelId) {
137                 case CHANNEL_STATION_LOCATION:
138                     state = getPointTypeState(localWeatherData.getLat(), localWeatherData.getLon());
139                     break;
140                 case CHANNEL_TIME_STAMP:
141                     state = getDateTimeTypeState(localWeatherData.getCurrent().getDt());
142                     break;
143                 case CHANNEL_SUNRISE:
144                     state = getDateTimeTypeState(localWeatherData.getCurrent().getSunrise());
145                     break;
146                 case CHANNEL_SUNSET:
147                     state = getDateTimeTypeState(localWeatherData.getCurrent().getSunset());
148                     break;
149                 case CHANNEL_CONDITION:
150                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
151                         state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getDescription());
152                     }
153                     break;
154                 case CHANNEL_CONDITION_ID:
155                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
156                         state = getStringTypeState(
157                                 Integer.toString(localWeatherData.getCurrent().getWeather().get(0).getId()));
158                     }
159                     break;
160                 case CHANNEL_CONDITION_ICON:
161                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
162                         state = getRawTypeState(OpenWeatherMapConnection
163                                 .getWeatherIcon(localWeatherData.getCurrent().getWeather().get(0).getIcon()));
164                     }
165                     break;
166                 case CHANNEL_CONDITION_ICON_ID:
167                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
168                         state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getIcon());
169                     }
170                     break;
171                 case CHANNEL_TEMPERATURE:
172                     state = getQuantityTypeState(localWeatherData.getCurrent().getTemp(), CELSIUS);
173                     break;
174                 case CHANNEL_APPARENT_TEMPERATURE:
175                     state = getQuantityTypeState(localWeatherData.getCurrent().getFeelsLike(), CELSIUS);
176                     break;
177                 case CHANNEL_PRESSURE:
178                     state = getQuantityTypeState(localWeatherData.getCurrent().getPressure(), HECTO(PASCAL));
179                     break;
180                 case CHANNEL_HUMIDITY:
181                     state = getQuantityTypeState(localWeatherData.getCurrent().getHumidity(), PERCENT);
182                     break;
183                 case CHANNEL_DEW_POINT:
184                     state = getQuantityTypeState(localWeatherData.getCurrent().getDewPoint(), CELSIUS);
185                     break;
186                 case CHANNEL_WIND_SPEED:
187                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindSpeed(), METRE_PER_SECOND);
188                     break;
189                 case CHANNEL_WIND_DIRECTION:
190                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindDeg(), DEGREE_ANGLE);
191                     break;
192                 case CHANNEL_GUST_SPEED:
193                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindGust(), METRE_PER_SECOND);
194                     break;
195                 case CHANNEL_CLOUDINESS:
196                     state = getQuantityTypeState(localWeatherData.getCurrent().getClouds(), PERCENT);
197                     break;
198                 case CHANNEL_UVINDEX:
199                     state = getDecimalTypeState(localWeatherData.getCurrent().getUvi());
200                     break;
201                 case CHANNEL_RAIN:
202                     Rain rain = localWeatherData.getCurrent().getRain();
203                     state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
204                     break;
205                 case CHANNEL_SNOW:
206                     Snow snow = localWeatherData.getCurrent().getSnow();
207                     state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
208                     break;
209                 case CHANNEL_VISIBILITY:
210                     @Nullable
211                     State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
212                             .toUnit(KILO(METRE));
213                     state = (tempstate == null ? state : tempstate);
214                     break;
215                 default:
216                     // This should not happen
217                     logger.warn("Unknown channel id {} in onecall current weather data", channelId);
218                     break;
219             }
220             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
221             updateState(channelUID, state);
222         } else {
223             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
224         }
225     }
226
227     /**
228      * Update the channel from the last OpenWeatherMap data retrieved.
229      *
230      * @param channelUID the id identifying the channel to be updated
231      * @param count the number of the hour referenced by the channel
232      */
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,
237                 count);
238         OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
239         if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
240             Hourly historyData = localWeatherData.getHourly().get(count);
241             State state = UnDefType.UNDEF;
242             switch (channelId) {
243                 case CHANNEL_TIME_STAMP:
244                     state = getDateTimeTypeState(historyData.getDt());
245                     break;
246                 case CHANNEL_CONDITION:
247                     if (!historyData.getWeather().isEmpty()) {
248                         state = getStringTypeState(historyData.getWeather().get(0).getDescription());
249                     }
250                     break;
251                 case CHANNEL_CONDITION_ID:
252                     if (!historyData.getWeather().isEmpty()) {
253                         state = getStringTypeState(Integer.toString(historyData.getWeather().get(0).getId()));
254                     }
255                     break;
256                 case CHANNEL_CONDITION_ICON:
257                     if (!historyData.getWeather().isEmpty()) {
258                         state = getRawTypeState(
259                                 OpenWeatherMapConnection.getWeatherIcon(historyData.getWeather().get(0).getIcon()));
260                     }
261                     break;
262                 case CHANNEL_CONDITION_ICON_ID:
263                     if (!historyData.getWeather().isEmpty()) {
264                         state = getStringTypeState(historyData.getWeather().get(0).getIcon());
265                     }
266                     break;
267                 case CHANNEL_TEMPERATURE:
268                     state = getQuantityTypeState(historyData.getTemp(), CELSIUS);
269                     break;
270                 case CHANNEL_APPARENT_TEMPERATURE:
271                     state = getQuantityTypeState(historyData.getFeelsLike(), CELSIUS);
272                     break;
273                 case CHANNEL_PRESSURE:
274                     state = getQuantityTypeState(historyData.getPressure(), HECTO(PASCAL));
275                     break;
276                 case CHANNEL_HUMIDITY:
277                     state = getQuantityTypeState(historyData.getHumidity(), PERCENT);
278                     break;
279                 case CHANNEL_DEW_POINT:
280                     state = getQuantityTypeState(historyData.getDewPoint(), CELSIUS);
281                     break;
282                 case CHANNEL_WIND_SPEED:
283                     state = getQuantityTypeState(historyData.getWindSpeed(), METRE_PER_SECOND);
284                     break;
285                 case CHANNEL_WIND_DIRECTION:
286                     state = getQuantityTypeState(historyData.getWindDeg(), DEGREE_ANGLE);
287                     break;
288                 case CHANNEL_GUST_SPEED:
289                     state = getQuantityTypeState(historyData.getWindGust(), METRE_PER_SECOND);
290                     break;
291                 case CHANNEL_CLOUDINESS:
292                     state = getQuantityTypeState(historyData.getClouds(), PERCENT);
293                     break;
294                 case CHANNEL_VISIBILITY:
295                     @Nullable
296                     State tempstate = new QuantityType<>(historyData.getVisibility(), METRE).toUnit(KILO(METRE));
297                     state = (tempstate == null ? state : tempstate);
298                 case CHANNEL_RAIN:
299                     Rain rain = historyData.getRain();
300                     state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
301                     break;
302                 case CHANNEL_SNOW:
303                     Snow snow = historyData.getSnow();
304                     state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
305                     break;
306                 default:
307                     // This should not happen
308                     logger.warn("Unknown channel id {} in onecall hourly weather data", channelId);
309                     break;
310             }
311             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
312             updateState(channelUID, state);
313         } else {
314             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
315         }
316     }
317 }