2 * Copyright (c) 2010-2022 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.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.binding.openthermgateway.internal.ConnectionState;
21 import org.openhab.binding.openthermgateway.internal.DataItemGroup;
22 import org.openhab.binding.openthermgateway.internal.GatewayCommand;
23 import org.openhab.binding.openthermgateway.internal.GatewayCommandCode;
24 import org.openhab.binding.openthermgateway.internal.Message;
25 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayBindingConstants;
26 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayCallback;
27 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConfiguration;
28 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConnector;
29 import org.openhab.binding.openthermgateway.internal.OpenThermGatewaySocketConnector;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.SIUnits;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.binding.BaseBridgeHandler;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.RefreshType;
41 import org.openhab.core.types.UnDefType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
47 * sent to one of the channels.
49 * @author Arjen Korevaar - Initial contribution
52 public class OpenThermGatewayHandler extends BaseBridgeHandler implements OpenThermGatewayCallback {
54 private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
56 private @Nullable OpenThermGatewayConfiguration configuration;
57 private @Nullable OpenThermGatewayConnector connector;
58 private @Nullable ScheduledFuture<?> reconnectTask;
60 private @Nullable ConnectionState state;
61 private boolean autoReconnect = true;
62 private boolean disposing = false;
64 public OpenThermGatewayHandler(Bridge bridge) {
69 public void initialize() {
70 logger.debug("Initializing OpenThermGateway handler for uid {}", getThing().getUID());
72 configuration = getConfigAs(OpenThermGatewayConfiguration.class);
73 logger.debug("Using configuration: {}", configuration);
76 updateStatus(ThingStatus.UNKNOWN);
81 public void handleCommand(ChannelUID channelUID, Command command) {
82 logger.debug("Received command {} for channel {}", command, channelUID);
84 if (!(command instanceof RefreshType)) {
85 String channel = channelUID.getId();
86 String code = getGatewayCodeFromChannel(channel);
88 GatewayCommand gatewayCommand = null;
90 if (command instanceof OnOffType) {
91 OnOffType onOff = (OnOffType) command;
92 gatewayCommand = GatewayCommand.parse(code, onOff == OnOffType.ON ? "1" : "0");
94 if (command instanceof QuantityType<?>) {
95 QuantityType<?> quantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
97 if (quantityType != null) {
98 double value = quantityType.doubleValue();
99 gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
103 if (gatewayCommand == null) {
104 gatewayCommand = GatewayCommand.parse(code, command.toFullString());
107 sendCommand(gatewayCommand);
109 if (GatewayCommandCode.CONTROLSETPOINT.equals(code)) {
110 if (gatewayCommand.getMessage().equals("0.0")) {
111 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
114 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
115 OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
116 } else if (GatewayCommandCode.CONTROLSETPOINT2.equals(code)) {
117 if (gatewayCommand.getMessage().equals("0.0")) {
118 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT,
121 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED,
122 OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
127 public void sendCommand(GatewayCommand gatewayCommand) {
129 OpenThermGatewayConnector conn = connector;
131 if (conn != null && conn.isConnected()) {
132 conn.sendCommand(gatewayCommand);
134 logger.debug("Unable to send command {}: connector not connected", gatewayCommand.toFullString());
139 public void receiveMessage(Message message) {
140 scheduler.submit(() -> receiveMessageTask(message));
143 private void receiveMessageTask(Message message) {
144 int msgId = message.getID();
146 if (!DataItemGroup.DATAITEMGROUPS.containsKey(msgId)) {
147 logger.debug("Unsupported message id {}", msgId);
151 for (Thing thing : getThing().getThings()) {
152 BaseDeviceHandler handler = (BaseDeviceHandler) thing.getHandler();
154 if (handler != null) {
155 handler.receiveMessage(message);
161 public void connectionStateChanged(ConnectionState state) {
162 scheduler.submit(() -> connectionStateChangedTask(state));
165 private void connectionStateChangedTask(ConnectionState state) {
166 if (this.state != state) {
171 updateStatus(ThingStatus.ONLINE);
172 cancelAutoReconnect();
176 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
185 public void handleRemoval() {
186 logger.debug("Removing OpenThermGateway handler");
188 super.handleRemoval();
192 public void dispose() {
193 logger.debug("Disposing OpenThermGateway handler");
199 private void connect() {
201 OpenThermGatewayConfiguration config = configuration;
203 if (this.state == ConnectionState.CONNECTING) {
204 logger.debug("OpenThermGateway connector is already connecting");
208 // Make sure everything is cleaned up before creating a new connection
211 if (config != null) {
212 connectionStateChanged(ConnectionState.INITIALIZING);
214 logger.debug("Starting OpenThermGateway connector");
216 autoReconnect = true;
218 OpenThermGatewayConnector conn = connector = new OpenThermGatewaySocketConnector(this, config);
221 logger.debug("OpenThermGateway connector started");
225 private void disconnect() {
227 OpenThermGatewayConnector conn = connector;
229 autoReconnect = false;
231 cancelAutoReconnect();
239 private void autoReconnect() {
241 OpenThermGatewayConfiguration config = configuration;
243 if (autoReconnect && config != null && config.connectionRetryInterval > 0) {
244 logger.debug("Scheduling to auto reconnect in {} seconds", config.connectionRetryInterval);
245 reconnectTask = scheduler.schedule(this::connect, config.connectionRetryInterval, TimeUnit.SECONDS);
249 private void cancelAutoReconnect() {
250 ScheduledFuture<?> localReconnectTask = reconnectTask;
252 if (localReconnectTask != null) {
253 if (!localReconnectTask.isDone()) {
254 logger.debug("Cancelling auto reconnect task");
255 localReconnectTask.cancel(true);
258 reconnectTask = null;
262 private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
264 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
265 return GatewayCommandCode.TEMPERATURETEMPORARY;
266 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
267 return GatewayCommandCode.TEMPERATURECONSTANT;
268 case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
269 return GatewayCommandCode.TEMPERATUREOUTSIDE;
270 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
271 return GatewayCommandCode.SETPOINTWATER;
272 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
273 return GatewayCommandCode.CONTROLSETPOINT;
274 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
275 return GatewayCommandCode.CENTRALHEATING;
276 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT:
277 return GatewayCommandCode.CONTROLSETPOINT2;
278 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED:
279 return GatewayCommandCode.CENTRALHEATING2;
280 case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_VENTILATION_SETPOINT:
281 return GatewayCommandCode.VENTILATIONSETPOINT;
282 case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
285 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));