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