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.openthermgateway.handler;
15 import java.util.concurrent.TimeUnit;
17 import javax.measure.Unit;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.openthermgateway.OpenThermGatewayBindingConstants;
22 import org.openhab.binding.openthermgateway.internal.DataItem;
23 import org.openhab.binding.openthermgateway.internal.DataItemGroup;
24 import org.openhab.binding.openthermgateway.internal.GatewayCommand;
25 import org.openhab.binding.openthermgateway.internal.GatewayCommandCode;
26 import org.openhab.binding.openthermgateway.internal.Message;
27 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayCallback;
28 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConfiguration;
29 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConnector;
30 import org.openhab.binding.openthermgateway.internal.OpenThermGatewaySocketConnector;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.unit.SIUnits;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseThingHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.UnDefType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
49 * sent to one of the channels.
51 * @author Arjen Korevaar - Initial contribution
54 public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThermGatewayCallback {
56 private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
58 private @Nullable OpenThermGatewayConfiguration config;
60 private @Nullable OpenThermGatewayConnector connector;
62 private boolean connecting = false;
64 private boolean explicitDisconnect = false;
66 public OpenThermGatewayHandler(Thing thing) {
71 public void initialize() {
72 logger.debug("Initializing OpenTherm Gateway handler for uid '{}'", getThing().getUID());
74 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Initializing");
76 config = getConfigAs(OpenThermGatewayConfiguration.class);
82 public void handleCommand(ChannelUID channelUID, Command command) {
83 logger.debug("Received channel: {}, command: {}", channelUID, command);
85 if (!(command instanceof RefreshType)) {
86 String channel = channelUID.getId();
87 String code = getGatewayCodeFromChannel(channel);
89 GatewayCommand gatewayCommand = null;
91 if (command instanceof OnOffType) {
92 OnOffType onOff = (OnOffType) command;
93 gatewayCommand = GatewayCommand.parse(code, onOff == OnOffType.ON ? "1" : "0");
95 if (command instanceof QuantityType<?>) {
96 QuantityType<?> quantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
98 if (quantityType != null) {
99 double value = quantityType.doubleValue();
100 gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
104 if (gatewayCommand == null) {
105 gatewayCommand = GatewayCommand.parse(code, command.toFullString());
108 if (checkConnection()) {
110 OpenThermGatewayConnector conn = connector;
113 conn.sendCommand(gatewayCommand);
115 if (code == GatewayCommandCode.ControlSetpoint) {
116 if (gatewayCommand.getMessage().equals("0.0")) {
118 OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
121 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
122 OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
130 public void connecting() {
132 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Connecting");
136 public void connected() {
138 updateStatus(ThingStatus.ONLINE);
142 public void disconnected() {
144 OpenThermGatewayConnector conn = connector;
147 OpenThermGatewayConfiguration conf = config;
151 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected");
153 // retry connection if disconnect is not explicitly requested
154 if (conf != null && !explicitDisconnect && conf.connectionRetryInterval > 0) {
155 scheduler.schedule(() -> {
156 if (conn != null && !connecting && !conn.isConnected()) {
159 }, conf.connectionRetryInterval, TimeUnit.SECONDS);
164 public void receiveMessage(Message message) {
165 if (DataItemGroup.dataItemGroups.containsKey(message.getID())) {
166 DataItem[] dataItems = DataItemGroup.dataItemGroups.get(message.getID());
168 for (DataItem dataItem : dataItems) {
169 String channelId = dataItem.getSubject();
171 if (!OpenThermGatewayBindingConstants.SUPPORTED_CHANNEL_IDS.contains(channelId)
172 || (dataItem.getFilteredCode() != null && dataItem.getFilteredCode() != message.getCode())) {
178 switch (dataItem.getDataType()) {
180 state = OnOffType.from(message.getBit(dataItem.getByteType(), dataItem.getBitPos()));
184 state = new DecimalType(message.getUInt(dataItem.getByteType()));
188 state = new DecimalType(message.getInt(dataItem.getByteType()));
191 float value = message.getFloat();
193 Unit<?> unit = dataItem.getUnit();
194 state = (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit);
201 logger.debug("Received update for channel '{}': {}", channelId, state);
202 updateState(channelId, state);
209 public void handleRemoval() {
210 logger.debug("Removing OpenTherm Gateway handler");
212 super.handleRemoval();
216 public void dispose() {
221 private boolean checkConnection() {
223 OpenThermGatewayConnector conn = connector;
225 if (conn != null && conn.isConnected()) {
232 private boolean connect() {
234 OpenThermGatewayConfiguration conf = config;
239 logger.debug("Starting OpenTherm Gateway connector");
241 explicitDisconnect = false;
243 connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port);
245 Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread");
246 thread.setDaemon(true);
249 logger.debug("OpenTherm Gateway connector started");
257 private void disconnect() {
259 OpenThermGatewayConnector conn = connector;
262 if (conn.isConnected()) {
263 logger.debug("Stopping OpenTherm Gateway connector");
265 explicitDisconnect = true;
273 private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
275 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
276 return GatewayCommandCode.TemperatureTemporary;
277 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
278 return GatewayCommandCode.TemperatureConstant;
279 case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
280 return GatewayCommandCode.TemperatureOutside;
281 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
282 return GatewayCommandCode.SetpointWater;
283 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
284 return GatewayCommandCode.ControlSetpoint;
285 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
286 return GatewayCommandCode.CentralHeating;
287 case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
290 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));