]> git.basschouten.com Git - openhab-addons.git/blob
6ade3a2d5bf7744f9339de015532e1dd7fc94514
[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.ArrayList;
21 import java.util.List;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.client.HttpResponseException;
29 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapWeatherAndForecastConfiguration;
30 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
31 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonDailyForecastData;
32 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyForecastData;
33 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonWeatherData;
34 import org.openhab.binding.openweathermap.internal.dto.base.Rain;
35 import org.openhab.binding.openweathermap.internal.dto.base.Snow;
36 import org.openhab.binding.openweathermap.internal.dto.forecast.daily.FeelsLikeTemp;
37 import org.openhab.core.config.core.Configuration;
38 import org.openhab.core.i18n.CommunicationException;
39 import org.openhab.core.i18n.ConfigurationException;
40 import org.openhab.core.i18n.TimeZoneProvider;
41 import org.openhab.core.library.types.QuantityType;
42 import org.openhab.core.thing.Channel;
43 import org.openhab.core.thing.ChannelUID;
44 import org.openhab.core.thing.Thing;
45 import org.openhab.core.thing.ThingStatus;
46 import org.openhab.core.thing.ThingStatusDetail;
47 import org.openhab.core.thing.binding.builder.ThingBuilder;
48 import org.openhab.core.types.State;
49 import org.openhab.core.types.UnDefType;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.gson.JsonSyntaxException;
54
55 /**
56  * The {@link OpenWeatherMapWeatherAndForecastHandler} is responsible for handling commands, which are sent to one of
57  * the channels.
58  *
59  * @author Christoph Weitkamp - Initial contribution
60  */
61 @NonNullByDefault
62 public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeatherMapHandler {
63
64     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapWeatherAndForecastHandler.class);
65
66     private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
67     private static final String CHANNEL_GROUP_DAILY_FORECAST_PREFIX = "forecastDay";
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
73     // keeps track of the parsed counts
74     private int forecastHours = 24;
75     private int forecastDays = 6;
76
77     private @Nullable OpenWeatherMapJsonWeatherData weatherData;
78     private @Nullable OpenWeatherMapJsonHourlyForecastData hourlyForecastData;
79     private @Nullable OpenWeatherMapJsonDailyForecastData dailyForecastData;
80
81     public OpenWeatherMapWeatherAndForecastHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
82         super(thing, timeZoneProvider);
83     }
84
85     @Override
86     public void initialize() {
87         super.initialize();
88         logger.debug("Initialize OpenWeatherMapWeatherAndForecastHandler handler '{}'.", getThing().getUID());
89         OpenWeatherMapWeatherAndForecastConfiguration config = getConfigAs(
90                 OpenWeatherMapWeatherAndForecastConfiguration.class);
91
92         boolean configValid = true;
93         int newForecastHours = config.forecastHours;
94         if (newForecastHours < 0 || newForecastHours > 120 || newForecastHours % 3 != 0) {
95             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
96                     "@text/offline.conf-error-not-supported-number-of-hours");
97             configValid = false;
98         }
99         int newForecastDays = config.forecastDays;
100         if (newForecastDays < 0 || newForecastDays > 16) {
101             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
102                     "@text/offline.conf-error-not-supported-number-of-days");
103             configValid = false;
104         }
105
106         if (configValid) {
107             logger.debug("Rebuilding thing '{}'.", getThing().getUID());
108             List<Channel> toBeAddedChannels = new ArrayList<>();
109             List<Channel> toBeRemovedChannels = new ArrayList<>();
110             if (forecastHours != newForecastHours) {
111                 logger.debug("Rebuilding hourly forecast channel groups.");
112                 if (forecastHours > newForecastHours) {
113                     for (int i = newForecastHours + 3; i <= forecastHours; i += 3) {
114                         toBeRemovedChannels.addAll(removeChannelsOfGroup(
115                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
116                     }
117                 } else {
118                     for (int i = forecastHours + 3; i <= newForecastHours; i += 3) {
119                         toBeAddedChannels.addAll(createChannelsForGroup(
120                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
121                                 CHANNEL_GROUP_TYPE_HOURLY_FORECAST));
122                     }
123                 }
124                 forecastHours = newForecastHours;
125             }
126             if (forecastDays != newForecastDays) {
127                 logger.debug("Rebuilding daily forecast channel groups.");
128                 if (forecastDays > newForecastDays) {
129                     if (newForecastDays < 1) {
130                         toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TODAY));
131                     }
132                     if (newForecastDays < 2) {
133                         toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TOMORROW));
134                     }
135                     for (int i = newForecastDays; i < forecastDays; ++i) {
136                         toBeRemovedChannels.addAll(
137                                 removeChannelsOfGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i)));
138                     }
139                 } else {
140                     if (forecastDays == 0 && newForecastDays > 0) {
141                         toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TODAY,
142                                 CHANNEL_GROUP_TYPE_DAILY_FORECAST));
143                     }
144                     if (forecastDays <= 1 && newForecastDays > 1) {
145                         toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TOMORROW,
146                                 CHANNEL_GROUP_TYPE_DAILY_FORECAST));
147                     }
148                     for (int i = (forecastDays < 2) ? 2 : forecastDays; i < newForecastDays; ++i) {
149                         toBeAddedChannels.addAll(
150                                 createChannelsForGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i),
151                                         CHANNEL_GROUP_TYPE_DAILY_FORECAST));
152                     }
153                 }
154                 forecastDays = newForecastDays;
155             }
156             ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
157             for (Channel channel : toBeAddedChannels) {
158                 builder.withChannel(channel);
159             }
160             updateThing(builder.build());
161         }
162     }
163
164     @Override
165     protected boolean requestData(OpenWeatherMapConnection connection)
166             throws CommunicationException, ConfigurationException {
167         logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
168         try {
169             weatherData = connection.getWeatherData(location);
170             if (forecastHours > 0) {
171                 hourlyForecastData = connection.getHourlyForecastData(location, forecastHours / 3);
172             }
173             if (forecastDays > 0) {
174                 try {
175                     dailyForecastData = connection.getDailyForecastData(location, forecastDays);
176                 } catch (ConfigurationException e) {
177                     if (e.getCause() instanceof HttpResponseException) {
178                         forecastDays = 0;
179                         Configuration editConfig = editConfiguration();
180                         editConfig.put(CONFIG_FORECAST_DAYS, 0);
181                         updateConfiguration(editConfig);
182                         logger.debug("Removing daily forecast channel groups.");
183                         List<Channel> channels = getThing().getChannels().stream()
184                                 .filter(c -> CHANNEL_GROUP_FORECAST_TODAY.equals(c.getUID().getGroupId())
185                                         || CHANNEL_GROUP_FORECAST_TOMORROW.equals(c.getUID().getGroupId())
186                                         || c.getUID().getGroupId().startsWith(CHANNEL_GROUP_DAILY_FORECAST_PREFIX))
187                                 .collect(Collectors.toList());
188                         updateThing(editThing().withoutChannels(channels).build());
189                     } else {
190                         throw e;
191                     }
192                 }
193             }
194             return true;
195         } catch (JsonSyntaxException e) {
196             logger.debug("JsonSyntaxException occurred during execution: {}", e.getMessage(), e);
197             return false;
198         }
199     }
200
201     @Override
202     protected void updateChannel(ChannelUID channelUID) {
203         String channelGroupId = channelUID.getGroupId();
204         switch (channelGroupId) {
205             case CHANNEL_GROUP_STATION:
206             case CHANNEL_GROUP_CURRENT_WEATHER:
207                 updateCurrentChannel(channelUID);
208                 break;
209             case CHANNEL_GROUP_FORECAST_TODAY:
210                 updateDailyForecastChannel(channelUID, 0);
211                 break;
212             case CHANNEL_GROUP_FORECAST_TOMORROW:
213                 updateDailyForecastChannel(channelUID, 1);
214                 break;
215             default:
216                 int i;
217                 Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
218                 if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 3
219                         && i <= 120) {
220                     updateHourlyForecastChannel(channelUID, (i / 3) - 1);
221                     break;
222                 }
223                 Matcher dailyForecastMatcher = CHANNEL_GROUP_DAILY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
224                 if (dailyForecastMatcher.find() && (i = Integer.parseInt(dailyForecastMatcher.group(1))) > 1
225                         && i <= 16) {
226                     updateDailyForecastChannel(channelUID, i);
227                     break;
228                 }
229                 break;
230         }
231     }
232
233     /**
234      * Update the channel from the last OpenWeatherMap data retrieved.
235      *
236      * @param channelUID the id identifying the channel to be updated
237      */
238     private void updateCurrentChannel(ChannelUID channelUID) {
239         String channelId = channelUID.getIdWithoutGroup();
240         String channelGroupId = channelUID.getGroupId();
241         OpenWeatherMapJsonWeatherData localWeatherData = weatherData;
242         if (localWeatherData != null) {
243             State state = UnDefType.UNDEF;
244             switch (channelId) {
245                 case CHANNEL_STATION_ID:
246                     state = getStringTypeState(localWeatherData.getId().toString());
247                     break;
248                 case CHANNEL_STATION_NAME:
249                     state = getStringTypeState(localWeatherData.getName());
250                     break;
251                 case CHANNEL_STATION_LOCATION:
252                     state = getPointTypeState(localWeatherData.getCoord().getLat(),
253                             localWeatherData.getCoord().getLon());
254                     break;
255                 case CHANNEL_TIME_STAMP:
256                     state = getDateTimeTypeState(localWeatherData.getDt());
257                     break;
258                 case CHANNEL_CONDITION:
259                     if (!localWeatherData.getWeather().isEmpty()) {
260                         state = getStringTypeState(localWeatherData.getWeather().get(0).getDescription());
261                     }
262                     break;
263                 case CHANNEL_CONDITION_ID:
264                     if (!localWeatherData.getWeather().isEmpty()) {
265                         state = getStringTypeState(localWeatherData.getWeather().get(0).getId().toString());
266                     }
267                     break;
268                 case CHANNEL_CONDITION_ICON:
269                     if (!localWeatherData.getWeather().isEmpty()) {
270                         state = getRawTypeState(OpenWeatherMapConnection
271                                 .getWeatherIcon(localWeatherData.getWeather().get(0).getIcon()));
272                     }
273                     break;
274                 case CHANNEL_CONDITION_ICON_ID:
275                     if (!localWeatherData.getWeather().isEmpty()) {
276                         state = getStringTypeState(localWeatherData.getWeather().get(0).getIcon());
277                     }
278                     break;
279                 case CHANNEL_TEMPERATURE:
280                     state = getQuantityTypeState(localWeatherData.getMain().getTemp(), CELSIUS);
281                     break;
282                 case CHANNEL_APPARENT_TEMPERATURE:
283                     state = getQuantityTypeState(localWeatherData.getMain().getFeelsLikeTemp(), CELSIUS);
284                     break;
285                 case CHANNEL_PRESSURE:
286                     state = getQuantityTypeState(localWeatherData.getMain().getPressure(), HECTO(PASCAL));
287                     break;
288                 case CHANNEL_HUMIDITY:
289                     state = getQuantityTypeState(localWeatherData.getMain().getHumidity(), PERCENT);
290                     break;
291                 case CHANNEL_WIND_SPEED:
292                     state = getQuantityTypeState(localWeatherData.getWind().getSpeed(), METRE_PER_SECOND);
293                     break;
294                 case CHANNEL_WIND_DIRECTION:
295                     state = getQuantityTypeState(localWeatherData.getWind().getDeg(), DEGREE_ANGLE);
296                     break;
297                 case CHANNEL_GUST_SPEED:
298                     state = getQuantityTypeState(localWeatherData.getWind().getGust(), METRE_PER_SECOND);
299                     break;
300                 case CHANNEL_CLOUDINESS:
301                     state = getQuantityTypeState(localWeatherData.getClouds().getAll(), PERCENT);
302                     break;
303                 case CHANNEL_RAIN:
304                     Rain rain = localWeatherData.getRain();
305                     state = getQuantityTypeState(rain == null ? 0 : rain.getVolume(), MILLI(METRE));
306                     break;
307                 case CHANNEL_SNOW:
308                     Snow snow = localWeatherData.getSnow();
309                     state = getQuantityTypeState(snow == null ? 0 : snow.getVolume(), MILLI(METRE));
310                     break;
311                 case CHANNEL_VISIBILITY:
312                     Integer localVisibility = localWeatherData.getVisibility();
313                     state = localVisibility == null ? UnDefType.UNDEF
314                             : new QuantityType<>(localVisibility, METRE).toUnit(KILO(METRE));
315                     if (state == null) {
316                         logger.debug("State conversion failed, cannot update state.");
317                         return;
318                     }
319                     break;
320             }
321             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
322             updateState(channelUID, state);
323         } else {
324             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
325         }
326     }
327
328     /**
329      * Update the channel from the last OpenWeatherMap data retrieved.
330      *
331      * @param channelUID the id identifying the channel to be updated
332      * @param count
333      */
334     private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
335         String channelId = channelUID.getIdWithoutGroup();
336         String channelGroupId = channelUID.getGroupId();
337         OpenWeatherMapJsonHourlyForecastData localHourlyForecastData = hourlyForecastData;
338         if (localHourlyForecastData != null && localHourlyForecastData.getList().size() > count) {
339             org.openhab.binding.openweathermap.internal.dto.forecast.hourly.List forecastData = localHourlyForecastData
340                     .getList().get(count);
341             State state = UnDefType.UNDEF;
342             switch (channelId) {
343                 case CHANNEL_TIME_STAMP:
344                     state = getDateTimeTypeState(forecastData.getDt());
345                     break;
346                 case CHANNEL_CONDITION:
347                     if (!forecastData.getWeather().isEmpty()) {
348                         state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
349                     }
350                     break;
351                 case CHANNEL_CONDITION_ID:
352                     if (!forecastData.getWeather().isEmpty()) {
353                         state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
354                     }
355                     break;
356                 case CHANNEL_CONDITION_ICON:
357                     if (!forecastData.getWeather().isEmpty()) {
358                         state = getRawTypeState(
359                                 OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
360                     }
361                     break;
362                 case CHANNEL_CONDITION_ICON_ID:
363                     if (!forecastData.getWeather().isEmpty()) {
364                         state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
365                     }
366                     break;
367                 case CHANNEL_TEMPERATURE:
368                     state = getQuantityTypeState(forecastData.getMain().getTemp(), CELSIUS);
369                     break;
370                 case CHANNEL_APPARENT_TEMPERATURE:
371                     state = getQuantityTypeState(forecastData.getMain().getFeelsLikeTemp(), CELSIUS);
372                     break;
373                 case CHANNEL_MIN_TEMPERATURE:
374                     state = getQuantityTypeState(forecastData.getMain().getTempMin(), CELSIUS);
375                     break;
376                 case CHANNEL_MAX_TEMPERATURE:
377                     state = getQuantityTypeState(forecastData.getMain().getTempMax(), CELSIUS);
378                     break;
379                 case CHANNEL_PRESSURE:
380                     state = getQuantityTypeState(forecastData.getMain().getPressure(), HECTO(PASCAL));
381                     break;
382                 case CHANNEL_HUMIDITY:
383                     state = getQuantityTypeState(forecastData.getMain().getHumidity(), PERCENT);
384                     break;
385                 case CHANNEL_WIND_SPEED:
386                     state = getQuantityTypeState(forecastData.getWind().getSpeed(), METRE_PER_SECOND);
387                     break;
388                 case CHANNEL_WIND_DIRECTION:
389                     state = getQuantityTypeState(forecastData.getWind().getDeg(), DEGREE_ANGLE);
390                     break;
391                 case CHANNEL_GUST_SPEED:
392                     state = getQuantityTypeState(forecastData.getWind().getGust(), METRE_PER_SECOND);
393                     break;
394                 case CHANNEL_CLOUDINESS:
395                     state = getQuantityTypeState(forecastData.getClouds().getAll(), PERCENT);
396                     break;
397                 case CHANNEL_RAIN:
398                     Rain rain = forecastData.getRain();
399                     state = getQuantityTypeState(rain == null ? 0 : rain.getVolume(), MILLI(METRE));
400                     break;
401                 case CHANNEL_SNOW:
402                     Snow snow = forecastData.getSnow();
403                     state = getQuantityTypeState(snow == null ? 0 : snow.getVolume(), MILLI(METRE));
404                     break;
405             }
406             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
407             updateState(channelUID, state);
408         } else {
409             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
410         }
411     }
412
413     /**
414      * Update the channel from the last OpenWeatherMap data retrieved.
415      *
416      * @param channelUID the id identifying the channel to be updated
417      * @param count
418      */
419     private void updateDailyForecastChannel(ChannelUID channelUID, int count) {
420         String channelId = channelUID.getIdWithoutGroup();
421         String channelGroupId = channelUID.getGroupId();
422         OpenWeatherMapJsonDailyForecastData localDailyForecastData = dailyForecastData;
423         if (localDailyForecastData != null && localDailyForecastData.getList().size() > count) {
424             org.openhab.binding.openweathermap.internal.dto.forecast.daily.List forecastData = localDailyForecastData
425                     .getList().get(count);
426             State state = UnDefType.UNDEF;
427             switch (channelId) {
428                 case CHANNEL_TIME_STAMP:
429                     state = getDateTimeTypeState(forecastData.getDt());
430                     break;
431                 case CHANNEL_SUNRISE:
432                     state = getDateTimeTypeState(forecastData.getSunrise());
433                     break;
434                 case CHANNEL_SUNSET:
435                     state = getDateTimeTypeState(forecastData.getSunset());
436                     break;
437                 case CHANNEL_CONDITION:
438                     if (!forecastData.getWeather().isEmpty()) {
439                         state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
440                     }
441                     break;
442                 case CHANNEL_CONDITION_ID:
443                     if (!forecastData.getWeather().isEmpty()) {
444                         state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
445                     }
446                     break;
447                 case CHANNEL_CONDITION_ICON:
448                     if (!forecastData.getWeather().isEmpty()) {
449                         state = getRawTypeState(
450                                 OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
451                     }
452                     break;
453                 case CHANNEL_CONDITION_ICON_ID:
454                     if (!forecastData.getWeather().isEmpty()) {
455                         state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
456                     }
457                     break;
458                 case CHANNEL_MIN_TEMPERATURE:
459                     state = getQuantityTypeState(forecastData.getTemp().getMin(), CELSIUS);
460                     break;
461                 case CHANNEL_MAX_TEMPERATURE:
462                     state = getQuantityTypeState(forecastData.getTemp().getMax(), CELSIUS);
463                     break;
464                 case CHANNEL_APPARENT_TEMPERATURE:
465                     FeelsLikeTemp feelsLikeTemp = forecastData.getFeelsLikeTemp();
466                     if (feelsLikeTemp != null) {
467                         state = getQuantityTypeState(feelsLikeTemp.getDay(), CELSIUS);
468                     }
469                     break;
470                 case CHANNEL_PRESSURE:
471                     state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
472                     break;
473                 case CHANNEL_HUMIDITY:
474                     state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
475                     break;
476                 case CHANNEL_WIND_SPEED:
477                     state = getQuantityTypeState(forecastData.getSpeed(), METRE_PER_SECOND);
478                     break;
479                 case CHANNEL_WIND_DIRECTION:
480                     state = getQuantityTypeState(forecastData.getDeg(), DEGREE_ANGLE);
481                     break;
482                 case CHANNEL_GUST_SPEED:
483                     state = getQuantityTypeState(forecastData.getGust(), METRE_PER_SECOND);
484                     break;
485                 case CHANNEL_CLOUDINESS:
486                     state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
487                     break;
488                 case CHANNEL_RAIN:
489                     Double rain = forecastData.getRain();
490                     state = getQuantityTypeState(rain == null ? 0 : rain, MILLI(METRE));
491                     break;
492                 case CHANNEL_SNOW:
493                     Double snow = forecastData.getSnow();
494                     state = getQuantityTypeState(snow == null ? 0 : snow, MILLI(METRE));
495                     break;
496                 case CHANNEL_PRECIP_PROBABILITY:
497                     Double probability = forecastData.getPop();
498                     state = getQuantityTypeState(probability == null ? 0 : probability * 100.0, PERCENT);
499                     break;
500             }
501             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
502             updateState(channelUID, state);
503         } else {
504             logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
505         }
506     }
507 }