]> git.basschouten.com Git - openhab-addons.git/blob
763842d9e4e532371981e13a0a88990526c1a458
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.ArrayList;
21 import java.util.List;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapOneCallConfiguration;
28 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
29 import org.openhab.binding.openweathermap.internal.dto.onecall.Alert;
30 import org.openhab.binding.openweathermap.internal.dto.onecall.FeelsLike;
31 import org.openhab.binding.openweathermap.internal.dto.onecall.OpenWeatherMapOneCallAPIData;
32 import org.openhab.binding.openweathermap.internal.dto.onecall.Rain;
33 import org.openhab.binding.openweathermap.internal.dto.onecall.Snow;
34 import org.openhab.binding.openweathermap.internal.dto.onecall.Temp;
35 import org.openhab.core.i18n.CommunicationException;
36 import org.openhab.core.i18n.ConfigurationException;
37 import org.openhab.core.i18n.TimeZoneProvider;
38 import org.openhab.core.library.types.QuantityType;
39 import org.openhab.core.thing.Channel;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingStatusDetail;
44 import org.openhab.core.thing.binding.builder.ThingBuilder;
45 import org.openhab.core.types.State;
46 import org.openhab.core.types.UnDefType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.gson.JsonSyntaxException;
51
52 /**
53  * The {@link OpenWeatherMapOneCallHandler} is responsible for handling commands, which are sent to one of
54  * the channels.
55  *
56  * @author Wolfgang Klimt - Initial contribution
57  * @author Christoph Weitkamp - Added weather alerts
58  */
59 @NonNullByDefault
60 public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler {
61
62     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapOneCallHandler.class);
63
64     private static final String CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX = "forecastMinutes";
65     private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
66     private static final String CHANNEL_GROUP_DAILY_FORECAST_PREFIX = "forecastDay";
67     private static final String CHANNEL_GROUP_ALERTS_PREFIX = "alerts";
68     private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
69             .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
70     private static final Pattern CHANNEL_GROUP_DAILY_FORECAST_PREFIX_PATTERN = Pattern
71             .compile(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + "([0-9]*)");
72     private static final Pattern CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX_PATTERN = Pattern
73             .compile(CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX + "([0-9]*)");
74     private static final Pattern CHANNEL_GROUP_ALERTS_PREFIX_PATTERN = Pattern
75             .compile(CHANNEL_GROUP_ALERTS_PREFIX + "([0-9]*)");
76
77     private @Nullable OpenWeatherMapOneCallAPIData weatherData;
78
79     private int forecastMinutes = 60;
80     private int forecastHours = 24;
81     private int forecastDays = 8;
82     private int numberOfAlerts = 0;
83
84     public OpenWeatherMapOneCallHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
85         super(thing, timeZoneProvider);
86     }
87
88     @Override
89     public void initialize() {
90         super.initialize();
91         logger.debug("Initialize OpenWeatherMapOneCallHandler handler '{}'.", getThing().getUID());
92         OpenWeatherMapOneCallConfiguration config = getConfigAs(OpenWeatherMapOneCallConfiguration.class);
93
94         boolean configValid = true;
95         int newForecastMinutes = config.forecastMinutes;
96         if (newForecastMinutes < 0 || newForecastMinutes > 60) {
97             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
98                     "@text/offline.conf-error-not-supported-onecall-number-of-minutes");
99             configValid = false;
100         }
101         int newForecastHours = config.forecastHours;
102         if (newForecastHours < 0 || newForecastHours > 48) {
103             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
104                     "@text/offline.conf-error-not-supported-onecall-number-of-hours");
105             configValid = false;
106         }
107         int newForecastDays = config.forecastDays;
108         if (newForecastDays < 0 || newForecastDays > 8) {
109             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
110                     "@text/offline.conf-error-not-supported-onecall-number-of-days");
111             configValid = false;
112         }
113         int newNumberOfAlerts = config.numberOfAlerts;
114         if (newNumberOfAlerts < 0) {
115             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
116                     "@text/offline.conf-error-not-supported-onecall-number-of-alerts");
117             configValid = false;
118         }
119
120         if (configValid) {
121             logger.debug("Rebuilding thing '{}'.", getThing().getUID());
122             List<Channel> toBeAddedChannels = new ArrayList<>();
123             List<Channel> toBeRemovedChannels = new ArrayList<>();
124             toBeAddedChannels
125                     .addAll(createChannelsForGroup(CHANNEL_GROUP_ONECALL_CURRENT, CHANNEL_GROUP_TYPE_ONECALL_CURRENT));
126             if (forecastMinutes != newForecastMinutes) {
127                 logger.debug("forecastMinutes changed from {} to {}. Rebuilding minutely forecast channel groups.",
128                         forecastMinutes, newForecastMinutes);
129                 if (forecastMinutes > newForecastMinutes) {
130                     for (int i = newForecastMinutes + 1; i <= forecastMinutes; i++) {
131                         toBeRemovedChannels.addAll(removeChannelsOfGroup(
132                                 CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
133                     }
134                 } else {
135                     for (int i = forecastMinutes + 1; i <= newForecastMinutes; i++) {
136                         toBeAddedChannels.addAll(createChannelsForGroup(
137                                 CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
138                                 CHANNEL_GROUP_TYPE_ONECALL_MINUTELY_FORECAST));
139                     }
140                 }
141                 forecastMinutes = newForecastMinutes;
142             }
143             if (forecastHours != newForecastHours) {
144                 logger.debug("ForecastHours changed from {} to {}. Rebuilding hourly forecast channel groups.",
145                         forecastHours, newForecastHours);
146                 if (forecastHours > newForecastHours) {
147                     for (int i = newForecastHours + 1; i <= forecastHours; i++) {
148                         toBeRemovedChannels.addAll(removeChannelsOfGroup(
149                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
150                     }
151                 } else {
152                     for (int i = forecastHours + 1; i <= newForecastHours; i++) {
153                         toBeAddedChannels.addAll(createChannelsForGroup(
154                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
155                                 CHANNEL_GROUP_TYPE_ONECALL_HOURLY_FORECAST));
156                     }
157                 }
158                 forecastHours = newForecastHours;
159             }
160             if (forecastDays != newForecastDays) {
161                 logger.debug("ForecastDays changed from {} to {}. Rebuilding daily forecast channel groups.",
162                         forecastDays, newForecastDays);
163                 if (forecastDays > newForecastDays) {
164                     if (newForecastDays < 1) {
165                         toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TODAY));
166                     }
167                     if (newForecastDays < 2) {
168                         toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TOMORROW));
169                     }
170                     for (int i = newForecastDays; i < forecastDays; ++i) {
171                         toBeRemovedChannels.addAll(
172                                 removeChannelsOfGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i)));
173                     }
174                 } else {
175                     if (forecastDays == 0 && newForecastDays > 0) {
176                         toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TODAY,
177                                 CHANNEL_GROUP_TYPE_ONECALL_DAILY_FORECAST));
178                     }
179                     if (forecastDays <= 1 && newForecastDays > 1) {
180                         toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TOMORROW,
181                                 CHANNEL_GROUP_TYPE_ONECALL_DAILY_FORECAST));
182                     }
183                     for (int i = Math.max(forecastDays, 2); i < newForecastDays; ++i) {
184                         toBeAddedChannels.addAll(
185                                 createChannelsForGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i),
186                                         CHANNEL_GROUP_TYPE_ONECALL_DAILY_FORECAST));
187                     }
188                 }
189                 forecastDays = newForecastDays;
190                 if (numberOfAlerts != newNumberOfAlerts) {
191                     logger.debug("Rebuilding alerts channel groups.");
192                     if (numberOfAlerts > newNumberOfAlerts) {
193                         for (int i = newNumberOfAlerts + 1; i <= numberOfAlerts; ++i) {
194                             toBeRemovedChannels
195                                     .addAll(removeChannelsOfGroup(CHANNEL_GROUP_ALERTS_PREFIX + Integer.toString(i)));
196                         }
197                     } else {
198                         for (int i = numberOfAlerts + 1; i <= newNumberOfAlerts; ++i) {
199                             toBeAddedChannels
200                                     .addAll(createChannelsForGroup(CHANNEL_GROUP_ALERTS_PREFIX + Integer.toString(i),
201                                             CHANNEL_GROUP_TYPE_ONECALL_ALERTS));
202                         }
203                     }
204                     numberOfAlerts = newNumberOfAlerts;
205                 }
206             }
207             logger.debug("toBeRemovedChannels: {}. toBeAddedChannels: {}", toBeRemovedChannels, toBeAddedChannels);
208             ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
209             for (Channel channel : toBeAddedChannels) {
210                 builder.withChannel(channel);
211             }
212             updateThing(builder.build());
213         }
214     }
215
216     @Override
217     protected boolean requestData(OpenWeatherMapConnection connection)
218             throws CommunicationException, ConfigurationException {
219         logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
220         try {
221             weatherData = connection.getOneCallAPIData(location, forecastMinutes == 0, forecastHours == 0,
222                     forecastDays == 0, numberOfAlerts == 0);
223             return true;
224         } catch (JsonSyntaxException e) {
225             logger.debug("JsonSyntaxException occurred during execution: {}", e.getMessage(), e);
226             return false;
227         }
228     }
229
230     @Override
231     protected void updateChannel(ChannelUID channelUID) {
232         String channelGroupId = channelUID.getGroupId();
233         logger.debug("OneCallHandler: updateChannel {}, groupID {}", channelUID, channelGroupId);
234         switch (channelGroupId) {
235             case CHANNEL_GROUP_ONECALL_CURRENT:
236                 updateCurrentChannel(channelUID);
237                 break;
238             case CHANNEL_GROUP_ONECALL_TODAY:
239                 updateDailyForecastChannel(channelUID, 0);
240                 break;
241             case CHANNEL_GROUP_ONECALL_TOMORROW:
242                 updateDailyForecastChannel(channelUID, 1);
243                 break;
244             default:
245                 int i;
246                 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
247                 if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 1
248                         && i <= 48) {
249                     updateHourlyForecastChannel(channelUID, (i - 1));
250                     break;
251                 }
252                 Matcher dailyForecastMatcher = CHANNEL_GROUP_DAILY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
253                 if (dailyForecastMatcher.find() && (i = Integer.parseInt(dailyForecastMatcher.group(1))) >= 1
254                         && i <= 7) {
255                     updateDailyForecastChannel(channelUID, i);
256                     break;
257                 }
258                 Matcher minutelyForecastMatcher = CHANNEL_GROUP_MINUTELY_FORECAST_PREFIX_PATTERN
259                         .matcher(channelGroupId);
260                 if (minutelyForecastMatcher.find() && (i = Integer.parseInt(minutelyForecastMatcher.group(1))) >= 1
261                         && i <= 60) {
262                     updateMinutelyForecastChannel(channelUID, i - 1);
263                     break;
264                 }
265                 Matcher alertsMatcher = CHANNEL_GROUP_ALERTS_PREFIX_PATTERN.matcher(channelGroupId);
266                 if (alertsMatcher.find() && (i = Integer.parseInt(alertsMatcher.group(1))) >= 1) {
267                     updateAlertsChannel(channelUID, i);
268                     break;
269                 }
270                 break;
271         }
272     }
273
274     /**
275      * Update the channel from the last OpenWeatherMap data retrieved.
276      *
277      * @param channelUID the id identifying the channel to be updated
278      */
279     private void updateCurrentChannel(ChannelUID channelUID) {
280         String channelId = channelUID.getIdWithoutGroup();
281         String channelGroupId = channelUID.getGroupId();
282         OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
283         if (localWeatherData != null) {
284             State state = UnDefType.UNDEF;
285             switch (channelId) {
286                 case CHANNEL_STATION_LOCATION:
287                     state = getPointTypeState(localWeatherData.getLat(), localWeatherData.getLon());
288                     break;
289                 case CHANNEL_TIME_STAMP:
290                     state = getDateTimeTypeState(localWeatherData.getCurrent().getDt());
291                     break;
292                 case CHANNEL_SUNRISE:
293                     state = getDateTimeTypeState(localWeatherData.getCurrent().getSunrise());
294                     break;
295                 case CHANNEL_SUNSET:
296                     state = getDateTimeTypeState(localWeatherData.getCurrent().getSunset());
297                     break;
298                 case CHANNEL_CONDITION:
299                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
300                         state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getDescription());
301                     }
302                     break;
303                 case CHANNEL_CONDITION_ID:
304                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
305                         state = getStringTypeState(
306                                 Integer.toString(localWeatherData.getCurrent().getWeather().get(0).getId()));
307                     }
308                     break;
309                 case CHANNEL_CONDITION_ICON:
310                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
311                         state = getRawTypeState(OpenWeatherMapConnection
312                                 .getWeatherIcon(localWeatherData.getCurrent().getWeather().get(0).getIcon()));
313                     }
314                     break;
315                 case CHANNEL_CONDITION_ICON_ID:
316                     if (!localWeatherData.getCurrent().getWeather().isEmpty()) {
317                         state = getStringTypeState(localWeatherData.getCurrent().getWeather().get(0).getIcon());
318                     }
319                     break;
320                 case CHANNEL_TEMPERATURE:
321                     state = getQuantityTypeState(localWeatherData.getCurrent().getTemp(), CELSIUS);
322                     break;
323                 case CHANNEL_APPARENT_TEMPERATURE:
324                     state = getQuantityTypeState(localWeatherData.getCurrent().getFeelsLike(), CELSIUS);
325                     break;
326                 case CHANNEL_PRESSURE:
327                     state = getQuantityTypeState(localWeatherData.getCurrent().getPressure(), HECTO(PASCAL));
328                     break;
329                 case CHANNEL_HUMIDITY:
330                     state = getQuantityTypeState(localWeatherData.getCurrent().getHumidity(), PERCENT);
331                     break;
332                 case CHANNEL_DEW_POINT:
333                     state = getQuantityTypeState(localWeatherData.getCurrent().getDewPoint(), CELSIUS);
334                     break;
335                 case CHANNEL_WIND_SPEED:
336                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindSpeed(), METRE_PER_SECOND);
337                     break;
338                 case CHANNEL_WIND_DIRECTION:
339                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindDeg(), DEGREE_ANGLE);
340                     break;
341                 case CHANNEL_GUST_SPEED:
342                     state = getQuantityTypeState(localWeatherData.getCurrent().getWindGust(), METRE_PER_SECOND);
343                     break;
344                 case CHANNEL_CLOUDINESS:
345                     state = getQuantityTypeState(localWeatherData.getCurrent().getClouds(), PERCENT);
346                     break;
347                 case CHANNEL_UVINDEX:
348                     state = getDecimalTypeState(localWeatherData.getCurrent().getUvi());
349                     break;
350                 case CHANNEL_RAIN:
351                     Rain rain = localWeatherData.getCurrent().getRain();
352                     state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
353                     break;
354                 case CHANNEL_SNOW:
355                     Snow snow = localWeatherData.getCurrent().getSnow();
356                     state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
357                     break;
358                 case CHANNEL_VISIBILITY:
359                     State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
360                             .toUnit(KILO(METRE));
361                     state = (tempstate == null ? state : tempstate);
362                     break;
363                 default:
364                     // This should not happen
365                     logger.warn("Unknown channel id {} in onecall current weather data", channelId);
366                     break;
367             }
368             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
369             updateState(channelUID, state);
370         } else {
371             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
372         }
373     }
374
375     private void updateMinutelyForecastChannel(ChannelUID channelUID, int count) {
376         String channelId = channelUID.getIdWithoutGroup();
377         String channelGroupId = channelUID.getGroupId();
378         OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
379         if (forecastMinutes == 0) {
380             logger.warn(
381                     "Can't update channel group {} because forecastMinutes is set to '0'. Please adjust config accordingly",
382                     channelGroupId);
383             return;
384         }
385         if (localWeatherData != null && localWeatherData.getMinutely() != null
386                 && localWeatherData.getMinutely().size() > count) {
387             org.openhab.binding.openweathermap.internal.dto.onecall.Minutely forecastData = localWeatherData
388                     .getMinutely().get(count);
389             State state = UnDefType.UNDEF;
390             switch (channelId) {
391                 case CHANNEL_TIME_STAMP:
392                     state = getDateTimeTypeState(forecastData.getDt());
393                     break;
394                 case CHANNEL_PRECIPITATION:
395                     double precipitation = forecastData.getPrecipitation();
396                     state = getQuantityTypeState(precipitation, MILLI(METRE));
397                     break;
398                 default:
399                     // This should not happen
400                     logger.warn("Unknown channel id {} in onecall minutely weather data", channelId);
401                     break;
402             }
403             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
404             updateState(channelUID, state);
405         } else {
406             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
407         }
408     }
409
410     /**
411      * Update the channel from the last OpenWeatherMap data retrieved.
412      *
413      * @param channelUID the id identifying the channel to be updated
414      * @param count the number of the hour referenced by the channel
415      */
416     private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
417         String channelId = channelUID.getIdWithoutGroup();
418         String channelGroupId = channelUID.getGroupId();
419         if (forecastHours == 0) {
420             logger.warn(
421                     "Can't update channel group {} because forecastHours is set to '0'. Please adjust config accordingly",
422                     channelGroupId);
423             return;
424         }
425         OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
426         if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
427             org.openhab.binding.openweathermap.internal.dto.onecall.Hourly forecastData = localWeatherData.getHourly()
428                     .get(count);
429             State state = UnDefType.UNDEF;
430             switch (channelId) {
431                 case CHANNEL_TIME_STAMP:
432                     state = getDateTimeTypeState(forecastData.getDt());
433                     break;
434                 case CHANNEL_CONDITION:
435                     if (!forecastData.getWeather().isEmpty()) {
436                         state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
437                     }
438                     break;
439                 case CHANNEL_CONDITION_ID:
440                     if (!forecastData.getWeather().isEmpty()) {
441                         state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
442                     }
443                     break;
444                 case CHANNEL_CONDITION_ICON:
445                     if (!forecastData.getWeather().isEmpty()) {
446                         state = getRawTypeState(
447                                 OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
448                     }
449                     break;
450                 case CHANNEL_CONDITION_ICON_ID:
451                     if (!forecastData.getWeather().isEmpty()) {
452                         state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
453                     }
454                     break;
455                 case CHANNEL_TEMPERATURE:
456                     state = getQuantityTypeState(forecastData.getTemp(), CELSIUS);
457                     break;
458                 case CHANNEL_APPARENT_TEMPERATURE:
459                     state = getQuantityTypeState(forecastData.getFeelsLike(), CELSIUS);
460                     break;
461                 case CHANNEL_PRESSURE:
462                     state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
463                     break;
464                 case CHANNEL_HUMIDITY:
465                     state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
466                     break;
467                 case CHANNEL_DEW_POINT:
468                     state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
469                     break;
470                 case CHANNEL_WIND_SPEED:
471                     state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
472                     break;
473                 case CHANNEL_WIND_DIRECTION:
474                     state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
475                     break;
476                 case CHANNEL_GUST_SPEED:
477                     state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
478                     break;
479                 case CHANNEL_CLOUDINESS:
480                     state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
481                     break;
482                 case CHANNEL_VISIBILITY:
483                     State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
484                             .toUnit(KILO(METRE));
485                     state = (tempstate == null ? state : tempstate);
486                 case CHANNEL_PRECIP_PROBABILITY:
487                     state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
488                     break;
489                 case CHANNEL_RAIN:
490                     Rain rain = forecastData.getRain();
491                     state = getQuantityTypeState(rain == null ? 0 : rain.get1h(), MILLI(METRE));
492                     break;
493                 case CHANNEL_SNOW:
494                     Snow snow = forecastData.getSnow();
495                     state = getQuantityTypeState(snow == null ? 0 : snow.get1h(), MILLI(METRE));
496                     break;
497                 default:
498                     // This should not happen
499                     logger.warn("Unknown channel id {} in onecall hourly weather data", channelId);
500                     break;
501             }
502             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
503             updateState(channelUID, state);
504         } else {
505             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
506         }
507     }
508
509     /**
510      * Update the channel from the last OpenWeatherMap data retrieved.
511      *
512      * @param channelUID the id identifying the channel to be updated
513      * @param count
514      */
515     private void updateDailyForecastChannel(ChannelUID channelUID, int count) {
516         String channelId = channelUID.getIdWithoutGroup();
517         String channelGroupId = channelUID.getGroupId();
518         if (forecastDays == 0) {
519             logger.warn(
520                     "Can't update channel group {} because forecastDays is set to '0'. Please adjust config accordingly",
521                     channelGroupId);
522             return;
523         }
524         OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
525         if (localWeatherData != null && localWeatherData.getDaily().size() > count) {
526             org.openhab.binding.openweathermap.internal.dto.onecall.Daily forecastData = localWeatherData.getDaily()
527                     .get(count);
528             State state = UnDefType.UNDEF;
529             Temp temp;
530             FeelsLike feelsLike;
531             switch (channelId) {
532                 case CHANNEL_TIME_STAMP:
533                     state = getDateTimeTypeState(forecastData.getDt());
534                     break;
535                 case CHANNEL_SUNRISE:
536                     state = getDateTimeTypeState(forecastData.getSunrise());
537                     break;
538                 case CHANNEL_SUNSET:
539                     state = getDateTimeTypeState(forecastData.getSunset());
540                     break;
541                 case CHANNEL_CONDITION:
542                     if (!forecastData.getWeather().isEmpty()) {
543                         state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
544                     }
545                     break;
546                 case CHANNEL_CONDITION_ID:
547                     if (!forecastData.getWeather().isEmpty()) {
548                         state = getStringTypeState(Integer.toString(forecastData.getWeather().get(0).getId()));
549                     }
550                     break;
551                 case CHANNEL_CONDITION_ICON:
552                     if (!forecastData.getWeather().isEmpty()) {
553                         state = getRawTypeState(
554                                 OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
555                     }
556                     break;
557                 case CHANNEL_CONDITION_ICON_ID:
558                     if (!forecastData.getWeather().isEmpty()) {
559                         state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
560                     }
561                     break;
562                 case CHANNEL_MIN_TEMPERATURE:
563                     temp = forecastData.getTemp();
564                     if (temp != null) {
565                         state = getQuantityTypeState(temp.getMin(), CELSIUS);
566                     }
567                     break;
568                 case CHANNEL_MAX_TEMPERATURE:
569                     temp = forecastData.getTemp();
570                     if (temp != null) {
571                         state = getQuantityTypeState(temp.getMax(), CELSIUS);
572                     }
573                     break;
574                 case CHANNEL_MORNING_TEMPERATURE:
575                     temp = forecastData.getTemp();
576                     if (temp != null) {
577                         state = getQuantityTypeState(temp.getMorn(), CELSIUS);
578                     }
579                     break;
580                 case CHANNEL_DAY_TEMPERATURE:
581                     temp = forecastData.getTemp();
582                     if (temp != null) {
583                         state = getQuantityTypeState(temp.getDay(), CELSIUS);
584                     }
585                     break;
586                 case CHANNEL_EVENING_TEMPERATURE:
587                     temp = forecastData.getTemp();
588                     if (temp != null) {
589                         state = getQuantityTypeState(temp.getEve(), CELSIUS);
590                     }
591                     break;
592                 case CHANNEL_NIGHT_TEMPERATURE:
593                     temp = forecastData.getTemp();
594                     if (temp != null) {
595                         state = getQuantityTypeState(temp.getNight(), CELSIUS);
596                     }
597                     break;
598
599                 case CHANNEL_APPARENT_DAY:
600                     feelsLike = forecastData.getFeelsLike();
601                     if (feelsLike != null) {
602                         state = getQuantityTypeState(feelsLike.getDay(), CELSIUS);
603                     }
604                     break;
605                 case CHANNEL_APPARENT_MORNING:
606                     feelsLike = forecastData.getFeelsLike();
607                     if (feelsLike != null) {
608                         state = getQuantityTypeState(feelsLike.getMorn(), CELSIUS);
609                     }
610                     break;
611                 case CHANNEL_APPARENT_EVENING:
612                     feelsLike = forecastData.getFeelsLike();
613                     if (feelsLike != null) {
614                         state = getQuantityTypeState(feelsLike.getEve(), CELSIUS);
615                     }
616                     break;
617                 case CHANNEL_APPARENT_NIGHT:
618                     feelsLike = forecastData.getFeelsLike();
619                     if (feelsLike != null) {
620                         state = getQuantityTypeState(feelsLike.getNight(), CELSIUS);
621                     }
622                     break;
623                 case CHANNEL_PRESSURE:
624                     state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
625                     break;
626                 case CHANNEL_HUMIDITY:
627                     state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
628                     break;
629                 case CHANNEL_WIND_SPEED:
630                     state = getQuantityTypeState(forecastData.getWindSpeed(), METRE_PER_SECOND);
631                     break;
632                 case CHANNEL_WIND_DIRECTION:
633                     state = getQuantityTypeState(forecastData.getWindDeg(), DEGREE_ANGLE);
634                     break;
635                 case CHANNEL_GUST_SPEED:
636                     state = getQuantityTypeState(forecastData.getWindGust(), METRE_PER_SECOND);
637                     break;
638                 case CHANNEL_CLOUDINESS:
639                     state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
640                     break;
641                 case CHANNEL_DEW_POINT:
642                     state = getQuantityTypeState(forecastData.getDewPoint(), CELSIUS);
643                     break;
644                 case CHANNEL_UVINDEX:
645                     state = getDecimalTypeState(forecastData.getUvi());
646                     break;
647                 case CHANNEL_VISIBILITY:
648                     State tempstate = new QuantityType<>(localWeatherData.getCurrent().getVisibility(), METRE)
649                             .toUnit(KILO(METRE));
650                     state = (tempstate == null ? state : tempstate);
651                 case CHANNEL_PRECIP_PROBABILITY:
652                     state = getQuantityTypeState(forecastData.getPop() * 100.0, PERCENT);
653                     break;
654                 case CHANNEL_RAIN:
655                     state = getQuantityTypeState(forecastData.getRain(), MILLI(METRE));
656                     break;
657                 case CHANNEL_SNOW:
658                     state = getQuantityTypeState(forecastData.getSnow(), MILLI(METRE));
659                     break;
660                 default:
661                     // This should not happen
662                     logger.warn("Unknown channel id {} in onecall daily weather data", channelId);
663                     break;
664             }
665             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
666             updateState(channelUID, state);
667         } else {
668             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
669         }
670     }
671
672     /**
673      * Update the channel from the last OpenWeaterhMap data retrieved.
674      *
675      * @param channelUID the id identifying the channel to be updated
676      * @param count
677      */
678     private void updateAlertsChannel(ChannelUID channelUID, int count) {
679         String channelId = channelUID.getIdWithoutGroup();
680         String channelGroupId = channelUID.getGroupId();
681         OpenWeatherMapOneCallAPIData localWeatherData = weatherData;
682         List<Alert> alerts = localWeatherData != null ? localWeatherData.alerts : null;
683         State state = UnDefType.UNDEF;
684         if (alerts != null && alerts.size() > count) {
685             Alert alert = alerts.get(count - 1);
686             switch (channelId) {
687                 case CHANNEL_ALERT_EVENT:
688                     state = getStringTypeState(alert.event);
689                     break;
690                 case CHANNEL_ALERT_DESCRIPTION:
691                     state = getStringTypeState(alert.description);
692                     break;
693                 case CHANNEL_ALERT_ONSET:
694                     state = getDateTimeTypeState(alert.start);
695                     break;
696                 case CHANNEL_ALERT_EXPIRES:
697                     state = getDateTimeTypeState(alert.end);
698                     break;
699                 case CHANNEL_ALERT_SOURCE:
700                     state = getStringTypeState(alert.senderName);
701                     break;
702             }
703             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
704         } else {
705             logger.debug("No data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
706         }
707         updateState(channelUID, state);
708     }
709 }