]> git.basschouten.com Git - openhab-addons.git/blob
bbc8548c9a8bb7a381c7d6fd898184b16e7bb37d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.onewiregpio.internal.handler;
14
15 import static org.openhab.binding.onewiregpio.internal.OneWireGPIOBindingConstants.*;
16
17 import java.io.IOException;
18 import java.math.BigDecimal;
19 import java.math.RoundingMode;
20 import java.nio.file.Files;
21 import java.nio.file.InvalidPathException;
22 import java.nio.file.Paths;
23 import java.util.List;
24 import java.util.Optional;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
27 import java.util.stream.Stream;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.openhab.binding.onewiregpio.internal.OneWireGPIOBindingConstants;
31 import org.openhab.binding.onewiregpio.internal.OneWireGpioConfiguration;
32 import org.openhab.core.library.types.QuantityType;
33 import org.openhab.core.library.unit.SIUnits;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseThingHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * The {@link OneWireGPIOHandler} is responsible for handling commands, which are
47  * sent to one of the channels.
48  *
49  * @author Anatol Ogorek - Initial contribution
50  * @author Konstantin Polihronov - Changed configuration handling and added new parameter - precision
51  */
52 public class OneWireGPIOHandler extends BaseThingHandler {
53
54     private final Logger logger = LoggerFactory.getLogger(OneWireGPIOHandler.class);
55
56     private String gpioBusFile;
57     private Integer refreshTime;
58     private Integer precision;
59
60     private ScheduledFuture<?> sensorRefreshJob;
61
62     public OneWireGPIOHandler(Thing thing) {
63         super(thing);
64     }
65
66     @Override
67     public void handleCommand(ChannelUID channelUID, Command command) {
68         if (channelUID.getId().equals(TEMPERATURE)) {
69             if (command instanceof RefreshType) {
70                 publishSensorValue(channelUID);
71             } else {
72                 logger.debug("Command {} is not supported for channel: {}. Supported command: REFRESH", command,
73                         channelUID.getId());
74             }
75         }
76     }
77
78     @Override
79     public void initialize() {
80         OneWireGpioConfiguration configuration = getConfigAs(OneWireGpioConfiguration.class);
81         gpioBusFile = configuration.gpio_bus_file;
82         refreshTime = configuration.refresh_time;
83         precision = configuration.precision.intValue();
84         logger.debug("GPIO Busfile={}, RefreshTime={}, precision={}", gpioBusFile, refreshTime, precision);
85
86         if (checkConfiguration()) {
87             startAutomaticRefresh();
88             updateStatus(ThingStatus.ONLINE);
89         }
90     }
91
92     /**
93      * This method checks if the provided configuration is valid.
94      * When invalid parameter is found, default value is assigned.
95      */
96     private boolean checkConfiguration() {
97         if (StringUtils.isEmpty(gpioBusFile)) {
98             logger.debug("GPIO_BUS_FILE not set. Please check configuration, and set proper path to w1_slave file.");
99             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
100                     "The path to the w1_slave sensor data file is missing.");
101             return false;
102         }
103
104         if (refreshTime <= 0) {
105             logger.debug("Refresh time [{}] is not valid. Falling back to default value: {}.", refreshTime,
106                     DEFAULT_REFRESH_TIME);
107             refreshTime = DEFAULT_REFRESH_TIME;
108         }
109
110         if (precision < 0 || precision > MAX_PRECISION_VALUE) {
111             logger.debug(
112                     "Precision value {} is outside allowed values [0 - {}]. Falling back to maximum precision value.",
113                     precision, MAX_PRECISION_VALUE);
114             precision = MAX_PRECISION_VALUE;
115         }
116
117         return true;
118     }
119
120     private void startAutomaticRefresh() {
121         Runnable refresher = () -> {
122             List<Channel> channels = getThing().getChannels();
123             for (Channel channel : channels) {
124                 if (isLinked(channel.getUID().getId())) {
125                     publishSensorValue(channel.getUID());
126                 }
127             }
128         };
129
130         sensorRefreshJob = scheduler.scheduleWithFixedDelay(refresher, 0, refreshTime.intValue(), TimeUnit.SECONDS);
131         logger.debug("Start automatic refresh every {} seconds", refreshTime.intValue());
132     }
133
134     private void publishSensorValue(ChannelUID channelUID) {
135         String channelID = channelUID.getId();
136         switch (channelID) {
137             case TEMPERATURE:
138                 publishTemperatureSensorState(channelUID);
139                 break;
140             default:
141                 logger.debug("Can not update channel with ID : {} - channel name might be wrong!", channelID);
142                 break;
143         }
144     }
145
146     private void publishTemperatureSensorState(ChannelUID channelUID) {
147         BigDecimal temp = readSensorTemperature(gpioBusFile);
148         if (temp != null) {
149             updateState(channelUID, new QuantityType<>(temp, SIUnits.CELSIUS));
150         }
151     }
152
153     private BigDecimal readSensorTemperature(String gpioFile) {
154         try (Stream<String> stream = Files.lines(Paths.get(gpioFile))) {
155             Optional<String> temperatureLine = stream
156                     .filter(s -> s.contains(OneWireGPIOBindingConstants.FILE_TEMP_MARKER)).findFirst();
157             if (temperatureLine.isPresent()) {
158                 String line = temperatureLine.get();
159                 String tempString = line.substring(line.indexOf(OneWireGPIOBindingConstants.FILE_TEMP_MARKER)
160                         + OneWireGPIOBindingConstants.FILE_TEMP_MARKER.length());
161                 Integer intTemp = Integer.parseInt(tempString);
162                 if (getThing().getStatus() != ThingStatus.ONLINE) {
163                     updateStatus(ThingStatus.ONLINE);
164                 }
165                 return calculateValue(intTemp);
166             } else {
167                 logger.debug(
168                         "GPIO file didn't contain line with 't=' where temperature value should be available. Check if configuration points to the proper file");
169                 return null;
170             }
171         } catch (IOException | InvalidPathException e) {
172             logger.debug("error reading GPIO bus file. File path is: {}.  Check if path is proper.", gpioFile, e);
173             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error reading GPIO bus file.");
174             return null;
175         }
176     }
177
178     private BigDecimal calculateValue(Integer intTemp) {
179         BigDecimal result = BigDecimal.valueOf(intTemp).movePointLeft(3);
180         if (precision != MAX_PRECISION_VALUE) {
181             result = result.setScale(precision, RoundingMode.HALF_UP);
182         }
183         logger.debug("Thing = {}, temperature value = {}.", getThing().getUID(), result);
184         return result;
185     }
186
187     @Override
188     public void dispose() {
189         if (sensorRefreshJob != null) {
190             sensorRefreshJob.cancel(true);
191         }
192         super.dispose();
193     }
194 }