]> git.basschouten.com Git - openhab-addons.git/blob
8e16a7acc40fedd522d952151e548f6bc0f60e9e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.TimeUnit;
16
17 import javax.measure.Unit;
18
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;
46
47 /**
48  * The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
49  * sent to one of the channels.
50  *
51  * @author Arjen Korevaar - Initial contribution
52  */
53 @NonNullByDefault
54 public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThermGatewayCallback {
55
56     private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
57
58     private @Nullable OpenThermGatewayConfiguration config;
59
60     private @Nullable OpenThermGatewayConnector connector;
61
62     private boolean connecting = false;
63
64     private boolean explicitDisconnect = false;
65
66     public OpenThermGatewayHandler(Thing thing) {
67         super(thing);
68     }
69
70     @Override
71     public void initialize() {
72         logger.debug("Initializing OpenTherm Gateway handler for uid '{}'", getThing().getUID());
73
74         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Initializing");
75
76         config = getConfigAs(OpenThermGatewayConfiguration.class);
77
78         connect();
79     }
80
81     @Override
82     public void handleCommand(ChannelUID channelUID, Command command) {
83         logger.debug("Received channel: {}, command: {}", channelUID, command);
84
85         if (!(command instanceof RefreshType)) {
86             String channel = channelUID.getId();
87             String code = getGatewayCodeFromChannel(channel);
88
89             GatewayCommand gatewayCommand = null;
90
91             if (command instanceof OnOffType) {
92                 OnOffType onOff = (OnOffType) command;
93                 gatewayCommand = GatewayCommand.parse(code, onOff == OnOffType.ON ? "1" : "0");
94             }
95             if (command instanceof QuantityType<?>) {
96                 QuantityType<?> quantityType = ((QuantityType<?>) command).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             if (checkConnection()) {
109                 @Nullable
110                 OpenThermGatewayConnector conn = connector;
111
112                 if (conn != null) {
113                     conn.sendCommand(gatewayCommand);
114
115                     if (code == GatewayCommandCode.ControlSetpoint) {
116                         if (gatewayCommand.getMessage().equals("0.0")) {
117                             updateState(
118                                     OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
119                                     UnDefType.UNDEF);
120                         }
121                         updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
122                                 OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
123                     }
124                 }
125             }
126         }
127     }
128
129     @Override
130     public void connecting() {
131         connecting = true;
132         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Connecting");
133     }
134
135     @Override
136     public void connected() {
137         connecting = false;
138         updateStatus(ThingStatus.ONLINE);
139     }
140
141     @Override
142     public void disconnected() {
143         @Nullable
144         OpenThermGatewayConnector conn = connector;
145
146         @Nullable
147         OpenThermGatewayConfiguration conf = config;
148
149         connecting = false;
150
151         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected");
152
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()) {
157                     connect();
158                 }
159             }, conf.connectionRetryInterval, TimeUnit.SECONDS);
160         }
161     }
162
163     @Override
164     public void receiveMessage(Message message) {
165         if (DataItemGroup.dataItemGroups.containsKey(message.getID())) {
166             DataItem[] dataItems = DataItemGroup.dataItemGroups.get(message.getID());
167
168             for (DataItem dataItem : dataItems) {
169                 String channelId = dataItem.getSubject();
170
171                 if (!OpenThermGatewayBindingConstants.SUPPORTED_CHANNEL_IDS.contains(channelId)
172                         || (dataItem.getFilteredCode() != null && dataItem.getFilteredCode() != message.getCode())) {
173                     continue;
174                 }
175
176                 State state = null;
177
178                 switch (dataItem.getDataType()) {
179                     case FLAGS:
180                         state = OnOffType.from(message.getBit(dataItem.getByteType(), dataItem.getBitPos()));
181                         break;
182                     case UINT8:
183                     case UINT16:
184                         state = new DecimalType(message.getUInt(dataItem.getByteType()));
185                         break;
186                     case INT8:
187                     case INT16:
188                         state = new DecimalType(message.getInt(dataItem.getByteType()));
189                         break;
190                     case FLOAT:
191                         float value = message.getFloat();
192                         @Nullable
193                         Unit<?> unit = dataItem.getUnit();
194                         state = (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit);
195                         break;
196                     case DOWTOD:
197                         break;
198                 }
199
200                 if (state != null) {
201                     logger.debug("Received update for channel '{}': {}", channelId, state);
202                     updateState(channelId, state);
203                 }
204             }
205         }
206     }
207
208     @Override
209     public void handleRemoval() {
210         logger.debug("Removing OpenTherm Gateway handler");
211         disconnect();
212         super.handleRemoval();
213     }
214
215     @Override
216     public void dispose() {
217         disconnect();
218         super.dispose();
219     }
220
221     private boolean checkConnection() {
222         @Nullable
223         OpenThermGatewayConnector conn = connector;
224
225         if (conn != null && conn.isConnected()) {
226             return true;
227         }
228
229         return connect();
230     }
231
232     private boolean connect() {
233         @Nullable
234         OpenThermGatewayConfiguration conf = config;
235
236         disconnect();
237
238         if (conf != null) {
239             logger.debug("Starting OpenTherm Gateway connector");
240
241             explicitDisconnect = false;
242
243             connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port);
244
245             Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread");
246             thread.setDaemon(true);
247             thread.start();
248
249             logger.debug("OpenTherm Gateway connector started");
250
251             return true;
252         }
253
254         return false;
255     }
256
257     private void disconnect() {
258         @Nullable
259         OpenThermGatewayConnector conn = connector;
260
261         if (conn != null) {
262             if (conn.isConnected()) {
263                 logger.debug("Stopping OpenTherm Gateway connector");
264
265                 explicitDisconnect = true;
266                 conn.stop();
267             }
268
269             connector = null;
270         }
271     }
272
273     private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
274         switch (channel) {
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:
288                 return null;
289             default:
290                 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
291         }
292     }
293 }