]> git.basschouten.com Git - openhab-addons.git/blob
14bd284742d2d11379aea37afb43e560edd2b506
[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.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
17
18 import javax.measure.Unit;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.openthermgateway.OpenThermGatewayBindingConstants;
23 import org.openhab.binding.openthermgateway.internal.DataItem;
24 import org.openhab.binding.openthermgateway.internal.DataItemGroup;
25 import org.openhab.binding.openthermgateway.internal.GatewayCommand;
26 import org.openhab.binding.openthermgateway.internal.GatewayCommandCode;
27 import org.openhab.binding.openthermgateway.internal.Message;
28 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayCallback;
29 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConfiguration;
30 import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConnector;
31 import org.openhab.binding.openthermgateway.internal.OpenThermGatewaySocketConnector;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.QuantityType;
35 import org.openhab.core.library.unit.SIUnits;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.ThingStatusDetail;
40 import org.openhab.core.thing.binding.BaseThingHandler;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.RefreshType;
43 import org.openhab.core.types.State;
44 import org.openhab.core.types.UnDefType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
50  * sent to one of the channels.
51  *
52  * @author Arjen Korevaar - Initial contribution
53  */
54 @NonNullByDefault
55 public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThermGatewayCallback {
56
57     private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
58
59     private @Nullable OpenThermGatewayConfiguration config;
60     private @Nullable OpenThermGatewayConnector connector;
61     private @Nullable ScheduledFuture<?> reconnectTask;
62
63     private boolean connecting = false;
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         @Nullable
84         OpenThermGatewayConnector conn = connector;
85
86         logger.debug("Received channel: {}, command: {}", channelUID, command);
87
88         if (!(command instanceof RefreshType)) {
89             String channel = channelUID.getId();
90             String code = getGatewayCodeFromChannel(channel);
91
92             GatewayCommand gatewayCommand = null;
93
94             if (command instanceof OnOffType) {
95                 OnOffType onOff = (OnOffType) command;
96                 gatewayCommand = GatewayCommand.parse(code, onOff == OnOffType.ON ? "1" : "0");
97             }
98             if (command instanceof QuantityType<?>) {
99                 QuantityType<?> quantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
100
101                 if (quantityType != null) {
102                     double value = quantityType.doubleValue();
103                     gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
104                 }
105             }
106
107             if (gatewayCommand == null) {
108                 gatewayCommand = GatewayCommand.parse(code, command.toFullString());
109             }
110
111             if (conn != null && conn.isConnected()) {
112                 conn.sendCommand(gatewayCommand);
113
114                 if (code == GatewayCommandCode.ControlSetpoint) {
115                     if (gatewayCommand.getMessage().equals("0.0")) {
116                         updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT,
117                                 UnDefType.UNDEF);
118                     }
119                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED,
120                             OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
121                 }
122             } else {
123                 connect();
124             }
125         }
126     }
127
128     @Override
129     public void connecting() {
130         connecting = true;
131         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Connecting");
132     }
133
134     @Override
135     public void connected() {
136         connecting = false;
137         updateStatus(ThingStatus.ONLINE);
138     }
139
140     @Override
141     public void disconnected() {
142         @Nullable
143         OpenThermGatewayConfiguration conf = config;
144
145         connecting = false;
146
147         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected");
148
149         // retry connection if disconnect is not explicitly requested
150         if (!explicitDisconnect && conf != null && conf.connectionRetryInterval > 0) {
151             logger.debug("Scheduling to reconnect in {} seconds.", conf.connectionRetryInterval);
152             reconnectTask = scheduler.schedule(this::connect, conf.connectionRetryInterval, TimeUnit.SECONDS);
153         }
154     }
155
156     @Override
157     public void receiveMessage(Message message) {
158         if (DataItemGroup.dataItemGroups.containsKey(message.getID())) {
159             DataItem[] dataItems = DataItemGroup.dataItemGroups.get(message.getID());
160
161             for (DataItem dataItem : dataItems) {
162                 String channelId = dataItem.getSubject();
163
164                 if (!OpenThermGatewayBindingConstants.SUPPORTED_CHANNEL_IDS.contains(channelId)
165                         || (dataItem.getFilteredCode() != null && dataItem.getFilteredCode() != message.getCode())) {
166                     continue;
167                 }
168
169                 State state = null;
170
171                 switch (dataItem.getDataType()) {
172                     case FLAGS:
173                         state = OnOffType.from(message.getBit(dataItem.getByteType(), dataItem.getBitPos()));
174                         break;
175                     case UINT8:
176                     case UINT16:
177                         state = new DecimalType(message.getUInt(dataItem.getByteType()));
178                         break;
179                     case INT8:
180                     case INT16:
181                         state = new DecimalType(message.getInt(dataItem.getByteType()));
182                         break;
183                     case FLOAT:
184                         float value = message.getFloat();
185                         @Nullable
186                         Unit<?> unit = dataItem.getUnit();
187                         state = (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit);
188                         break;
189                     case DOWTOD:
190                         break;
191                 }
192
193                 if (state != null) {
194                     logger.debug("Received update for channel '{}': {}", channelId, state);
195                     updateState(channelId, state);
196                 }
197             }
198         }
199     }
200
201     @Override
202     public void handleRemoval() {
203         logger.debug("Removing OpenTherm Gateway handler");
204         disconnect();
205         super.handleRemoval();
206     }
207
208     @Override
209     public void dispose() {
210         disconnect();
211
212         ScheduledFuture<?> localReconnectTask = reconnectTask;
213         if (localReconnectTask != null) {
214             localReconnectTask.cancel(true);
215             reconnectTask = null;
216         }
217
218         super.dispose();
219     }
220
221     private void connect() {
222         @Nullable
223         OpenThermGatewayConfiguration conf = config;
224
225         explicitDisconnect = false;
226
227         if (connecting) {
228             logger.debug("OpenTherm Gateway connector is already connecting ...");
229             return;
230         }
231
232         disconnect();
233
234         if (conf != null) {
235             logger.debug("Starting OpenTherm Gateway connector");
236
237             connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port);
238
239             Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread");
240             thread.setDaemon(true);
241             thread.start();
242
243             logger.debug("OpenTherm Gateway connector started");
244         }
245     }
246
247     private void disconnect() {
248         @Nullable
249         OpenThermGatewayConnector conn = connector;
250
251         explicitDisconnect = true;
252
253         if (conn != null) {
254             if (conn.isConnected()) {
255                 logger.debug("Stopping OpenTherm Gateway connector");
256                 conn.stop();
257             }
258
259             connector = null;
260         }
261     }
262
263     private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
264         switch (channel) {
265             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
266                 return GatewayCommandCode.TemperatureTemporary;
267             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
268                 return GatewayCommandCode.TemperatureConstant;
269             case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
270                 return GatewayCommandCode.TemperatureOutside;
271             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
272                 return GatewayCommandCode.SetpointWater;
273             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
274                 return GatewayCommandCode.ControlSetpoint;
275             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
276                 return GatewayCommandCode.CentralHeating;
277             case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
278                 return null;
279             default:
280                 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
281         }
282     }
283 }