]> git.basschouten.com Git - openhab-addons.git/blob
06809cd3a5f6ae4a97d02fb66607cd1386913576
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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         @Nullable
241         OpenThermGatewayConnector conn = connector;
242
243         autoReconnect = false;
244
245         cancelAutoReconnect();
246
247         if (conn != null) {
248             conn.stop();
249             connector = null;
250         }
251     }
252
253     private void autoReconnect() {
254         @Nullable
255         OpenThermGatewayConfiguration config = configuration;
256
257         if (autoReconnect && config != null && config.connectionRetryInterval > 0) {
258             logger.debug("Scheduling to auto reconnect in {} seconds", config.connectionRetryInterval);
259             reconnectTask = scheduler.schedule(this::connect, config.connectionRetryInterval, TimeUnit.SECONDS);
260         }
261     }
262
263     private void cancelAutoReconnect() {
264         ScheduledFuture<?> localReconnectTask = reconnectTask;
265
266         if (localReconnectTask != null) {
267             if (!localReconnectTask.isDone()) {
268                 logger.debug("Cancelling auto reconnect task");
269                 localReconnectTask.cancel(true);
270             }
271
272             reconnectTask = null;
273         }
274     }
275
276     private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
277         switch (channel) {
278             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
279                 return GatewayCommandCode.TEMPERATURETEMPORARY;
280             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
281                 return GatewayCommandCode.TEMPERATURECONSTANT;
282             case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
283                 return GatewayCommandCode.TEMPERATUREOUTSIDE;
284             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
285                 return GatewayCommandCode.SETPOINTWATER;
286             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
287                 return GatewayCommandCode.CONTROLSETPOINT;
288             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
289                 return GatewayCommandCode.CENTRALHEATING;
290             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT:
291                 return GatewayCommandCode.CONTROLSETPOINT2;
292             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED:
293                 return GatewayCommandCode.CENTRALHEATING2;
294             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_VENTILATION_SETPOINT:
295                 return GatewayCommandCode.VENTILATIONSETPOINT;
296             case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
297                 return null;
298             default:
299                 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
300         }
301     }
302 }