]> git.basschouten.com Git - openhab-addons.git/blob
f169c9fc7c1236add621846257fdc36550e3c337
[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.lgtvserial.internal.protocol.serial;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.OutputStream;
18 import java.util.Arrays;
19 import java.util.HashMap;
20 import java.util.Map;
21
22 import org.openhab.core.io.transport.serial.PortInUseException;
23 import org.openhab.core.io.transport.serial.SerialPort;
24 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
25 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
26 import org.openhab.core.thing.ChannelUID;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * This class handles the communication (read/writes) between the COM port and the things.
32  *
33  * @author Richard Lavoie - Initial contribution
34  *
35  */
36 public class LGSerialCommunicator {
37
38     private OutputStream output;
39     private InputStream input;
40     private SerialPort port;
41
42     private Map<Integer, LGSerialResponseListener> handlers = new HashMap<>();
43     private RegistrationCallback callback;
44
45     private byte[] buffer = new byte[1024];
46
47     /**
48      * Logger.
49      */
50     private final Logger logger = LoggerFactory.getLogger(LGSerialCommunicator.class);
51
52     private static final int BAUD_RATE = 9600;
53
54     public LGSerialCommunicator(SerialPortIdentifier serialPortIdentifier, RegistrationCallback callback)
55             throws IOException, PortInUseException, UnsupportedCommOperationException {
56         SerialPort port = serialPortIdentifier.open(LGSerialCommunicator.class.getCanonicalName(), 2000);
57         port.setSerialPortParams(BAUD_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
58
59         this.port = port;
60         this.output = port.getOutputStream();
61         this.input = port.getInputStream();
62         this.callback = callback;
63     }
64
65     public synchronized void write(LGSerialCommand command, String rawCommand, ChannelUID channel) throws IOException {
66         logger.debug("Sending command : {}", rawCommand);
67         output.write(rawCommand.getBytes());
68         output.write('\r');
69         output.flush();
70
71         // Let's wait not to spam the TV too much
72         try {
73             Thread.sleep(500);
74         } catch (InterruptedException e) {
75             Thread.currentThread().interrupt();
76             throw new IOException(e);
77         }
78
79         int data;
80         int len = 0;
81         int offset = 0;
82         while (input.available() > 0 && (data = input.read()) > -1) {
83             if (data == 'x') {
84                 String result = new String(buffer, offset, len);
85                 if (logger.isDebugEnabled()) {
86                     logger.debug("Buffer : {} offset={} len={}", Arrays.toString(buffer), offset, len);
87                     logger.debug("Received response : {}", result);
88                 }
89                 LGSerialResponse response = command.parseResponse(result);
90                 updateHandler(response, channel);
91
92                 offset += len;
93                 len = 0;
94             } else {
95                 if (offset + len == buffer.length) {
96                     byte[] newBuffer = new byte[1024];
97                     System.arraycopy(buffer, offset, newBuffer, 0, len);
98                     buffer = newBuffer;
99                 }
100                 buffer[offset + len++] = (byte) data;
101             }
102         }
103     }
104
105     /**
106      * Forward the response to the proper response handler, if a response was given.
107      *
108      * @param response Serial response
109      * @param channel Channel to update
110      */
111     private void updateHandler(LGSerialResponse response, ChannelUID channel) {
112         if (response != null) {
113             LGSerialResponseListener listener = handlers.get(response.getSetID());
114             if (listener != null) {
115                 if (response.isSuccess()) {
116                     listener.onSuccess(channel, response);
117                 } else {
118                     listener.onFailure(channel, response);
119                 }
120             }
121         }
122     }
123
124     public synchronized void register(LGSerialResponseListener listener) {
125         handlers.put(listener.getSetID(), listener);
126     }
127
128     public synchronized void unregister(LGSerialResponseListener listener) {
129         handlers.remove(listener.getSetID());
130         if (handlers.isEmpty()) {
131             callback.onUnregister();
132         }
133     }
134
135     public void close() {
136         try {
137             input.close();
138         } catch (IOException e) {
139             logger.debug("An error occured while closing the serial input stream", e);
140         }
141         try {
142             output.close();
143         } catch (IOException e) {
144             logger.debug("An error occured while closing the serial output stream", e);
145         }
146         port.close();
147         // For some reason, there needs some delay after close so we don't fail to open back the serial device
148         // TODO : investigate if this is still a real issue with the serial transport
149         try {
150             Thread.sleep(2000);
151         } catch (InterruptedException e) {
152             Thread.currentThread().interrupt();
153             logger.debug("Thread interrupted while closing the communicator");
154         }
155     }
156 }