]> git.basschouten.com Git - openhab-addons.git/blob
c0381ad93faae02e36183101530d6817aedc4073
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.dominoswiss.internal;
14
15 import static org.openhab.binding.dominoswiss.internal.DominoswissBindingConstants.*;
16
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
22 import java.net.InetSocketAddress;
23 import java.net.Socket;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.Future;
28 import java.util.concurrent.ScheduledFuture;
29 import java.util.concurrent.TimeUnit;
30
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.types.Command;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * The {@link EgateHandler} is responsible for handling commands, which are
46  * sent to one of the channels.
47  *
48  * @author Frieso Aeschbacher - Initial contribution
49  */
50 @NonNullByDefault
51 public class EGateHandler extends BaseBridgeHandler {
52
53     private final Logger logger = LoggerFactory.getLogger(EGateHandler.class);
54     private @Nullable Socket egateSocket;
55
56     private int port;
57     private @Nullable String host;
58     private static final int SOCKET_TIMEOUT_SEC = 250;
59     private final Object lock = new Object();
60     private @Nullable BufferedWriter writer;
61     private @Nullable BufferedReader reader;
62     private @Nullable Future<?> refreshJob;
63     private Map<String, ThingUID> registeredBlinds;
64     private @Nullable ScheduledFuture<?> pollingJob;
65
66     public EGateHandler(Bridge thing) {
67         super(thing);
68         registeredBlinds = new HashMap<String, ThingUID>();
69     }
70
71     @Override
72     public void handleCommand(ChannelUID channelUID, Command command) {
73         if (channelUID.getId().equals(GETCONFIG)) {
74             sendCommand("EthernetGet;\r");
75         }
76     }
77
78     @Override
79     public void initialize() {
80         DominoswissConfiguration config;
81         config = this.getConfigAs(DominoswissConfiguration.class);
82         host = config.ipAddress;
83         port = config.port;
84
85         if (host != null && port > 0) {
86             // Create a socket to eGate
87             try (Socket localEgateSocket = new Socket(host, port)) {
88                 writer = new BufferedWriter(new OutputStreamWriter(localEgateSocket.getOutputStream()));
89                 egateSocket = localEgateSocket;
90             } catch (IOException e) {
91                 logger.debug("IOException in initialize: {} host {} port {}", e.toString(), host, port);
92                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
93                 egateSocket = null;
94             }
95             pollingJob = scheduler.scheduleWithFixedDelay(this::pollingConfig, 0, 30, TimeUnit.SECONDS);
96         } else {
97             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
98                     "Cannot connect to dominoswiss eGate gateway. host IP address or port are not set.");
99         }
100     }
101
102     @Override
103     public void dispose() {
104         try {
105             Socket localEgateSocket = egateSocket;
106             if (localEgateSocket != null) {
107                 localEgateSocket.close();
108             }
109             Future<?> localRefreshJob = refreshJob;
110             if (localRefreshJob != null) {
111                 localRefreshJob.cancel(true);
112             }
113             BufferedReader localReader = reader;
114             if (localReader != null) {
115                 localReader.close();
116             }
117
118             BufferedWriter localWriter = writer;
119             if (localWriter != null) {
120                 localWriter.close();
121             }
122             ScheduledFuture<?> localPollingJob = pollingJob;
123             if (localPollingJob != null) {
124                 localPollingJob.cancel(true);
125                 localPollingJob = null;
126             }
127             logger.debug("EGate Handler connection closed, disposing");
128         } catch (IOException e) {
129             logger.debug("EGate Handler Error on dispose: {} ", e.toString());
130         }
131     }
132
133     public synchronized boolean isConnected() {
134         Socket localEGateSocket = egateSocket;
135         if (localEGateSocket == null) {
136             return false;
137         }
138
139         // NOTE: isConnected() returns true once a connection is made and will
140         // always return true even after the socket is closed
141         // http://stackoverflow.com/questions/10163358/
142         return localEGateSocket.isConnected() && !localEGateSocket.isClosed();
143     }
144
145     /**
146      * Possible Instructions are:
147      * FssTransmit 1 Kommandoabsetzung (Controller > eGate > Dominoswiss)
148      * FssReceive 2 Empfangenes Funkpaket (Dominoswiss > eGate > Controller)
149      *
150      * @throws InterruptedException
151      *
152      */
153
154     public void tiltUp(String id) throws InterruptedException {
155         for (int i = 0; i < 3; i++) {
156             pulseUp(id);
157             Thread.sleep(150); // sleep to not confuse the blinds
158
159         }
160     }
161
162     public void tiltDown(String id) throws InterruptedException {
163         for (int i = 0; i < 3; i++) {
164             pulseDown(id);
165             Thread.sleep(150);// sleep to not confuse the blinds
166         }
167     }
168
169     public void pulseUp(String id) {
170         sendCommand("Instruction=1;ID=" + id + ";Command=1;Priority=1;CheckNr=3415347;" + CR);
171     }
172
173     public void pulseDown(String id) {
174         sendCommand("Instruction=1;ID=" + id + ";Command=2;Priority=1;CheckNr=2764516;" + CR);
175     }
176
177     public void continuousUp(String id) {
178         sendCommand("Instruction=1;ID=" + id + ";Command=3;Priority=1;CheckNr=2867016;" + CR, 20000);
179     }
180
181     public void continuousDown(String id) {
182         sendCommand("Instruction=1;ID=" + id + ";Command=4;Priority=1;CheckNr=973898;" + CR, 20000);
183     }
184
185     public void stop(String id) {
186         sendCommand("Instruction=1;ID=" + id + ";Command=5;Priority=1;CheckNr=5408219;" + CR);
187     }
188
189     public void registerBlind(String id, ThingUID uid) {
190         logger.debug("Registring Blind id {} with thingUID {}", id, uid);
191         registeredBlinds.put(id, uid);
192     }
193
194     /**
195      * Send a command to the eGate Server.
196      */
197
198     private void sendCommand(String command) {
199         sendCommand(command, SOCKET_TIMEOUT_SEC);
200     }
201
202     private synchronized void sendCommand(String command, int timeout) {
203         logger.debug("EGate got command: {}", command);
204         Socket localEGateSocket = egateSocket;
205         BufferedWriter localWriter = writer;
206         if (localEGateSocket == null || localWriter == null) {
207             return;
208         }
209         if (!isConnected()) {
210             logger.debug("no connection to Dominoswiss eGate server when trying to send command, returning...");
211             return;
212         }
213
214         // Send plain string to eGate Server,
215         try {
216             localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
217             localWriter.write(command);
218             localWriter.flush();
219         } catch (IOException e) {
220             logger.debug("Error while sending command {} to Dominoswiss eGate Server {} ", command, e.toString());
221         }
222     }
223
224     private void pollingConfig() {
225         if (!isConnected()) {
226             Socket localEGateSocket = egateSocket;
227             BufferedWriter localWriter = writer;
228             if (localEGateSocket == null || localWriter == null) {
229                 return;
230             }
231             synchronized (lock) {
232                 try {
233                     localEGateSocket.connect(new InetSocketAddress(host, port));
234                     localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
235                     localWriter.write("SilenceModeSet;Value=0;" + CR);
236                     localWriter.flush();
237                 } catch (IOException e) {
238                     logger.debug("IOException in pollingConfig: {} host {} port {}", e.toString(), host, port);
239                     try {
240                         localEGateSocket.close();
241                         egateSocket = null;
242                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
243                     } catch (IOException e1) {
244                         logger.debug("EGate Socket not closed {}", e1.toString());
245                     }
246                     egateSocket = null;
247                 }
248                 if (egateSocket != null) {
249                     updateStatus(ThingStatus.ONLINE);
250                     startAutomaticRefresh();
251                     logger.debug("EGate Handler started automatic refresh, status: {} ",
252                             getThing().getStatus().toString());
253                 } else {
254                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
255                 }
256             }
257         }
258     }
259
260     private void startAutomaticRefresh() {
261         Runnable runnable = () -> {
262             try {
263
264                 Socket localSocket = egateSocket;
265                 if (localSocket == null) {
266                     return;
267                 }
268                 BufferedReader localReader = reader;
269                 if (localReader == null) {
270                     reader = new BufferedReader(new InputStreamReader(localSocket.getInputStream()));
271                 }
272                 if (localReader != null && localReader.ready()) {
273                     String input = localReader.readLine();
274                     logger.debug("Reader got from EGATE: {}", input);
275                     onData(input);
276                 }
277             } catch (IOException e) {
278                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
279                         "Error while reading command from Dominoswiss eGate Server " + e.toString());
280             }
281         };
282         refreshJob = scheduler.submit(runnable);
283     }
284
285     /**
286      * Finds and returns a child thing for a given UID of this bridge.
287      *
288      * @param uid uid of the child thing
289      * @return child thing with the given uid or null if thing was not found
290      */
291     public @Nullable Thing getThingByUID(ThingUID uid) {
292         Bridge bridge = getThing();
293
294         List<Thing> things = bridge.getThings();
295
296         for (Thing thing : things) {
297             if (thing.getUID().equals(uid)) {
298                 return thing;
299             }
300         }
301
302         return null;
303     }
304
305     protected void onData(String input) {
306         // Instruction=2;ID=19;Command=1;Value=0;Priority=0;
307         Map<String, String> map = new HashMap<String, String>();
308         // split on ;
309         String[] parts = input.split(";");
310         if (parts.length >= 2) {
311             for (int i = 0; i < parts.length; i += 2) {
312                 map.put(parts[i], parts[i + 1]);
313             }
314         }
315     }
316 }