]> git.basschouten.com Git - openhab-addons.git/blob
c8b5c72c37a4a265641932acf71acf06f40056a0
[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 = (int) (rawUnit.doubleValue() * ecoTouchTag.getDivisor());
123                                 localConnector.setValue(ecoTouchTag.getTagName(), raw);
124                             }
125                         } else {
126                             logger.debug("handleCommand: requires a QuantityType");
127                         }
128                     } else {
129                         State state = (State) command;
130                         DecimalType decimalType = state.as(DecimalType.class);
131                         if (decimalType != null) {
132                             BigDecimal decimal = decimalType.toBigDecimal();
133                             decimal = decimal.multiply(new BigDecimal(ecoTouchTag.getDivisor()));
134                             int raw = decimal.intValue();
135                             localConnector.setValue(ecoTouchTag.getTagName(), raw);
136                         } else {
137                             logger.debug("cannot convert {} to a DecimalType", state);
138                         }
139                     }
140                 }
141             } catch (Exception e) {
142                 logger.debug("handleCommand: {}", e.toString());
143             }
144         }
145     }
146
147     @Override
148     public void initialize() {
149         config = getConfigAs(EcoTouchConfiguration.class);
150
151         var localConfig = config;
152         if (localConfig == null) {
153             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
154             return;
155         }
156
157         connector = new EcoTouchConnector(localConfig.ip, localConfig.username, localConfig.password);
158
159         scheduler.execute(() -> {
160             try {
161                 // try to get a single value
162                 var localConnector = connector;
163                 if (localConnector == null) {
164                     updateStatus(ThingStatus.OFFLINE);
165                     return;
166                 }
167                 localConnector.getValue("A1");
168             } catch (IOException io) {
169                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
170                 return;
171             } catch (Exception e) {
172                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
173                 return;
174             }
175
176             updateStatus(ThingStatus.ONLINE);
177         });
178
179         // start refresh handler
180         startAutomaticRefresh();
181     }
182
183     private void startAutomaticRefresh() {
184         var localRefreshJob = refreshJob;
185         if (localRefreshJob == null || localRefreshJob.isCancelled()) {
186             Runnable runnable = () -> {
187                 try {
188                     Set<String> tags = new HashSet<String>();
189                     for (EcoTouchTags ecoTouchTag : EcoTouchTags.values()) {
190                         String channel = ecoTouchTag.getCommand();
191                         boolean linked = isLinked(channel);
192                         if (linked)
193                             tags.add(ecoTouchTag.getTagName());
194                     }
195                     var localConnector = connector;
196                     if (localConnector != null) {
197                         Map<String, String> result = localConnector.getValues(tags);
198
199                         Iterator<Map.Entry<String, String>> it = result.entrySet().iterator();
200                         while (it.hasNext()) {
201                             Map.Entry<String, String> pair = it.next();
202                             updateChannel(pair.getKey(), pair.getValue());
203                         }
204                     }
205                 } catch (IOException io) {
206                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
207                 } catch (Exception e) {
208                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
209                 } catch (Error e) {
210                     // during thing creation, the following error is thrown:
211                     // java.lang.NoSuchMethodError: 'org.openhab.binding.ecotouch.internal.EcoTouchTags[]
212                     // org.openhab.binding.ecotouch.internal.EcoTouchTags.values()'
213                     // not sure why... lets ignore it for now
214                     updateStatus(ThingStatus.OFFLINE);
215                 }
216             };
217
218             var localConfig = config;
219             if (localConfig != null)
220                 refreshJob = scheduler.scheduleWithFixedDelay(runnable, 10, localConfig.refresh, TimeUnit.SECONDS);
221         }
222     }
223
224     @Override
225     public void dispose() {
226         var localRefreshJob = refreshJob;
227         if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
228             localRefreshJob.cancel(true);
229             localRefreshJob = null;
230         }
231         var localConnector = connector;
232         if (localConnector != null)
233             localConnector.logout();
234     }
235 }