2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.netatmo.internal.station;
15 import static org.openhab.binding.netatmo.internal.APIUtils.*;
16 import static org.openhab.binding.netatmo.internal.ChannelTypeUtils.*;
17 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
19 import java.util.ArrayList;
20 import java.util.List;
22 import java.util.Optional;
23 import java.util.concurrent.ConcurrentHashMap;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.openhab.binding.netatmo.internal.WeatherUtils;
27 import org.openhab.binding.netatmo.internal.handler.NetatmoDeviceHandler;
28 import org.openhab.binding.netatmo.internal.handler.NetatmoModuleHandler;
29 import org.openhab.core.i18n.TimeZoneProvider;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.types.State;
33 import io.swagger.client.model.NADashboardData;
34 import io.swagger.client.model.NAMain;
37 * {@link NAMainHandler} is the base class for all current Netatmo
38 * weather station equipments (both modules and devices)
40 * @author Gaƫl L'hopital - Initial contribution
41 * @author Rob Nielsen - Added day, week, and month measurements to the weather station and modules
45 public class NAMainHandler extends NetatmoDeviceHandler<NAMain> {
46 private Map<String, Float> channelMeasurements = new ConcurrentHashMap<>();
48 public NAMainHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
49 super(thing, timeZoneProvider);
53 protected Optional<NAMain> updateReadings() {
54 Optional<NAMain> result = getBridgeHandler().flatMap(handler -> handler.getStationsDataBody(getId()))
55 .map(dataBody -> nonNullStream(dataBody.getDevices())
56 .filter(device -> device.getId().equalsIgnoreCase(getId())).findFirst().orElse(null));
57 result.ifPresent(device -> {
58 nonNullList(device.getModules()).forEach(child -> childs.put(child.getId(), child));
63 childs.keySet().forEach((childId) -> {
64 findNAThing(childId).map(NetatmoModuleHandler.class::cast).ifPresent(naChildModule -> {
65 naChildModule.updateMeasurements();
73 protected void updateProperties(NAMain deviceData) {
74 updateProperties(deviceData.getFirmware(), deviceData.getType());
78 public void updateMeasurements() {
79 updateDayMeasurements();
80 updateWeekMeasurements();
81 updateMonthMeasurements();
84 private void updateDayMeasurements() {
85 List<String> channels = new ArrayList<>();
86 List<String> types = new ArrayList<>();
87 addMeasurement(channels, types, CHANNEL_MIN_CO2, MIN_CO2);
88 addMeasurement(channels, types, CHANNEL_MAX_CO2, MAX_CO2);
89 addMeasurement(channels, types, CHANNEL_MIN_HUMIDITY, MIN_HUM);
90 addMeasurement(channels, types, CHANNEL_MAX_HUMIDITY, MAX_HUM);
91 addMeasurement(channels, types, CHANNEL_MIN_NOISE, MIN_NOISE);
92 addMeasurement(channels, types, CHANNEL_MAX_NOISE, MAX_NOISE);
93 addMeasurement(channels, types, CHANNEL_MIN_PRESSURE, MIN_PRESSURE);
94 addMeasurement(channels, types, CHANNEL_MAX_PRESSURE, MAX_PRESSURE);
95 addMeasurement(channels, types, CHANNEL_DATE_MIN_CO2, DATE_MIN_CO2);
96 addMeasurement(channels, types, CHANNEL_DATE_MAX_CO2, DATE_MAX_CO2);
97 addMeasurement(channels, types, CHANNEL_DATE_MIN_HUMIDITY, DATE_MIN_HUM);
98 addMeasurement(channels, types, CHANNEL_DATE_MAX_HUMIDITY, DATE_MAX_HUM);
99 addMeasurement(channels, types, CHANNEL_DATE_MIN_NOISE, DATE_MIN_NOISE);
100 addMeasurement(channels, types, CHANNEL_DATE_MAX_NOISE, DATE_MAX_NOISE);
101 addMeasurement(channels, types, CHANNEL_DATE_MIN_PRESSURE, DATE_MIN_PRESSURE);
102 addMeasurement(channels, types, CHANNEL_DATE_MAX_PRESSURE, DATE_MAX_PRESSURE);
103 if (!channels.isEmpty()) {
104 getMeasurements(getId(), null, ONE_DAY, types, channels, channelMeasurements);
108 private void updateWeekMeasurements() {
109 List<String> channels = new ArrayList<>();
110 List<String> types = new ArrayList<>();
111 addMeasurement(channels, types, CHANNEL_MIN_CO2_THIS_WEEK, MIN_CO2);
112 addMeasurement(channels, types, CHANNEL_MAX_CO2_THIS_WEEK, MAX_CO2);
113 addMeasurement(channels, types, CHANNEL_MIN_HUMIDITY_THIS_WEEK, MIN_HUM);
114 addMeasurement(channels, types, CHANNEL_MAX_HUMIDITY_THIS_WEEK, MAX_HUM);
115 addMeasurement(channels, types, CHANNEL_MIN_NOISE_THIS_WEEK, MIN_NOISE);
116 addMeasurement(channels, types, CHANNEL_MAX_NOISE_THIS_WEEK, MAX_NOISE);
117 addMeasurement(channels, types, CHANNEL_MIN_PRESSURE_THIS_WEEK, MIN_PRESSURE);
118 addMeasurement(channels, types, CHANNEL_MAX_PRESSURE_THIS_WEEK, MAX_PRESSURE);
119 addMeasurement(channels, types, CHANNEL_MIN_TEMP_THIS_WEEK, MIN_TEMP);
120 addMeasurement(channels, types, CHANNEL_MAX_TEMP_THIS_WEEK, MAX_TEMP);
121 addMeasurement(channels, types, CHANNEL_DATE_MIN_CO2_THIS_WEEK, DATE_MIN_CO2);
122 addMeasurement(channels, types, CHANNEL_DATE_MAX_CO2_THIS_WEEK, DATE_MAX_CO2);
123 addMeasurement(channels, types, CHANNEL_DATE_MIN_HUMIDITY_THIS_WEEK, DATE_MIN_HUM);
124 addMeasurement(channels, types, CHANNEL_DATE_MAX_HUMIDITY_THIS_WEEK, DATE_MAX_HUM);
125 addMeasurement(channels, types, CHANNEL_DATE_MIN_NOISE_THIS_WEEK, DATE_MIN_NOISE);
126 addMeasurement(channels, types, CHANNEL_DATE_MAX_NOISE_THIS_WEEK, DATE_MAX_NOISE);
127 addMeasurement(channels, types, CHANNEL_DATE_MIN_PRESSURE_THIS_WEEK, DATE_MIN_PRESSURE);
128 addMeasurement(channels, types, CHANNEL_DATE_MAX_PRESSURE_THIS_WEEK, DATE_MAX_PRESSURE);
129 addMeasurement(channels, types, CHANNEL_DATE_MIN_TEMP_THIS_WEEK, DATE_MIN_TEMP);
130 addMeasurement(channels, types, CHANNEL_DATE_MAX_TEMP_THIS_WEEK, DATE_MAX_TEMP);
131 if (!channels.isEmpty()) {
132 getMeasurements(getId(), null, ONE_WEEK, types, channels, channelMeasurements);
136 private void updateMonthMeasurements() {
137 List<String> channels = new ArrayList<>();
138 List<String> types = new ArrayList<>();
139 addMeasurement(channels, types, CHANNEL_MIN_CO2_THIS_MONTH, MIN_CO2);
140 addMeasurement(channels, types, CHANNEL_MAX_CO2_THIS_MONTH, MAX_CO2);
141 addMeasurement(channels, types, CHANNEL_MIN_HUMIDITY_THIS_MONTH, MIN_HUM);
142 addMeasurement(channels, types, CHANNEL_MAX_HUMIDITY_THIS_MONTH, MAX_HUM);
143 addMeasurement(channels, types, CHANNEL_MIN_NOISE_THIS_MONTH, MIN_NOISE);
144 addMeasurement(channels, types, CHANNEL_MAX_NOISE_THIS_MONTH, MAX_NOISE);
145 addMeasurement(channels, types, CHANNEL_MIN_PRESSURE_THIS_MONTH, MIN_PRESSURE);
146 addMeasurement(channels, types, CHANNEL_MAX_PRESSURE_THIS_MONTH, MAX_PRESSURE);
147 addMeasurement(channels, types, CHANNEL_MIN_TEMP_THIS_MONTH, MIN_TEMP);
148 addMeasurement(channels, types, CHANNEL_MAX_TEMP_THIS_MONTH, MAX_TEMP);
149 addMeasurement(channels, types, CHANNEL_DATE_MIN_CO2_THIS_MONTH, DATE_MIN_CO2);
150 addMeasurement(channels, types, CHANNEL_DATE_MAX_CO2_THIS_MONTH, DATE_MAX_CO2);
151 addMeasurement(channels, types, CHANNEL_DATE_MIN_HUMIDITY_THIS_MONTH, DATE_MIN_HUM);
152 addMeasurement(channels, types, CHANNEL_DATE_MAX_HUMIDITY_THIS_MONTH, DATE_MAX_HUM);
153 addMeasurement(channels, types, CHANNEL_DATE_MIN_NOISE_THIS_MONTH, DATE_MIN_NOISE);
154 addMeasurement(channels, types, CHANNEL_DATE_MAX_NOISE_THIS_MONTH, DATE_MAX_NOISE);
155 addMeasurement(channels, types, CHANNEL_DATE_MIN_PRESSURE_THIS_MONTH, DATE_MIN_PRESSURE);
156 addMeasurement(channels, types, CHANNEL_DATE_MAX_PRESSURE_THIS_MONTH, DATE_MAX_PRESSURE);
157 addMeasurement(channels, types, CHANNEL_DATE_MIN_TEMP_THIS_MONTH, DATE_MIN_TEMP);
158 addMeasurement(channels, types, CHANNEL_DATE_MAX_TEMP_THIS_MONTH, DATE_MAX_TEMP);
159 if (!channels.isEmpty()) {
160 getMeasurements(getId(), null, ONE_MONTH, types, channels, channelMeasurements);
165 protected State getNAThingProperty(String channelId) {
166 NADashboardData dashboardData = getDevice().map(d -> d.getDashboardData()).orElse(null);
167 if (dashboardData != null) {
170 return toQuantityType(dashboardData.getCo2(), API_CO2_UNIT);
171 case CHANNEL_TEMPERATURE:
172 return toQuantityType(dashboardData.getTemperature(), API_TEMPERATURE_UNIT);
173 case CHANNEL_MIN_TEMP:
174 return toQuantityType(dashboardData.getMinTemp(), API_TEMPERATURE_UNIT);
175 case CHANNEL_MAX_TEMP:
176 return toQuantityType(dashboardData.getMaxTemp(), API_TEMPERATURE_UNIT);
177 case CHANNEL_TEMP_TREND:
178 return toStringType(dashboardData.getTempTrend());
180 return toQuantityType(dashboardData.getNoise(), API_NOISE_UNIT);
181 case CHANNEL_PRESSURE:
182 return toQuantityType(dashboardData.getPressure(), API_PRESSURE_UNIT);
183 case CHANNEL_PRESS_TREND:
184 return toStringType(dashboardData.getPressureTrend());
185 case CHANNEL_ABSOLUTE_PRESSURE:
186 return toQuantityType(dashboardData.getAbsolutePressure(), API_PRESSURE_UNIT);
187 case CHANNEL_TIMEUTC:
188 return toDateTimeType(dashboardData.getTimeUtc(), timeZoneProvider.getTimeZone());
189 case CHANNEL_DATE_MIN_TEMP:
190 return toDateTimeType(dashboardData.getDateMinTemp(), timeZoneProvider.getTimeZone());
191 case CHANNEL_DATE_MAX_TEMP:
192 return toDateTimeType(dashboardData.getDateMaxTemp(), timeZoneProvider.getTimeZone());
193 case CHANNEL_HUMIDITY:
194 return toQuantityType(dashboardData.getHumidity(), API_HUMIDITY_UNIT);
195 case CHANNEL_HUMIDEX:
196 return toDecimalType(
197 WeatherUtils.getHumidex(dashboardData.getTemperature(), dashboardData.getHumidity()));
198 case CHANNEL_HEATINDEX:
199 return toQuantityType(
200 WeatherUtils.getHeatIndex(dashboardData.getTemperature(), dashboardData.getHumidity()),
201 API_TEMPERATURE_UNIT);
202 case CHANNEL_DEWPOINT:
203 return toQuantityType(
204 WeatherUtils.getDewPoint(dashboardData.getTemperature(), dashboardData.getHumidity()),
205 API_TEMPERATURE_UNIT);
206 case CHANNEL_DEWPOINTDEP:
207 Double dewPoint = WeatherUtils.getDewPoint(dashboardData.getTemperature(),
208 dashboardData.getHumidity());
209 return toQuantityType(WeatherUtils.getDewPointDep(dashboardData.getTemperature(), dewPoint),
210 API_TEMPERATURE_UNIT);
215 case CHANNEL_MIN_CO2:
216 case CHANNEL_MIN_CO2_THIS_WEEK:
217 case CHANNEL_MIN_CO2_THIS_MONTH:
218 case CHANNEL_MAX_CO2:
219 case CHANNEL_MAX_CO2_THIS_WEEK:
220 case CHANNEL_MAX_CO2_THIS_MONTH:
221 return toQuantityType(channelMeasurements.get(channelId), API_CO2_UNIT);
222 case CHANNEL_MIN_HUMIDITY:
223 case CHANNEL_MIN_HUMIDITY_THIS_WEEK:
224 case CHANNEL_MIN_HUMIDITY_THIS_MONTH:
225 case CHANNEL_MAX_HUMIDITY:
226 case CHANNEL_MAX_HUMIDITY_THIS_WEEK:
227 case CHANNEL_MAX_HUMIDITY_THIS_MONTH:
228 return toQuantityType(channelMeasurements.get(channelId), API_HUMIDITY_UNIT);
229 case CHANNEL_MIN_NOISE:
230 case CHANNEL_MIN_NOISE_THIS_WEEK:
231 case CHANNEL_MIN_NOISE_THIS_MONTH:
232 case CHANNEL_MAX_NOISE:
233 case CHANNEL_MAX_NOISE_THIS_WEEK:
234 case CHANNEL_MAX_NOISE_THIS_MONTH:
235 return toQuantityType(channelMeasurements.get(channelId), API_NOISE_UNIT);
236 case CHANNEL_MIN_PRESSURE:
237 case CHANNEL_MIN_PRESSURE_THIS_WEEK:
238 case CHANNEL_MIN_PRESSURE_THIS_MONTH:
239 case CHANNEL_MAX_PRESSURE:
240 case CHANNEL_MAX_PRESSURE_THIS_WEEK:
241 case CHANNEL_MAX_PRESSURE_THIS_MONTH:
242 return toQuantityType(channelMeasurements.get(channelId), API_PRESSURE_UNIT);
243 case CHANNEL_MIN_TEMP_THIS_WEEK:
244 case CHANNEL_MIN_TEMP_THIS_MONTH:
245 case CHANNEL_MAX_TEMP_THIS_WEEK:
246 case CHANNEL_MAX_TEMP_THIS_MONTH:
247 return toQuantityType(channelMeasurements.get(channelId), API_TEMPERATURE_UNIT);
248 case CHANNEL_DATE_MIN_CO2:
249 case CHANNEL_DATE_MIN_CO2_THIS_WEEK:
250 case CHANNEL_DATE_MIN_CO2_THIS_MONTH:
251 case CHANNEL_DATE_MAX_CO2:
252 case CHANNEL_DATE_MAX_CO2_THIS_WEEK:
253 case CHANNEL_DATE_MAX_CO2_THIS_MONTH:
254 case CHANNEL_DATE_MIN_NOISE:
255 case CHANNEL_DATE_MIN_NOISE_THIS_WEEK:
256 case CHANNEL_DATE_MIN_NOISE_THIS_MONTH:
257 case CHANNEL_DATE_MAX_NOISE:
258 case CHANNEL_DATE_MAX_NOISE_THIS_WEEK:
259 case CHANNEL_DATE_MAX_NOISE_THIS_MONTH:
260 case CHANNEL_DATE_MIN_HUMIDITY:
261 case CHANNEL_DATE_MIN_HUMIDITY_THIS_WEEK:
262 case CHANNEL_DATE_MIN_HUMIDITY_THIS_MONTH:
263 case CHANNEL_DATE_MAX_HUMIDITY:
264 case CHANNEL_DATE_MAX_HUMIDITY_THIS_WEEK:
265 case CHANNEL_DATE_MAX_HUMIDITY_THIS_MONTH:
266 case CHANNEL_DATE_MIN_PRESSURE:
267 case CHANNEL_DATE_MIN_PRESSURE_THIS_WEEK:
268 case CHANNEL_DATE_MIN_PRESSURE_THIS_MONTH:
269 case CHANNEL_DATE_MAX_PRESSURE:
270 case CHANNEL_DATE_MAX_PRESSURE_THIS_WEEK:
271 case CHANNEL_DATE_MAX_PRESSURE_THIS_MONTH:
272 case CHANNEL_DATE_MIN_TEMP_THIS_WEEK:
273 case CHANNEL_DATE_MIN_TEMP_THIS_MONTH:
274 case CHANNEL_DATE_MAX_TEMP_THIS_WEEK:
275 case CHANNEL_DATE_MAX_TEMP_THIS_MONTH:
276 return toDateTimeType(channelMeasurements.get(channelId), timeZoneProvider.getTimeZone());
279 return super.getNAThingProperty(channelId);
283 protected Optional<Integer> getDataTimestamp() {
284 return getDevice().map(d -> d.getLastStatusStore());
288 protected boolean isReachable() {
289 boolean result = false;
290 Optional<NAMain> device = getDevice();
291 if (device.isPresent()) {
292 Boolean reachable = device.get().isReachable();
293 result = reachable != null ? reachable.booleanValue() : false;