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 = (int) (rawUnit.doubleValue() * ecoTouchTag.getDivisor());
123 localConnector.setValue(ecoTouchTag.getTagName(), raw);
126 logger.debug("handleCommand: requires a QuantityType");
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);
137 logger.debug("cannot convert {} to a DecimalType", state);
141 } catch (Exception e) {
142 logger.debug("handleCommand: {}", e.toString());
148 public void initialize() {
149 config = getConfigAs(EcoTouchConfiguration.class);
151 var localConfig = config;
152 if (localConfig == null) {
153 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
157 connector = new EcoTouchConnector(localConfig.ip, localConfig.username, localConfig.password);
159 scheduler.execute(() -> {
161 // try to get a single value
162 var localConnector = connector;
163 if (localConnector == null) {
164 updateStatus(ThingStatus.OFFLINE);
167 localConnector.getValue("A1");
168 } catch (IOException io) {
169 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, io.toString());
171 } catch (Exception e) {
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.toString());
176 updateStatus(ThingStatus.ONLINE);
179 // start refresh handler
180 startAutomaticRefresh();
183 private void startAutomaticRefresh() {
184 var localRefreshJob = refreshJob;
185 if (localRefreshJob == null || localRefreshJob.isCancelled()) {
186 Runnable runnable = () -> {
188 Set<String> tags = new HashSet<String>();
189 for (EcoTouchTags ecoTouchTag : EcoTouchTags.values()) {
190 String channel = ecoTouchTag.getCommand();
191 boolean linked = isLinked(channel);
193 tags.add(ecoTouchTag.getTagName());
195 var localConnector = connector;
196 if (localConnector != null) {
197 Map<String, String> result = localConnector.getValues(tags);
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());
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());
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);
218 var localConfig = config;
219 if (localConfig != null)
220 refreshJob = scheduler.scheduleWithFixedDelay(runnable, 10, localConfig.refresh, TimeUnit.SECONDS);
225 public void dispose() {
226 var localRefreshJob = refreshJob;
227 if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
228 localRefreshJob.cancel(true);
229 localRefreshJob = null;
231 var localConnector = connector;
232 if (localConnector != null)
233 localConnector.logout();