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