]> git.basschouten.com Git - openhab-addons.git/blob
733af53449c294ef7ded44c68bad99accb839c7b
[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 (GatewayCommandCode.ControlSetpoint.equals(code)) {
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                 } else if (GatewayCommandCode.ControlSetpoint2.equals(code)) {
122                     if (gatewayCommand.getMessage().equals("0.0")) {
123                         updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT,
124                                 UnDefType.UNDEF);
125                     }
126                     updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED,
127                             OnOffType.from(!gatewayCommand.getMessage().equals("0.0")));
128                 }
129             } else {
130                 connect();
131             }
132         }
133     }
134
135     @Override
136     public void connecting() {
137         connecting = true;
138         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Connecting");
139     }
140
141     @Override
142     public void connected() {
143         connecting = false;
144         updateStatus(ThingStatus.ONLINE);
145     }
146
147     @Override
148     public void disconnected() {
149         @Nullable
150         OpenThermGatewayConfiguration conf = config;
151
152         connecting = false;
153
154         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected");
155
156         // retry connection if disconnect is not explicitly requested
157         if (!explicitDisconnect && conf != null && conf.connectionRetryInterval > 0) {
158             logger.debug("Scheduling to reconnect in {} seconds.", conf.connectionRetryInterval);
159             reconnectTask = scheduler.schedule(this::connect, 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
219         ScheduledFuture<?> localReconnectTask = reconnectTask;
220         if (localReconnectTask != null) {
221             localReconnectTask.cancel(true);
222             reconnectTask = null;
223         }
224
225         super.dispose();
226     }
227
228     private void connect() {
229         @Nullable
230         OpenThermGatewayConfiguration conf = config;
231
232         explicitDisconnect = false;
233
234         if (connecting) {
235             logger.debug("OpenTherm Gateway connector is already connecting ...");
236             return;
237         }
238
239         disconnect();
240
241         if (conf != null) {
242             logger.debug("Starting OpenTherm Gateway connector");
243
244             connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port);
245
246             Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread");
247             thread.setDaemon(true);
248             thread.start();
249
250             logger.debug("OpenTherm Gateway connector started");
251         }
252     }
253
254     private void disconnect() {
255         @Nullable
256         OpenThermGatewayConnector conn = connector;
257
258         explicitDisconnect = true;
259
260         if (conn != null) {
261             if (conn.isConnected()) {
262                 logger.debug("Stopping OpenTherm Gateway connector");
263                 conn.stop();
264             }
265
266             connector = null;
267         }
268     }
269
270     private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
271         switch (channel) {
272             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
273                 return GatewayCommandCode.TemperatureTemporary;
274             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
275                 return GatewayCommandCode.TemperatureConstant;
276             case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
277                 return GatewayCommandCode.TemperatureOutside;
278             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
279                 return GatewayCommandCode.SetpointWater;
280             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT:
281                 return GatewayCommandCode.ControlSetpoint;
282             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED:
283                 return GatewayCommandCode.CentralHeating;
284             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT:
285                 return GatewayCommandCode.ControlSetpoint2;
286             case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED:
287                 return GatewayCommandCode.CentralHeating2;
288             case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
289                 return null;
290             default:
291                 throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
292         }
293     }
294 }