2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ecotouch.internal;
15 import static org.openhab.core.library.unit.SIUnits.*;
16 import static org.openhab.core.library.unit.Units.*;
18 import java.io.IOException;
19 import java.math.BigDecimal;
20 import java.math.RoundingMode;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
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;
41 * The {@link EcoTouchHandler} is responsible for handling commands, which are
42 * sent to one of the channels.
44 * @author Sebastian Held - Initial contribution
47 public class EcoTouchHandler extends BaseThingHandler {
49 private @Nullable EcoTouchConnector connector = null;
51 private final Logger logger = LoggerFactory.getLogger(EcoTouchHandler.class);
53 private @Nullable EcoTouchConfiguration config = null;
55 private @Nullable ScheduledFuture<?> refreshJob = null;
57 public EcoTouchHandler(Thing thing) {
61 private void updateChannel(String tag, String value_str) {
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);
74 if (ecoTouchTag.getUnit() != ONE) {
75 // this is a quantity type
76 QuantityType<?> quantity = new QuantityType<>(value, ecoTouchTag.getUnit());
77 updateState(channel, quantity);
79 DecimalType number = new DecimalType(value);
80 updateState(channel, number);
84 } catch (Exception e) {
89 public void handleCommand(ChannelUID channelUID, Command command) {
90 var localConnector = connector;
91 if (localConnector == null) {
92 // the binding was not initialized, yet
95 if (command instanceof RefreshType) {
96 // request a refresh of a channel
98 EcoTouchTags tag = EcoTouchTags.fromString(channelUID.getId());
100 String valueStr = localConnector.getValue(tag.getTagName());
101 updateChannel(tag.getTagName(), valueStr);
102 updateStatus(ThingStatus.ONLINE);
104 } catch (Exception e) {
107 // send command to heat pump
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);
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);
127 logger.debug("handleCommand: requires a QuantityType");
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);
138 logger.debug("cannot convert {} to a DecimalType", state);
142 } catch (Exception e) {
143 logger.debug("handleCommand: {}", e.toString());
149 public void initialize() {
150 config = getConfigAs(EcoTouchConfiguration.class);
152 var localConfig = config;
153 if (localConfig == null) {
154 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
158 connector = new EcoTouchConnector(localConfig.ip, localConfig.username, localConfig.password);
160 scheduler.execute(() -> {
162 // try to get a single value
163 var localConnector = connector;
164 if (localConnector == null) {
165 updateStatus(ThingStatus.OFFLINE);
168 localConnector.getValue("A1");
169 } catch (IOException io) {
170 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
172 } catch (Exception e) {
173 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
177 updateStatus(ThingStatus.ONLINE);
180 // start refresh handler
181 startAutomaticRefresh();
184 private void startAutomaticRefresh() {
185 var localRefreshJob = refreshJob;
186 if (localRefreshJob == null || localRefreshJob.isCancelled()) {
187 Runnable runnable = () -> {
189 Set<String> tags = new HashSet<String>();
190 for (EcoTouchTags ecoTouchTag : EcoTouchTags.values()) {
191 String channel = ecoTouchTag.getCommand();
192 boolean linked = isLinked(channel);
194 tags.add(ecoTouchTag.getTagName());
196 var localConnector = connector;
197 if (localConnector != null) {
198 Map<String, String> result = localConnector.getValues(tags);
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());
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());
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);
219 var localConfig = config;
220 if (localConfig != null)
221 refreshJob = scheduler.scheduleWithFixedDelay(runnable, 10, localConfig.refresh, TimeUnit.SECONDS);
226 public void dispose() {
227 var localRefreshJob = refreshJob;
228 if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
229 localRefreshJob.cancel(true);
230 localRefreshJob = null;
232 var localConnector = connector;
233 if (localConnector != null)
234 localConnector.logout();