]> git.basschouten.com Git - openhab-addons.git/blob
a5153430344a1edcf1f326faf0bcb2bf33db3d0f
[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 onOffCommand) {
93                 gatewayCommand = GatewayCommand.parse(code, onOffCommand == OnOffType.ON ? "1" : "0");
94             }
95             if (command instanceof QuantityType<?> quantityCommand) {
96                 QuantityType<?> quantityType = quantityCommand.toUnit(SIUnits.CELSIUS);
97
98                 if (quantityType != null) {
99                     double value = quantityType.doubleValue();
100                     gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
101                 }
102             }
103
104             if (gatewayCommand == null) {
105                 gatewayCommand = GatewayCommand.parse(code, command.toFullString());
106             }
107
108             sendCommand(gatewayCommand);
109
110             if (GatewayCommandCode.CONTROLSETPOINT.equals(code)) {
111                 if ("0.0".equals(gatewayCommand.getMessage())) {
112                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
113                             UnDefType.UNDEF);
114                 }
115                 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
116                         OnOffType.from(!"0.0".equals(gatewayCommand.getMessage())));
117             } else if (GatewayCommandCode.CONTROLSETPOINT2.equals(code)) {
118                 if ("0.0".equals(gatewayCommand.getMessage())) {
119                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT,
120                             UnDefType.UNDEF);
121                 }
122                 updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED,
123                         OnOffType.from(!"0.0".equals(gatewayCommand.getMessage())));
124             }
125         }
126     }
127
128     public void sendCommand(GatewayCommand gatewayCommand) {
129         @Nullable
130         OpenThermGatewayConnector conn = connector;
131
132         if (conn != null && conn.isConnected()) {
133             conn.sendCommand(gatewayCommand);
134         } else {
135             logger.debug("Unable to send command {}: connector not connected", gatewayCommand.toFullString());
136         }
137     }
138
139     @Override
140     public void receiveMessage(Message message) {
141         scheduler.submit(() -> receiveMessageTask(message));
142     }
143
144     private void receiveMessageTask(Message message) {
145         int msgId = message.getID();
146
147         if (!DataItemGroup.DATAITEMGROUPS.containsKey(msgId)) {
148             logger.debug("Unsupported message id {}", msgId);
149             return;
150         }
151
152         for (Thing thing : getThing().getThings()) {
153             BaseDeviceHandler handler = (BaseDeviceHandler) thing.getHandler();
154
155             if (handler != null) {
156                 handler.receiveMessage(message);
157             }
158         }
159     }
160
161     @Override
162     public void connectionStateChanged(ConnectionState state) {
163         scheduler.submit(() -> connectionStateChangedTask(state));
164     }
165
166     private void connectionStateChangedTask(ConnectionState state) {
167         if (this.state != state) {
168             this.state = state;
169
170             switch (state) {
171                 case CONNECTED:
172                     updateStatus(ThingStatus.ONLINE);
173                     cancelAutoReconnect();
174                     break;
175                 case DISCONNECTED:
176                     if (!disposing) {
177                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
178                         autoReconnect();
179                     }
180                 default:
181             }
182         }
183     }
184
185     @Override
186     public void receiveAcknowledgement(String message) {
187         scheduler.submit(() -> receiveAcknowledgementTask(message));
188     }
189
190     private void receiveAcknowledgementTask(String message) {
191         if (message.startsWith(PROPERTY_GATEWAY_ID_TAG)) {
192             getThing().setProperty(PROPERTY_GATEWAY_ID_NAME,
193                     message.substring(PROPERTY_GATEWAY_ID_TAG.length()).strip());
194         }
195     }
196
197     @Override
198     public void handleRemoval() {
199         logger.debug("Removing OpenThermGateway handler");
200         disconnect();
201         super.handleRemoval();
202     }
203
204     @Override
205     public void dispose() {
206         logger.debug("Disposing OpenThermGateway handler");
207         disposing = true;
208         disconnect();
209         super.dispose();
210     }
211
212     private void connect() {
213         @Nullable
214         OpenThermGatewayConfiguration config = configuration;
215
216         if (this.state == ConnectionState.CONNECTING) {
217             logger.debug("OpenThermGateway connector is already connecting");
218             return;
219         }
220
221         // Make sure everything is cleaned up before creating a new connection
222         disconnect();
223
224         if (config != null) {
225             connectionStateChanged(ConnectionState.INITIALIZING);
226
227             logger.debug("Starting OpenThermGateway connector");
228
229             autoReconnect = true;
230
231             OpenThermGatewayConnector conn = connector = new OpenThermGatewaySocketConnector(this, config);
232             conn.start();
233
234             logger.debug("OpenThermGateway connector started");
235         }
236     }
237
238     private void disconnect() {
239         updateStatus(ThingStatus.OFFLINE);
240
241         autoReconnect = false;
242
243         cancelAutoReconnect();
244
245         @Nullable
246         OpenThermGatewayConnector conn = connector;
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 }