2 * Copyright (c) 2010-2023 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;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
26 import java.util.concurrent.ScheduledFuture;
27 import java.util.concurrent.TimeUnit;
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;
45 * The {@link EcoTouchHandler} is responsible for handling commands, which are
46 * sent to one of the channels.
48 * @author Sebastian Held - Initial contribution
51 public class EcoTouchHandler extends BaseThingHandler {
53 private @Nullable EcoTouchConnector connector = null;
55 private final Logger logger = LoggerFactory.getLogger(EcoTouchHandler.class);
57 private @Nullable EcoTouchConfiguration config = null;
59 private @Nullable ScheduledFuture<?> refreshJob = null;
61 public EcoTouchHandler(Thing thing) {
65 private void updateChannel(String tag, String value_str) {
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);
78 if (ecoTouchTag.getUnit() != ONE) {
79 // this is a quantity type
80 QuantityType<?> quantity = new QuantityType<>(value, ecoTouchTag.getUnit());
81 updateState(channel, quantity);
83 DecimalType number = new DecimalType(value);
84 updateState(channel, number);
88 } catch (Exception e) {
93 public void handleCommand(ChannelUID channelUID, Command command) {
94 var localConnector = connector;
95 if (localConnector == null) {
96 // the binding was not initialized, yet
99 if (command instanceof RefreshType) {
100 // request a refresh of a channel
102 EcoTouchTags tag = EcoTouchTags.fromString(channelUID.getId());
104 String valueStr = localConnector.getValue(tag.getTagName());
105 updateChannel(tag.getTagName(), valueStr);
106 updateStatus(ThingStatus.ONLINE);
108 } catch (Exception e) {
111 // send command to heat pump
113 EcoTouchTags ecoTouchTag = EcoTouchTags.fromString(channelUID.getId());
114 if (ecoTouchTag == null) {
115 logger.warn("ID: {} unknown", channelUID.getId());
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);
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);
132 logger.debug("handleCommand: requires a QuantityType");
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);
143 logger.debug("cannot convert {} to a DecimalType", state);
147 } catch (Exception e) {
148 logger.debug("handleCommand: {}", e.toString());
154 public void initialize() {
155 config = getConfigAs(EcoTouchConfiguration.class);
157 var localConfig = config;
158 if (localConfig == null) {
159 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
163 connector = new EcoTouchConnector(localConfig.ip, localConfig.username, localConfig.password);
165 scheduler.execute(() -> {
167 // try to get a single value
168 var localConnector = connector;
169 if (localConnector == null) {
170 updateStatus(ThingStatus.OFFLINE);
173 localConnector.getValue("A1");
174 } catch (IOException io) {
175 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
177 } catch (Exception e) {
178 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
182 updateStatus(ThingStatus.ONLINE);
185 // start refresh handler
186 startAutomaticRefresh();
189 private void startAutomaticRefresh() {
190 var localRefreshJob = refreshJob;
191 if (localRefreshJob == null || localRefreshJob.isCancelled()) {
192 Runnable runnable = () -> {
194 Set<String> tags = new HashSet<String>();
195 for (EcoTouchTags ecoTouchTag : EcoTouchTags.values()) {
196 String channel = ecoTouchTag.getCommand();
197 boolean linked = isLinked(channel);
199 tags.add(ecoTouchTag.getTagName());
202 var localConnector = connector;
203 if (localConnector != null) {
204 Map<String, String> result = localConnector.getValues(tags);
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());
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());
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);
225 var localConfig = config;
226 if (localConfig != null) {
227 refreshJob = scheduler.scheduleWithFixedDelay(runnable, 10, localConfig.refresh, TimeUnit.SECONDS);
233 public void dispose() {
234 var localRefreshJob = refreshJob;
235 if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
236 localRefreshJob.cancel(true);
237 localRefreshJob = null;
239 var localConnector = connector;
240 if (localConnector != null) {
241 localConnector.logout();