]> git.basschouten.com Git - openhab-addons.git/blob
c1c2974280edfbaac5c41dca00af64492a65fbad
[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.netatmo.internal.handler;
14
15 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
16 import static org.openhab.core.library.unit.MetricPrefix.*;
17
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21
22 import javax.measure.Unit;
23 import javax.measure.quantity.Angle;
24 import javax.measure.quantity.Dimensionless;
25 import javax.measure.quantity.Length;
26 import javax.measure.quantity.Pressure;
27 import javax.measure.quantity.Speed;
28 import javax.measure.quantity.Temperature;
29
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.openhab.binding.netatmo.internal.channelhelper.BatteryHelper;
33 import org.openhab.binding.netatmo.internal.channelhelper.RadioHelper;
34 import org.openhab.core.config.core.Configuration;
35 import org.openhab.core.i18n.TimeZoneProvider;
36 import org.openhab.core.library.unit.SIUnits;
37 import org.openhab.core.library.unit.Units;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.ThingStatusInfo;
44 import org.openhab.core.thing.binding.BaseThingHandler;
45 import org.openhab.core.thing.binding.BridgeHandler;
46 import org.openhab.core.thing.type.ChannelKind;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.RefreshType;
49 import org.openhab.core.types.State;
50 import org.openhab.core.types.UnDefType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * {@link AbstractNetatmoThingHandler} is the abstract class that handles
56  * common behaviors of all netatmo things
57  *
58  * @author GaĆ«l L'hopital - Initial contribution OH2 version
59  * @author Rob Nielsen - Added day, week, and month measurements to the weather station and modules
60  *
61  */
62 @NonNullByDefault
63 public abstract class AbstractNetatmoThingHandler extends BaseThingHandler {
64     // Units of measurement of the data delivered by the API
65     public static final Unit<Temperature> API_TEMPERATURE_UNIT = SIUnits.CELSIUS;
66     public static final Unit<Dimensionless> API_HUMIDITY_UNIT = Units.PERCENT;
67     public static final Unit<Pressure> API_PRESSURE_UNIT = HECTO(SIUnits.PASCAL);
68     public static final Unit<Speed> API_WIND_SPEED_UNIT = SIUnits.KILOMETRE_PER_HOUR;
69     public static final Unit<Angle> API_WIND_DIRECTION_UNIT = Units.DEGREE_ANGLE;
70     public static final Unit<Length> API_RAIN_UNIT = MILLI(SIUnits.METRE);
71     public static final Unit<Dimensionless> API_CO2_UNIT = Units.PARTS_PER_MILLION;
72     public static final Unit<Dimensionless> API_NOISE_UNIT = Units.DECIBEL;
73
74     private final Logger logger = LoggerFactory.getLogger(AbstractNetatmoThingHandler.class);
75
76     protected final TimeZoneProvider timeZoneProvider;
77     private @Nullable RadioHelper radioHelper;
78     private @Nullable BatteryHelper batteryHelper;
79     protected @Nullable Configuration config;
80     private @Nullable NetatmoBridgeHandler bridgeHandler;
81
82     AbstractNetatmoThingHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
83         super(thing);
84         this.timeZoneProvider = timeZoneProvider;
85     }
86
87     @Override
88     public void initialize() {
89         logger.debug("initializing handler for thing {}", getThing().getUID());
90         Bridge bridge = getBridge();
91         initializeThing(bridge != null ? bridge.getStatus() : null);
92     }
93
94     @Override
95     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
96         logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
97         initializeThing(bridgeStatusInfo.getStatus());
98     }
99
100     private void initializeThing(@Nullable ThingStatus bridgeStatus) {
101         Bridge bridge = getBridge();
102         BridgeHandler bridgeHandler = bridge != null ? bridge.getHandler() : null;
103         if (bridgeHandler != null && bridgeStatus != null) {
104             if (bridgeStatus == ThingStatus.ONLINE) {
105                 config = getThing().getConfiguration();
106
107                 String signalLevel = thing.getProperties().get(PROPERTY_SIGNAL_LEVELS);
108                 radioHelper = signalLevel != null ? new RadioHelper(signalLevel) : null;
109                 String batteryLevel = thing.getProperties().get(PROPERTY_BATTERY_LEVELS);
110                 batteryHelper = batteryLevel != null ? new BatteryHelper(batteryLevel) : null;
111                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Pending parent object initialization");
112
113                 initializeThing();
114             } else {
115                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
116             }
117         } else {
118             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
119         }
120     }
121
122     protected abstract void initializeThing();
123
124     protected State getNAThingProperty(String channelId) {
125         Optional<State> result = getBatteryHelper().flatMap(helper -> helper.getNAThingProperty(channelId));
126         if (result.isPresent()) {
127             return result.get();
128         }
129         result = getRadioHelper().flatMap(helper -> helper.getNAThingProperty(channelId));
130         if (result.isPresent()) {
131             return result.get();
132         }
133         return UnDefType.UNDEF;
134     }
135
136     protected void updateChannels() {
137         if (thing.getStatus() != ThingStatus.ONLINE) {
138             return;
139         }
140
141         updateDataChannels();
142
143         triggerEventChannels();
144     }
145
146     private void updateDataChannels() {
147         getThing().getChannels().stream()
148                 .filter(channel -> !ChannelKind.TRIGGER.equals(channel.getKind()) && isLinked(channel.getUID()))
149                 .map(channel -> channel.getUID()).forEach(this::updateChannel);
150     }
151
152     private void updateChannel(ChannelUID channelUID) {
153         updateState(channelUID, getNAThingProperty(channelUID.getId()));
154     }
155
156     /**
157      * Triggers all event/trigger channels
158      * (when a channel is triggered, a rule can get all other information from the updated non-trigger channels)
159      */
160     private void triggerEventChannels() {
161         getThing().getChannels().stream().filter(channel -> ChannelKind.TRIGGER.equals(channel.getKind()))
162                 .map(channel -> channel.getUID().getId()).forEach(this::triggerChannelIfRequired);
163     }
164
165     /**
166      * Triggers the trigger channel with the given channel id when required (when an update is available)
167      *
168      * @param channelId channel id
169      */
170     protected void triggerChannelIfRequired(String channelId) {
171     }
172
173     @Override
174     public void handleCommand(ChannelUID channelUID, Command command) {
175         if (command == RefreshType.REFRESH) {
176             logger.debug("Refreshing '{}'", channelUID);
177             updateChannel(channelUID);
178         }
179     }
180
181     protected Optional<NetatmoBridgeHandler> getBridgeHandler() {
182         if (bridgeHandler == null) {
183             Bridge bridge = getBridge();
184             if (bridge != null) {
185                 bridgeHandler = (NetatmoBridgeHandler) bridge.getHandler();
186             }
187         }
188         NetatmoBridgeHandler handler = bridgeHandler;
189         return handler != null ? Optional.of(handler) : Optional.empty();
190     }
191
192     protected Optional<AbstractNetatmoThingHandler> findNAThing(@Nullable String searchedId) {
193         return getBridgeHandler().flatMap(handler -> handler.findNAThing(searchedId));
194     }
195
196     public boolean matchesId(@Nullable String searchedId) {
197         return searchedId != null && searchedId.equalsIgnoreCase(getId());
198     }
199
200     protected @Nullable String getId() {
201         Configuration conf = config;
202         Object equipmentId = conf != null ? conf.get(EQUIPMENT_ID) : null;
203         if (equipmentId instanceof String) {
204             return ((String) equipmentId).toLowerCase();
205         }
206         return null;
207     }
208
209     protected void updateProperties(@Nullable Integer firmware, @Nullable String modelId) {
210         Map<String, String> properties = editProperties();
211         if (firmware != null || modelId != null) {
212             properties.put(Thing.PROPERTY_VENDOR, VENDOR);
213         }
214         if (firmware != null) {
215             properties.put(Thing.PROPERTY_FIRMWARE_VERSION, firmware.toString());
216         }
217         if (modelId != null) {
218             properties.put(Thing.PROPERTY_MODEL_ID, modelId);
219         }
220         updateProperties(properties);
221     }
222
223     protected Optional<RadioHelper> getRadioHelper() {
224         RadioHelper helper = radioHelper;
225         return helper != null ? Optional.of(helper) : Optional.empty();
226     }
227
228     protected Optional<BatteryHelper> getBatteryHelper() {
229         BatteryHelper helper = batteryHelper;
230         return helper != null ? Optional.of(helper) : Optional.empty();
231     }
232
233     public void updateMeasurements() {
234     }
235
236     public void getMeasurements(@Nullable String device, @Nullable String module, String scale, List<String> types,
237             List<String> channels, Map<String, Float> channelMeasurements) {
238         Optional<NetatmoBridgeHandler> handler = getBridgeHandler();
239         if (!handler.isPresent() || device == null) {
240             return;
241         }
242
243         if (types.size() != channels.size()) {
244             throw new IllegalArgumentException("types and channels lists are different sizes.");
245         }
246
247         List<Float> measurements = handler.get().getStationMeasureResponses(device, module, scale, types);
248         if (measurements.size() != types.size()) {
249             throw new IllegalArgumentException("types and measurements lists are different sizes.");
250         }
251
252         int i = 0;
253         for (Float measurement : measurements) {
254             channelMeasurements.put(channels.get(i++), measurement);
255         }
256     }
257
258     public void addMeasurement(List<String> channels, List<String> types, String channel, String type) {
259         if (isLinked(channel)) {
260             channels.add(channel);
261             types.add(type);
262         }
263     }
264
265     protected boolean isReachable() {
266         return true;
267     }
268 }