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