]> git.basschouten.com Git - openhab-addons.git/blob
006f151b59af93b321f8cf3ee7f9817714bd0644
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.foobot.internal.handler;
14
15 import java.math.BigDecimal;
16 import java.time.Duration;
17 import java.time.Instant;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.util.Map;
21
22 import javax.measure.Unit;
23
24 import org.apache.commons.lang.StringUtils;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.foobot.internal.FoobotApiConnector;
28 import org.openhab.binding.foobot.internal.FoobotApiException;
29 import org.openhab.binding.foobot.internal.FoobotBindingConstants;
30 import org.openhab.binding.foobot.internal.json.FoobotDevice;
31 import org.openhab.binding.foobot.internal.json.FoobotJsonData;
32 import org.openhab.binding.foobot.internal.json.FoobotSensor;
33 import org.openhab.core.cache.ExpiringCache;
34 import org.openhab.core.library.types.DateTimeType;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.QuantityType;
37 import org.openhab.core.thing.Channel;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.binding.BaseThingHandler;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
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 /**
51  * The {@link FoobotDeviceHandler} is responsible for handling commands, which are sent to one of the channels.
52  *
53  * @author Divya Chauhan - Initial contribution
54  * @author George Katsis - Add Bridge thing type
55  *
56  */
57 @NonNullByDefault
58 public class FoobotDeviceHandler extends BaseThingHandler {
59
60     private final Logger logger = LoggerFactory.getLogger(FoobotDeviceHandler.class);
61     private final FoobotApiConnector connector;
62
63     private @NonNullByDefault({}) ExpiringCache<FoobotJsonData> dataCache;
64     private String uuid = "";
65
66     public FoobotDeviceHandler(final Thing thing, final FoobotApiConnector connector) {
67         super(thing);
68         this.connector = connector;
69     }
70
71     @Override
72     public void handleCommand(final ChannelUID channelUID, final Command command) {
73         if (command instanceof RefreshType) {
74             final FoobotJsonData sensorData = dataCache.getValue();
75
76             if (sensorData != null) {
77                 updateState(channelUID, sensorDataToState(channelUID.getId(), sensorData));
78             }
79         } else {
80             logger.debug("The Foobot binding is read-only and can not handle command {}", command);
81         }
82     }
83
84     /**
85      * @return Returns the uuid associated with this device.
86      */
87     public String getUuid() {
88         return uuid;
89     }
90
91     @Override
92     public void initialize() {
93         logger.debug("Initializing Foobot handler.");
94         uuid = (String) getConfig().get(FoobotBindingConstants.CONFIG_UUID);
95
96         if (StringUtils.trimToNull(uuid) == null) {
97             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
98                     "Parameter 'uuid' is mandatory and must be configured");
99             return;
100         }
101         final FoobotAccountHandler bridgeHandler = getBridgeHandler();
102         final int refreshInterval = bridgeHandler == null ? FoobotBindingConstants.DEFAULT_REFRESH_PERIOD_MINUTES
103                 : bridgeHandler.getRefreshInterval();
104
105         dataCache = new ExpiringCache<>(Duration.ofMinutes(refreshInterval), this::retrieveSensorData);
106         scheduler.execute(this::refreshSensors);
107     }
108
109     /**
110      * Updates the thing properties as retrieved by the bridge.
111      *
112      * @param foobot device parameters.
113      */
114     public void handleUpdateProperties(final FoobotDevice foobot) {
115         final Map<String, String> properties = editProperties();
116
117         properties.put(Thing.PROPERTY_MAC_ADDRESS, foobot.getMac());
118         properties.put(FoobotBindingConstants.PROPERTY_NAME, foobot.getName());
119         updateProperties(properties);
120     }
121
122     /**
123      * Calls the footbot api to retrieve the sensor data. Sets thing offline in case of errors.
124      *
125      * @return returns the retrieved sensor data or null if no data or an error occurred.
126      */
127     private @Nullable FoobotJsonData retrieveSensorData() {
128         logger.debug("Refresh sensor data for: {}", uuid);
129         FoobotJsonData sensorData = null;
130
131         try {
132             sensorData = connector.getSensorData(uuid);
133             if (sensorData == null) {
134                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "No sensor data received");
135                 return sensorData;
136             }
137         } catch (FoobotApiException e) {
138             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
139             return null;
140         } catch (RuntimeException e) {
141             logger.debug("Error requesting sensor data: ", e);
142             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
143             return null;
144         }
145         if (getThing().getStatus() != ThingStatus.ONLINE) {
146             updateStatus(ThingStatus.ONLINE);
147         }
148         final FoobotAccountHandler bridgeHandler = getBridgeHandler();
149
150         if (bridgeHandler != null) {
151             bridgeHandler.updateRemainingLimitStatus();
152         }
153         return sensorData;
154     }
155
156     /**
157      * Refreshes the device channels.
158      */
159     public void refreshSensors() {
160         final FoobotJsonData sensorData = dataCache.getValue();
161
162         if (sensorData != null) {
163             for (final Channel channel : getThing().getChannels()) {
164                 final ChannelUID channelUid = channel.getUID();
165
166                 updateState(channelUid, sensorDataToState(channelUid.getId(), sensorData));
167             }
168             updateTime(sensorData);
169         }
170     }
171
172     private void updateTime(final FoobotJsonData sensorData) {
173         final State lastTime = sensorDataToState(FoobotSensor.TIME.getChannelId(), sensorData);
174
175         if (lastTime instanceof DecimalType) {
176             ((DecimalType) lastTime).intValue();
177         }
178     }
179
180     @Override
181     public void dispose() {
182         logger.debug("Disposing the Foobot handler.");
183     }
184
185     protected State sensorDataToState(final String channelId, final FoobotJsonData data) {
186         final FoobotSensor sensor = FoobotSensor.findSensorByChannelId(channelId);
187
188         if (sensor == null || data.getSensors() == null || data.getDatapoints() == null
189                 || data.getDatapoints().isEmpty()) {
190             return UnDefType.UNDEF;
191         }
192         final int sensorIndex = data.getSensors().indexOf(sensor.getDataKey());
193
194         if (sensorIndex == -1) {
195             return UnDefType.UNDEF;
196         }
197         final String value = data.getDatapoints().get(0).get(sensorIndex);
198         final String unit = data.getUnits().get(sensorIndex);
199
200         if (value == null) {
201             return UnDefType.UNDEF;
202         } else {
203             final Unit<?> stateUnit = sensor.getUnit(unit);
204             if (sensor == FoobotSensor.TIME) {
205                 return new DateTimeType(
206                         ZonedDateTime.ofInstant(Instant.ofEpochSecond(Long.parseLong(value)), ZoneId.systemDefault()));
207             } else if (stateUnit == null) {
208                 return new DecimalType(value);
209             } else {
210                 return new QuantityType(new BigDecimal(value), stateUnit);
211             }
212         }
213     }
214
215     private @Nullable FoobotAccountHandler getBridgeHandler() {
216         return getBridge() != null && getBridge().getHandler() instanceof FoobotAccountHandler
217                 ? (FoobotAccountHandler) getBridge().getHandler()
218                 : null;
219     }
220 }