]> git.basschouten.com Git - openhab-addons.git/blob
d3c5cfb8171742c7092e581711449e46f32868df
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.velbus.internal.handler;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.OutputStream;
18 import java.time.Instant;
19 import java.time.ZonedDateTime;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.TimeZone;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.velbus.internal.VelbusPacketInputStream;
31 import org.openhab.binding.velbus.internal.VelbusPacketListener;
32 import org.openhab.binding.velbus.internal.config.VelbusBridgeConfig;
33 import org.openhab.binding.velbus.internal.discovery.VelbusThingDiscoveryService;
34 import org.openhab.binding.velbus.internal.packets.VelbusSetDatePacket;
35 import org.openhab.binding.velbus.internal.packets.VelbusSetDaylightSavingsStatusPacket;
36 import org.openhab.binding.velbus.internal.packets.VelbusSetRealtimeClockPacket;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.binding.BaseBridgeHandler;
42 import org.openhab.core.thing.binding.ThingHandlerService;
43 import org.openhab.core.types.Command;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * {@link VelbusBridgeHandler} is an abstract handler for a Velbus interface and connects it to
49  * the framework.
50  *
51  * @author Cedric Boon - Initial contribution
52  */
53 @NonNullByDefault
54 public abstract class VelbusBridgeHandler extends BaseBridgeHandler {
55     private final Logger logger = LoggerFactory.getLogger(VelbusBridgeHandler.class);
56
57     private long lastPacketTimeMillis;
58
59     protected @Nullable VelbusPacketListener defaultPacketListener;
60     protected Map<Byte, VelbusPacketListener> packetListeners = new HashMap<>();
61
62     private @NonNullByDefault({}) VelbusBridgeConfig bridgeConfig;
63     private @Nullable ScheduledFuture<?> timeUpdateJob;
64     private @Nullable ScheduledFuture<?> reconnectionHandler;
65
66     private @NonNullByDefault({}) OutputStream outputStream;
67     private @NonNullByDefault({}) VelbusPacketInputStream inputStream;
68
69     private boolean listenerStopped;
70
71     public VelbusBridgeHandler(Bridge velbusBridge) {
72         super(velbusBridge);
73     }
74
75     @Override
76     public void initialize() {
77         logger.debug("Initializing velbus bridge handler.");
78
79         bridgeConfig = getConfigAs(VelbusBridgeConfig.class);
80
81         connect();
82         initializeTimeUpdate();
83     }
84
85     private void initializeTimeUpdate() {
86         int timeUpdateInterval = bridgeConfig.timeUpdateInterval;
87
88         if (timeUpdateInterval > 0) {
89             startTimeUpdates(timeUpdateInterval);
90         }
91     }
92
93     private void startTimeUpdates(int timeUpdatesInterval) {
94         timeUpdateJob = scheduler.scheduleWithFixedDelay(this::updateDateTime, 0, timeUpdatesInterval,
95                 TimeUnit.MINUTES);
96     }
97
98     private void updateDateTime() {
99         ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.now(), TimeZone.getDefault().toZoneId());
100
101         updateDate(zonedDateTime);
102         updateTime(zonedDateTime);
103         updateDaylightSavingsStatus(zonedDateTime);
104     }
105
106     private void updateTime(ZonedDateTime zonedDateTime) {
107         VelbusSetRealtimeClockPacket packet = new VelbusSetRealtimeClockPacket((byte) 0x00, zonedDateTime);
108
109         byte[] packetBytes = packet.getBytes();
110         this.sendPacket(packetBytes);
111     }
112
113     private void updateDate(ZonedDateTime zonedDateTime) {
114         VelbusSetDatePacket packet = new VelbusSetDatePacket((byte) 0x00, zonedDateTime);
115
116         byte[] packetBytes = packet.getBytes();
117         this.sendPacket(packetBytes);
118     }
119
120     private void updateDaylightSavingsStatus(ZonedDateTime zonedDateTime) {
121         VelbusSetDaylightSavingsStatusPacket packet = new VelbusSetDaylightSavingsStatusPacket((byte) 0x00,
122                 zonedDateTime);
123
124         byte[] packetBytes = packet.getBytes();
125         this.sendPacket(packetBytes);
126     }
127
128     protected void initializeStreams(OutputStream outputStream, InputStream inputStream) {
129         this.outputStream = outputStream;
130         this.inputStream = new VelbusPacketInputStream(inputStream);
131     }
132
133     @Override
134     public void dispose() {
135         final ScheduledFuture<?> timeUpdateJob = this.timeUpdateJob;
136         if (timeUpdateJob != null) {
137             timeUpdateJob.cancel(true);
138         }
139         disconnect();
140     }
141
142     @Override
143     public void handleCommand(ChannelUID channelUID, Command command) {
144         // There is nothing to handle in the bridge handler
145     }
146
147     public synchronized void sendPacket(byte[] packet) {
148         long currentTimeMillis = System.currentTimeMillis();
149         long timeSinceLastPacket = currentTimeMillis - lastPacketTimeMillis;
150
151         if (timeSinceLastPacket < 60) {
152             // When sending you need a delay of 60ms between each packet (to prevent flooding the VMB1USB).
153             long timeToDelay = 60 - timeSinceLastPacket;
154
155             scheduler.schedule(() -> {
156                 sendPacket(packet);
157             }, timeToDelay, TimeUnit.MILLISECONDS);
158
159             return;
160         }
161
162         writePacket(packet);
163
164         lastPacketTimeMillis = System.currentTimeMillis();
165     }
166
167     private void readPacket(byte[] packet) {
168         byte address = packet[2];
169
170         if (packetListeners.containsKey(address)) {
171             VelbusPacketListener packetListener = packetListeners.get(address);
172             packetListener.onPacketReceived(packet);
173         } else {
174             final VelbusPacketListener defaultPacketListener = this.defaultPacketListener;
175             if (defaultPacketListener != null) {
176                 defaultPacketListener.onPacketReceived(packet);
177             }
178         }
179     }
180
181     protected void readPackets() {
182         if (inputStream == null) {
183             onConnectionLost();
184             return;
185         }
186
187         byte[] packet;
188
189         listenerStopped = false;
190
191         try {
192             while (!listenerStopped & ((packet = inputStream.readPacket()).length > 0)) {
193                 readPacket(packet);
194             }
195         } catch (IOException e) {
196             if (!listenerStopped) {
197                 onConnectionLost();
198             }
199         }
200     }
201
202     private void writePacket(byte[] packet) {
203         if (outputStream == null) {
204             onConnectionLost();
205             return;
206         }
207
208         try {
209             outputStream.write(packet);
210             outputStream.flush();
211         } catch (IOException e) {
212             onConnectionLost();
213         }
214     }
215
216     protected void onConnectionLost() {
217         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
218                 "A network communication error occurred.");
219         disconnect();
220         startReconnectionHandler();
221     }
222
223     /**
224      * Makes a connection to the Velbus system.
225      *
226      * @return True if the connection succeeded, false if the connection did not succeed.
227      */
228     protected abstract boolean connect();
229
230     protected void disconnect() {
231         listenerStopped = true;
232
233         try {
234             if (outputStream != null) {
235                 outputStream.close();
236             }
237         } catch (IOException e) {
238             logger.debug("Error while closing output stream", e);
239         }
240
241         try {
242             if (inputStream != null) {
243                 inputStream.close();
244             }
245         } catch (IOException e) {
246             logger.debug("Error while closing input stream", e);
247         }
248     }
249
250     public void startReconnectionHandler() {
251         final ScheduledFuture<?> reconnectionHandler = this.reconnectionHandler;
252         if (reconnectionHandler == null || reconnectionHandler.isCancelled()) {
253             int reconnectionInterval = bridgeConfig.reconnectionInterval;
254             if (reconnectionInterval > 0) {
255                 this.reconnectionHandler = scheduler.scheduleWithFixedDelay(() -> {
256                     final ScheduledFuture<?> currentReconnectionHandler = this.reconnectionHandler;
257                     if (connect() && currentReconnectionHandler != null) {
258                         currentReconnectionHandler.cancel(false);
259                     }
260                 }, reconnectionInterval, reconnectionInterval, TimeUnit.SECONDS);
261             }
262         }
263     }
264
265     @Override
266     public Collection<Class<? extends ThingHandlerService>> getServices() {
267         return Collections.singleton(VelbusThingDiscoveryService.class);
268     }
269
270     public void setDefaultPacketListener(VelbusPacketListener velbusPacketListener) {
271         defaultPacketListener = velbusPacketListener;
272     }
273
274     public void clearDefaultPacketListener() {
275         defaultPacketListener = null;
276     }
277
278     public void registerPacketListener(byte address, VelbusPacketListener packetListener) {
279         packetListeners.put(Byte.valueOf(address), packetListener);
280     }
281
282     public void unregisterRelayStatusListener(byte address) {
283         packetListeners.remove(Byte.valueOf(address));
284     }
285 }