]> git.basschouten.com Git - openhab-addons.git/blob
bb82111cba390abe7d8f2562bf0e86240b6a97f9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.openthermgateway.handler;
14
15 import java.util.concurrent.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
17
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;
44
45 /**
46  * The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
47  * sent to one of the channels.
48  *
49  * @author Arjen Korevaar - Initial contribution
50  */
51 @NonNullByDefault
52 public class OpenThermGatewayHandler extends BaseBridgeHandler implements OpenThermGatewayCallback {
53     private static final String PROPERTY_GATEWAY_ID_NAME = "gatewayId";
54     private static final String PROPERTY_GATEWAY_ID_TAG = "PR: A=";
55
56     private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
57
58     private @Nullable OpenThermGatewayConfiguration configuration;
59     private @Nullable OpenThermGatewayConnector connector;
60     private @Nullable ScheduledFuture<?> reconnectTask;
61
62     private @Nullable ConnectionState state;
63     private boolean autoReconnect = true;
64     private boolean disposing = false;
65
66     public OpenThermGatewayHandler(Bridge bridge) {
67         super(bridge);
68     }
69
70     @Override
71     public void initialize() {
72         logger.debug("Initializing OpenThermGateway handler for uid {}", getThing().getUID());
73
74         configuration = getConfigAs(OpenThermGatewayConfiguration.class);
75         logger.debug("Using configuration: {}", configuration);
76
77         disposing = false;
78         updateStatus(ThingStatus.UNKNOWN);
79         connect();
80     }
81
82     @Override
83     public void handleCommand(ChannelUID channelUID, Command command) {
84         logger.debug("Received command {} for channel {}", command, channelUID);
85
86         if (!(command instanceof RefreshType)) {
87             String channel = channelUID.getId();
88             String code = getGatewayCodeFromChannel(channel);
89
90             GatewayCommand gatewayCommand = null;
91
92             if (command instanceof OnOffType) {
93                 OnOffType onOff = (OnOffType) command;
94                 gatewayCommand = GatewayCommand.parse(code, onOff == OnOffType.ON ? "1" : "0");
95             }
96             if (command instanceof QuantityType<?>) {
97                 QuantityType<?> quantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
98
99                 if (quantityType != null) {
100                     double value = quantityType.doubleValue();
101                     gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
102                 }
103             }
104
105             if (gatewayCommand == null) {
106                 gatewayCommand = GatewayCommand.parse(code, command.toFullString());
107             }
108
109             sendCommand(gatewayCommand);
110
111             if (GatewayCommandCode.CONTROLSETPOINT.equals(code)) {
112                 if (gatewayCommand.getMessage().equals("0.0")) {
113                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
114                             UnDefType.UNDEF);
115                 }
116                 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
117                         OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
118             } else if (GatewayCommandCode.CONTROLSETPOINT2.equals(code)) {
119                 if (gatewayCommand.getMessage().equals("0.0")) {
120                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT,
121                             UnDefType.UNDEF);
122                 }
123                 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED,
124                         OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
125             }
126         }
127     }
128
129     public void sendCommand(GatewayCommand gatewayCommand) {
130         @Nullable
131         OpenThermGatewayConnector conn = connector;
132
133         if (conn != null && conn.isConnected()) {
134             conn.sendCommand(gatewayCommand);
135         } else {
136             logger.debug("Unable to send command {}: connector not connected", gatewayCommand.toFullString());
137         }
138     }
139
140     @Override
141     public void receiveMessage(Message message) {
142         scheduler.submit(() -> receiveMessageTask(message));
143     }
144
145     private void receiveMessageTask(Message message) {
146         int msgId = message.getID();
147
148         if (!DataItemGroup.DATAITEMGROUPS.containsKey(msgId)) {
149             logger.debug("Unsupported message id {}", msgId);
150             return;
151         }
152
153         for (Thing thing : getThing().getThings()) {
154             BaseDeviceHandler handler = (BaseDeviceHandler) thing.getHandler();
155
156             if (handler != null) {
157                 handler.receiveMessage(message);
158             }
159         }
160     }
161
162     @Override
163     public void connectionStateChanged(ConnectionState state) {
164         scheduler.submit(() -> connectionStateChangedTask(state));
165     }
166
167     private void connectionStateChangedTask(ConnectionState state) {
168         if (this.state != state) {
169             this.state = state;
170
171             switch (state) {
172                 case CONNECTED:
173                     updateStatus(ThingStatus.ONLINE);
174                     cancelAutoReconnect();
175                     break;
176                 case DISCONNECTED:
177                     if (!disposing) {
178                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
179                         autoReconnect();
180                     }
181                 default:
182             }
183         }
184     }
185
186     @Override
187     public void receiveAcknowledgement(String message) {
188         scheduler.submit(() -> receiveAcknowledgementTask(message));
189     }
190
191     private void receiveAcknowledgementTask(String message) {
192         if (message.startsWith(PROPERTY_GATEWAY_ID_TAG)) {
193             getThing().setProperty(PROPERTY_GATEWAY_ID_NAME,
194                     message.substring(PROPERTY_GATEWAY_ID_TAG.length()).strip());
195         }
196     }
197
198     @Override
199     public void handleRemoval() {
200         logger.debug("Removing OpenThermGateway handler");
201         disconnect();
202         super.handleRemoval();
203     }
204
205     @Override
206     public void dispose() {
207         logger.debug("Disposing OpenThermGateway handler");
208         disposing = true;
209         disconnect();
210         super.dispose();
211     }
212
213     private void connect() {
214         @Nullable
215         OpenThermGatewayConfiguration config = configuration;
216
217         if (this.state == ConnectionState.CONNECTING) {
218             logger.debug("OpenThermGateway connector is already connecting");
219             return;
220         }
221
222         // Make sure everything is cleaned up before creating a new connection
223         disconnect();
224
225         if (config != null) {
226             connectionStateChanged(ConnectionState.INITIALIZING);
227
228             logger.debug("Starting OpenThermGateway connector");
229
230             autoReconnect = true;
231
232             OpenThermGatewayConnector conn = connector = new OpenThermGatewaySocketConnector(this, config);
233             conn.start();
234
235             logger.debug("OpenThermGateway connector started");
236         }
237     }
238
239     private void disconnect() {
240         updateStatus(ThingStatus.OFFLINE);
241
242         autoReconnect = false;
243
244         cancelAutoReconnect();
245
246         @Nullable
247         OpenThermGatewayConnector conn = connector;
248         if (conn != null) {
249             conn.stop();
250             connector = null;
251         }
252     }
253
254     private void autoReconnect() {
255         @Nullable
256         OpenThermGatewayConfiguration config = configuration;
257
258         if (autoReconnect && config != null && config.connectionRetryInterval > 0) {
259             logger.debug("Scheduling to auto reconnect in {} seconds", config.connectionRetryInterval);
260             reconnectTask = scheduler.schedule(this::connect, config.connectionRetryInterval, TimeUnit.SECONDS);
261         }
262     }
263
264     private void cancelAutoReconnect() {
265         ScheduledFuture<?> localReconnectTask = reconnectTask;
266
267         if (localReconnectTask != null) {
268             if (!localReconnectTask.isDone()) {
269                 logger.debug("Cancelling auto reconnect task");
270                 localReconnectTask.cancel(true);
271             }
272
273             reconnectTask = null;
274         }
275     }
276
277     private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
278         switch (channel) {
279             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
280                 return GatewayCommandCode.TEMPERATURETEMPORARY;
281             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
282                 return GatewayCommandCode.TEMPERATURECONSTANT;
283             case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
284                 return GatewayCommandCode.TEMPERATUREOUTSIDE;
285             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
286                 return GatewayCommandCode.SETPOINTWATER;
287             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
288                 return GatewayCommandCode.CONTROLSETPOINT;
289             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
290                 return GatewayCommandCode.CENTRALHEATING;
291             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT:
292                 return GatewayCommandCode.CONTROLSETPOINT2;
293             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED:
294                 return GatewayCommandCode.CENTRALHEATING2;
295             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_VENTILATION_SETPOINT:
296                 return GatewayCommandCode.VENTILATIONSETPOINT;
297             case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
298                 return null;
299             default:
300                 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
301         }
302     }
303 }