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