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