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