]> git.basschouten.com Git - openhab-addons.git/blob
f76daa485d5b7355f94287d81a21345f227cef16
[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.ecotouch.internal;
14
15 import static org.openhab.core.library.unit.SIUnits.*;
16 import static org.openhab.core.library.unit.Units.*;
17
18 import java.io.IOException;
19 import java.math.BigDecimal;
20 import java.math.RoundingMode;
21 import java.util.*;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.QuantityType;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.openhab.core.types.State;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link EcoTouchHandler} is responsible for handling commands, which are
42  * sent to one of the channels.
43  *
44  * @author Sebastian Held - Initial contribution
45  */
46 @NonNullByDefault
47 public class EcoTouchHandler extends BaseThingHandler {
48
49     private @Nullable EcoTouchConnector connector = null;
50
51     private final Logger logger = LoggerFactory.getLogger(EcoTouchHandler.class);
52
53     private @Nullable EcoTouchConfiguration config = null;
54
55     private @Nullable ScheduledFuture<?> refreshJob = null;
56
57     public EcoTouchHandler(Thing thing) {
58         super(thing);
59     }
60
61     private void updateChannel(String tag, String value_str) {
62         try {
63             List<EcoTouchTags> ecoTouchTags = EcoTouchTags.fromTag(tag);
64             for (EcoTouchTags ecoTouchTag : ecoTouchTags) {
65                 String channel = ecoTouchTag.getCommand();
66                 BigDecimal value = ecoTouchTag.decodeValue(value_str);
67                 if (ecoTouchTag == EcoTouchTags.TYPE_ADAPT_HEATING) {
68                     // this type needs special treatment
69                     // the following reads: value = value / 2 - 2
70                     value = value.divide(new BigDecimal(2), 1, RoundingMode.UNNECESSARY).subtract(new BigDecimal(2));
71                     QuantityType<?> quantity = new QuantityType<javax.measure.quantity.Temperature>(value, CELSIUS);
72                     updateState(channel, quantity);
73                 } else {
74                     if (ecoTouchTag.getUnit() != ONE) {
75                         // this is a quantity type
76                         QuantityType<?> quantity = new QuantityType<>(value, ecoTouchTag.getUnit());
77                         updateState(channel, quantity);
78                     } else {
79                         DecimalType number = new DecimalType(value);
80                         updateState(channel, number);
81                     }
82                 }
83             }
84         } catch (Exception e) {
85         }
86     }
87
88     @Override
89     public void handleCommand(ChannelUID channelUID, Command command) {
90         var localConnector = connector;
91         if (localConnector == null) {
92             // the binding was not initialized, yet
93             return;
94         }
95         if (command instanceof RefreshType) {
96             // request a refresh of a channel
97             try {
98                 EcoTouchTags tag = EcoTouchTags.fromString(channelUID.getId());
99                 if (tag != null) {
100                     String valueStr = localConnector.getValue(tag.getTagName());
101                     updateChannel(tag.getTagName(), valueStr);
102                     updateStatus(ThingStatus.ONLINE);
103                 }
104             } catch (Exception e) {
105             }
106         } else {
107             // send command to heat pump
108             try {
109                 EcoTouchTags ecoTouchTag = EcoTouchTags.fromString(channelUID.getId());
110                 if (ecoTouchTag == EcoTouchTags.TYPE_ADAPT_HEATING) {
111                     // this type needs special treatment
112                     QuantityType<?> value = (QuantityType<?>) command;
113                     int raw = Math.round(value.floatValue() * 2 + 4);
114                     localConnector.setValue(ecoTouchTag.getTagName(), raw);
115                 } else {
116                     if (ecoTouchTag.getUnit() != ONE) {
117                         if (command instanceof QuantityType) {
118                             // convert from user unit to heat pump unit
119                             QuantityType<?> value = (QuantityType<?>) command;
120                             QuantityType<?> rawUnit = value.toUnit(ecoTouchTag.getUnit());
121                             if (rawUnit != null) {
122                                 int raw = rawUnit.intValue();
123                                 raw *= ecoTouchTag.getDivisor();
124                                 localConnector.setValue(ecoTouchTag.getTagName(), raw);
125                             }
126                         } else {
127                             logger.debug("handleCommand: requires a QuantityType");
128                         }
129                     } else {
130                         State state = (State) command;
131                         DecimalType decimalType = state.as(DecimalType.class);
132                         if (decimalType != null) {
133                             BigDecimal decimal = decimalType.toBigDecimal();
134                             decimal = decimal.multiply(new BigDecimal(ecoTouchTag.getDivisor()));
135                             int raw = decimal.intValue();
136                             localConnector.setValue(ecoTouchTag.getTagName(), raw);
137                         } else {
138                             logger.debug("cannot convert {} to a DecimalType", state);
139                         }
140                     }
141                 }
142             } catch (Exception e) {
143                 logger.debug("handleCommand: {}", e.toString());
144             }
145         }
146     }
147
148     @Override
149     public void initialize() {
150         config = getConfigAs(EcoTouchConfiguration.class);
151
152         var localConfig = config;
153         if (localConfig == null) {
154             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
155             return;
156         }
157
158         connector = new EcoTouchConnector(localConfig.ip, localConfig.username, localConfig.password);
159
160         scheduler.execute(() -> {
161             try {
162                 // try to get a single value
163                 var localConnector = connector;
164                 if (localConnector == null) {
165                     updateStatus(ThingStatus.OFFLINE);
166                     return;
167                 }
168                 localConnector.getValue("A1");
169             } catch (IOException io) {
170                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
171                 return;
172             } catch (Exception e) {
173                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
174                 return;
175             }
176
177             updateStatus(ThingStatus.ONLINE);
178         });
179
180         // start refresh handler
181         startAutomaticRefresh();
182     }
183
184     private void startAutomaticRefresh() {
185         var localRefreshJob = refreshJob;
186         if (localRefreshJob == null || localRefreshJob.isCancelled()) {
187             Runnable runnable = () -> {
188                 try {
189                     Set<String> tags = new HashSet<String>();
190                     for (EcoTouchTags ecoTouchTag : EcoTouchTags.values()) {
191                         String channel = ecoTouchTag.getCommand();
192                         boolean linked = isLinked(channel);
193                         if (linked)
194                             tags.add(ecoTouchTag.getTagName());
195                     }
196                     var localConnector = connector;
197                     if (localConnector != null) {
198                         Map<String, String> result = localConnector.getValues(tags);
199
200                         Iterator<Map.Entry<String, String>> it = result.entrySet().iterator();
201                         while (it.hasNext()) {
202                             Map.Entry<String, String> pair = it.next();
203                             updateChannel(pair.getKey(), pair.getValue());
204                         }
205                     }
206                 } catch (IOException io) {
207                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
208                 } catch (Exception e) {
209                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
210                 } catch (Error e) {
211                     // during thing creation, the following error is thrown:
212                     // java.lang.NoSuchMethodError: 'org.openhab.binding.ecotouch.internal.EcoTouchTags[]
213                     // org.openhab.binding.ecotouch.internal.EcoTouchTags.values()'
214                     // not sure why... lets ignore it for now
215                     updateStatus(ThingStatus.OFFLINE);
216                 }
217             };
218
219             var localConfig = config;
220             if (localConfig != null)
221                 refreshJob = scheduler.scheduleWithFixedDelay(runnable, 10, localConfig.refresh, TimeUnit.SECONDS);
222         }
223     }
224
225     @Override
226     public void dispose() {
227         var localRefreshJob = refreshJob;
228         if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
229             localRefreshJob.cancel(true);
230             localRefreshJob = null;
231         }
232         var localConnector = connector;
233         if (localConnector != null)
234             localConnector.logout();
235     }
236 }