]> git.basschouten.com Git - openhab-addons.git/blob
41d530cb72d087254b7c9e7451c157184337975d
[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
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAirPollutionConfiguration;
25 import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
26 import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonAirPollutionData;
27 import org.openhab.core.i18n.CommunicationException;
28 import org.openhab.core.i18n.ConfigurationException;
29 import org.openhab.core.i18n.TimeZoneProvider;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.unit.Units;
32 import org.openhab.core.thing.Channel;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.binding.builder.ThingBuilder;
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 OpenWeatherMapAirPollutionHandler} is responsible for handling commands, which are sent to one of the
47  * channels.
48  *
49  * @author Christoph Weitkamp - Initial contribution
50  */
51 @NonNullByDefault
52 public class OpenWeatherMapAirPollutionHandler extends AbstractOpenWeatherMapHandler {
53
54     private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAirPollutionHandler.class);
55
56     private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
57     private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
58             .compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
59
60     // keeps track of the parsed count
61     private int forecastHours = 0;
62
63     private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionData;
64     private @Nullable OpenWeatherMapJsonAirPollutionData airPollutionForecastData;
65
66     public OpenWeatherMapAirPollutionHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
67         super(thing, timeZoneProvider);
68     }
69
70     @Override
71     public void initialize() {
72         super.initialize();
73         logger.debug("Initialize OpenWeatherMapAirPollutionHandler handler '{}'.", getThing().getUID());
74         OpenWeatherMapAirPollutionConfiguration config = getConfigAs(OpenWeatherMapAirPollutionConfiguration.class);
75
76         boolean configValid = true;
77         int newForecastHours = config.forecastHours;
78         if (newForecastHours < 0 || newForecastHours > 120) {
79             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
80                     "@text/offline.conf-error-not-supported-air-pollution-number-of-hours");
81             configValid = false;
82         }
83
84         if (configValid) {
85             logger.debug("Rebuilding thing '{}'.", getThing().getUID());
86             List<Channel> toBeAddedChannels = new ArrayList<>();
87             List<Channel> toBeRemovedChannels = new ArrayList<>();
88             if (forecastHours != newForecastHours) {
89                 logger.debug("Rebuilding air pollution channel groups.");
90                 if (forecastHours > newForecastHours) {
91                     for (int i = newForecastHours + 1; i <= forecastHours; i++) {
92                         toBeRemovedChannels.addAll(removeChannelsOfGroup(
93                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
94                     }
95                 } else {
96                     for (int i = forecastHours + 1; i <= newForecastHours; i++) {
97                         toBeAddedChannels.addAll(createChannelsForGroup(
98                                 CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
99                                 CHANNEL_GROUP_TYPE_AIR_POLLUTION_FORECAST));
100                     }
101                 }
102                 forecastHours = newForecastHours;
103             }
104             ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
105             for (Channel channel : toBeAddedChannels) {
106                 builder.withChannel(channel);
107             }
108             updateThing(builder.build());
109         }
110     }
111
112     @Override
113     protected boolean requestData(OpenWeatherMapConnection connection)
114             throws CommunicationException, ConfigurationException {
115         logger.debug("Update air pollution data of thing '{}'.", getThing().getUID());
116         try {
117             airPollutionData = connection.getAirPollutionData(location);
118             if (forecastHours > 0) {
119                 airPollutionForecastData = connection.getAirPollutionForecastData(location);
120             }
121             return true;
122         } catch (JsonSyntaxException e) {
123             logger.debug("JsonSyntaxException occurred during execution: {}", e.getMessage(), e);
124             return false;
125         }
126     }
127
128     @Override
129     protected void updateChannel(ChannelUID channelUID) {
130         switch (channelUID.getGroupId()) {
131             case CHANNEL_GROUP_CURRENT_AIR_POLLUTION:
132                 updateCurrentAirPollutionChannel(channelUID);
133                 break;
134             default:
135                 Matcher m = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelUID.getGroupId());
136                 int i;
137                 if (m.find() && (i = Integer.parseInt(m.group(1))) > 0 && i <= 120) {
138                     updateHourlyForecastChannel(channelUID, i);
139                 }
140                 break;
141         }
142     }
143
144     /**
145      * Update the channel from the last OpenWeatherMap data retrieved.
146      *
147      * @param channelUID the id identifying the channel to be updated
148      */
149     private void updateCurrentAirPollutionChannel(ChannelUID channelUID) {
150         String channelId = channelUID.getIdWithoutGroup();
151         String channelGroupId = channelUID.getGroupId();
152         OpenWeatherMapJsonAirPollutionData localAirPollutionData = airPollutionData;
153         if (localAirPollutionData != null && !localAirPollutionData.list.isEmpty()) {
154             org.openhab.binding.openweathermap.internal.dto.airpollution.List currentData = localAirPollutionData.list
155                     .get(0);
156             State state = UnDefType.UNDEF;
157             switch (channelId) {
158                 case CHANNEL_TIME_STAMP:
159                     state = getDateTimeTypeState(currentData.dt);
160                     break;
161                 case CHANNEL_AIR_QUALITY_INDEX:
162                     state = new DecimalType(currentData.airQualityIndex.index);
163                     break;
164                 case CHANNEL_PARTICULATE_MATTER_2_5:
165                     state = getQuantityTypeState(currentData.measurements.particulateMatter2dot5,
166                             Units.MICROGRAM_PER_CUBICMETRE);
167                     break;
168                 case CHANNEL_PARTICULATE_MATTER_10:
169                     state = getQuantityTypeState(currentData.measurements.particulateMatter10,
170                             Units.MICROGRAM_PER_CUBICMETRE);
171                     break;
172                 case CHANNEL_CARBON_MONOXIDE:
173                     state = getQuantityTypeState(currentData.measurements.carbonMonoxide,
174                             Units.MICROGRAM_PER_CUBICMETRE);
175                     break;
176                 case CHANNEL_NITROGEN_MONOXIDE:
177                     state = getQuantityTypeState(currentData.measurements.nitrogenMonoxide,
178                             Units.MICROGRAM_PER_CUBICMETRE);
179                     break;
180                 case CHANNEL_NITROGEN_DIOXIDE:
181                     state = getQuantityTypeState(currentData.measurements.nitrogenDioxide,
182                             Units.MICROGRAM_PER_CUBICMETRE);
183                     break;
184                 case CHANNEL_OZONE:
185                     state = getQuantityTypeState(currentData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
186                     break;
187                 case CHANNEL_SULPHUR_DIOXIDE:
188                     state = getQuantityTypeState(currentData.measurements.sulphurDioxide,
189                             Units.MICROGRAM_PER_CUBICMETRE);
190                     break;
191                 case CHANNEL_AMMONIA:
192                     state = getQuantityTypeState(currentData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
193                     break;
194             }
195             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
196             updateState(channelUID, state);
197         } else {
198             logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
199                     channelGroupId);
200         }
201     }
202
203     /**
204      * Update the channel from the last OpenWeatherMap data retrieved.
205      *
206      * @param channelUID the id identifying the channel to be updated
207      * @param count
208      */
209     private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
210         String channelId = channelUID.getIdWithoutGroup();
211         String channelGroupId = channelUID.getGroupId();
212         OpenWeatherMapJsonAirPollutionData localAirPollutionForecastData = airPollutionForecastData;
213         if (localAirPollutionForecastData != null && localAirPollutionForecastData.list.size() >= count) {
214             org.openhab.binding.openweathermap.internal.dto.airpollution.List forecastData = localAirPollutionForecastData.list
215                     .get(count - 1);
216             State state = UnDefType.UNDEF;
217             switch (channelId) {
218                 case CHANNEL_TIME_STAMP:
219                     state = getDateTimeTypeState(forecastData.dt);
220                     break;
221                 case CHANNEL_AIR_QUALITY_INDEX:
222                     state = new DecimalType(forecastData.airQualityIndex.index);
223                     break;
224                 case CHANNEL_PARTICULATE_MATTER_2_5:
225                     state = getQuantityTypeState(forecastData.measurements.particulateMatter2dot5,
226                             Units.MICROGRAM_PER_CUBICMETRE);
227                     break;
228                 case CHANNEL_PARTICULATE_MATTER_10:
229                     state = getQuantityTypeState(forecastData.measurements.particulateMatter10,
230                             Units.MICROGRAM_PER_CUBICMETRE);
231                     break;
232                 case CHANNEL_CARBON_MONOXIDE:
233                     state = getQuantityTypeState(forecastData.measurements.carbonMonoxide,
234                             Units.MICROGRAM_PER_CUBICMETRE);
235                     break;
236                 case CHANNEL_NITROGEN_MONOXIDE:
237                     state = getQuantityTypeState(forecastData.measurements.nitrogenMonoxide,
238                             Units.MICROGRAM_PER_CUBICMETRE);
239                     break;
240                 case CHANNEL_NITROGEN_DIOXIDE:
241                     state = getQuantityTypeState(forecastData.measurements.nitrogenDioxide,
242                             Units.MICROGRAM_PER_CUBICMETRE);
243                     break;
244                 case CHANNEL_OZONE:
245                     state = getQuantityTypeState(forecastData.measurements.ozone, Units.MICROGRAM_PER_CUBICMETRE);
246                     break;
247                 case CHANNEL_SULPHUR_DIOXIDE:
248                     state = getQuantityTypeState(forecastData.measurements.sulphurDioxide,
249                             Units.MICROGRAM_PER_CUBICMETRE);
250                     break;
251                 case CHANNEL_AMMONIA:
252                     state = getQuantityTypeState(forecastData.measurements.ammonia, Units.MICROGRAM_PER_CUBICMETRE);
253                     break;
254             }
255             logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
256             updateState(channelUID, state);
257         } else {
258             logger.debug("No air pollution data available to update channel '{}' of group '{}'.", channelId,
259                     channelGroupId);
260         }
261     }
262 }