]> git.basschouten.com Git - openhab-addons.git/blob
b1497e8aa255cf92518e68b0da98390646d01479
[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.irtrans.internal;
14
15 import java.io.UnsupportedEncodingException;
16 import java.nio.ByteBuffer;
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * The {@link IrCommand} is a structure to store and manipulate infrared command
26  * in various formats
27  *
28  * @author Karel Goderis - Initial contribution
29  *
30  */
31 public class IrCommand {
32
33     private Logger logger = LoggerFactory.getLogger(IrCommand.class);
34
35     /**
36      *
37      * Each infrared command is in essence a sequence of characters/pointers
38      * that refer to pulse/pause timing pairs. So, in order to build an infrared
39      * command one has to collate the pulse/pause timings as defined by the
40      * sequence
41      *
42      * PulsePair is a small datastructure to capture each pulse/pair timing pair
43      *
44      */
45     private class PulsePair {
46         public int Pulse;
47         public int Pause;
48     }
49
50     private String remote;
51     private String command;
52     private String sequence;
53     private List<PulsePair> pulsePairs;
54     private int numberOfRepeats;
55     private int frequency;
56     private int frameLength;
57     private int pause;
58     private boolean startBit;
59     private boolean repeatStartBit;
60     private boolean noTog;
61     private boolean rc5;
62     private boolean rc6;
63
64     public String getRemote() {
65         return remote;
66     }
67
68     public void setRemote(String remote) {
69         this.remote = remote;
70     }
71
72     public String getCommand() {
73         return command;
74     }
75
76     public void setCommand(String command) {
77         this.command = command;
78     }
79
80     public String getSequence() {
81         return sequence;
82     }
83
84     public void setSequence(String sequence) {
85         this.sequence = sequence;
86     }
87
88     public List<PulsePair> getPulsePairs() {
89         return pulsePairs;
90     }
91
92     public void setPulsePairs(ArrayList<PulsePair> pulsePairs) {
93         this.pulsePairs = pulsePairs;
94     }
95
96     public int getNumberOfRepeats() {
97         return numberOfRepeats;
98     }
99
100     public void setNumberOfRepeats(int numberOfRepeats) {
101         this.numberOfRepeats = numberOfRepeats;
102     }
103
104     public int getFrequency() {
105         return frequency;
106     }
107
108     public void setFrequency(int frequency) {
109         this.frequency = frequency;
110     }
111
112     public int getFrameLength() {
113         return frameLength;
114     }
115
116     public void setFrameLength(int frameLength) {
117         this.frameLength = frameLength;
118     }
119
120     public int getPause() {
121         return pause;
122     }
123
124     public void setPause(int pause) {
125         this.pause = pause;
126     }
127
128     public boolean isStartBit() {
129         return startBit;
130     }
131
132     public void setStartBit(boolean startBit) {
133         this.startBit = startBit;
134     }
135
136     public boolean isRepeatStartBit() {
137         return repeatStartBit;
138     }
139
140     public void setRepeatStartBit(boolean repeatStartBit) {
141         this.repeatStartBit = repeatStartBit;
142     }
143
144     public boolean isNoTog() {
145         return noTog;
146     }
147
148     public void setNoTog(boolean noTog) {
149         this.noTog = noTog;
150     }
151
152     public boolean isRc5() {
153         return rc5;
154     }
155
156     public void setRc5(boolean rc5) {
157         this.rc5 = rc5;
158     }
159
160     public boolean isRc6() {
161         return rc6;
162     }
163
164     public void setRc6(boolean rc6) {
165         this.rc6 = rc6;
166     }
167
168     /**
169      * Matches two IrCommands Commands match if they have the same remote and
170      * the same command
171      *
172      * @param anotherCommand the another command
173      * @return true, if successful
174      */
175     public boolean matches(IrCommand anotherCommand) {
176         return (matchRemote(anotherCommand) && matchCommand(anotherCommand));
177     }
178
179     /**
180      * Match remote fields of two IrCommands In everything we do in the IRtrans
181      * binding, the "*" stands for a wilcard character and will match anything
182      *
183      * @param S the infrared command
184      * @return true, if successful
185      */
186     private boolean matchRemote(IrCommand S) {
187         if ("*".equals(getRemote()) || "*".equals(S.getRemote())) {
188             return true;
189         } else {
190             return S.getRemote().equals(getRemote());
191         }
192     }
193
194     /**
195      * Match command fields of two IrCommands
196      *
197      * @param S the infrared command
198      * @return true, if successful
199      */
200     private boolean matchCommand(IrCommand S) {
201         if ("*".equals(command) || "*".equals(S.command)) {
202             return true;
203         } else {
204             return S.command.equals(command);
205         }
206     }
207
208     /**
209      * Convert/Parse the IRCommand into a ByteBuffer that is compatible with the
210      * IRTrans devices
211      *
212      * @return the byte buffer
213      */
214     public ByteBuffer toByteBuffer() {
215         ByteBuffer byteBuffer = ByteBuffer.allocate(44 + 210 + 1);
216
217         // skip first byte for length - we will fill it in at the end
218         byteBuffer.position(1);
219
220         // Checksum - 1 byte - not used in the ethernet version of the device
221         byteBuffer.put((byte) 0);
222
223         // Command - 1 byte - not used
224         byteBuffer.put((byte) 0);
225
226         // Address - 1 byte - not used
227         byteBuffer.put((byte) 0);
228
229         // Mask - 2 bytes - not used
230         byteBuffer.putShort((short) 0);
231
232         // Number of pulse pairs - 1 byte
233         byte[] byteSequence = sequence.getBytes(StandardCharsets.US_ASCII);
234         byteBuffer.put((byte) (byteSequence.length));
235
236         // Frequency - 1 byte
237         byteBuffer.put((byte) frequency);
238
239         // Mode / Flags - 1 byte
240         byte modeFlags = 0;
241         if (startBit) {
242             modeFlags = (byte) (modeFlags | 1);
243         }
244         if (repeatStartBit) {
245             modeFlags = (byte) (modeFlags | 2);
246         }
247         if (rc5) {
248             modeFlags = (byte) (modeFlags | 4);
249         }
250         if (rc6) {
251             modeFlags = (byte) (modeFlags | 8);
252         }
253         byteBuffer.put(modeFlags);
254
255         // Pause timings - 8 Shorts = 16 bytes
256         for (int i = 0; i < pulsePairs.size(); i++) {
257             byteBuffer.putShort((short) Math.round(pulsePairs.get(i).Pause / 8));
258         }
259         for (int i = pulsePairs.size(); i <= 7; i++) {
260             byteBuffer.putShort((short) 0);
261         }
262
263         // Pulse timings - 8 Shorts = 16 bytes
264         for (int i = 0; i < pulsePairs.size(); i++) {
265             byteBuffer.putShort((short) Math.round(pulsePairs.get(i).Pulse / 8));
266         }
267         for (int i = pulsePairs.size(); i <= 7; i++) {
268             byteBuffer.putShort((short) 0);
269         }
270
271         // Time Counts - 1 Byte
272         byteBuffer.put((byte) pulsePairs.size());
273
274         // Repeats - 1 Byte
275         byte repeat = (byte) 0;
276         repeat = (byte) numberOfRepeats;
277         if (frameLength > 0) {
278             repeat = (byte) (repeat | 128);
279         }
280         byteBuffer.put(repeat);
281
282         // Repeat Pause or Frame Length - 1 byte
283         if ((repeat & 128) == 128) {
284             byteBuffer.put((byte) frameLength);
285         } else {
286             byteBuffer.put((byte) pause);
287         }
288
289         // IR pulse sequence
290         try {
291             byteBuffer.put(sequence.getBytes("ASCII"));
292         } catch (UnsupportedEncodingException e) {
293             logger.warn("An exception occurred while encoding the sequence : '{}'", e.getMessage(), e);
294         }
295
296         // Add <CR> (ASCII 13) at the end of the sequence
297         byteBuffer.put((byte) ((char) 13));
298
299         // set the length of the byte sequence
300         byteBuffer.flip();
301         byteBuffer.position(0);
302         byteBuffer.put((byte) (byteBuffer.limit() - 1));
303         byteBuffer.position(0);
304
305         return byteBuffer;
306     }
307
308     /**
309      * Convert the the infrared command to a Hexadecimal notation/string that
310      * can be interpreted by the IRTrans device
311      *
312      * Convert the first 44 bytes to hex notation, then copy the remainder (= IR
313      * command piece) as ASCII string
314      *
315      * @return the byte buffer in Hex format
316      */
317     public ByteBuffer toHEXByteBuffer() {
318         byte[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
319
320         ByteBuffer byteBuffer = toByteBuffer();
321         byte[] toConvert = new byte[byteBuffer.limit()];
322         byteBuffer.get(toConvert, 0, byteBuffer.limit());
323
324         byte[] converted = new byte[toConvert.length * 2];
325
326         for (int k = 0; k < toConvert.length - 1; k++) {
327             converted[2 * k] = hexDigit[(toConvert[k] >> 4) & 0x0f];
328             converted[2 * k + 1] = hexDigit[toConvert[k] & 0x0f];
329
330         }
331
332         ByteBuffer convertedBuffer = ByteBuffer.allocate(converted.length);
333         convertedBuffer.put(converted);
334         convertedBuffer.flip();
335
336         return convertedBuffer;
337     }
338
339     /**
340      * Convert 'sequence' bit of the IRTrans compatible byte buffer to a
341      * Hexidecimal string
342      *
343      * @return the string
344      */
345     public String sequenceToHEXString() {
346         byte[] byteArray = toHEXByteArray();
347         return new String(byteArray, 88, byteArray.length - 88 - 2);
348     }
349
350     /**
351      * Convert the IRTrans compatible byte buffer to a string
352      *
353      * @return the string
354      */
355     public String toHEXString() {
356         return new String(toHEXByteArray());
357     }
358
359     /**
360      * Convert the IRTrans compatible byte buffer to a byte array.
361      *
362      * @return the byte[]
363      */
364     public byte[] toHEXByteArray() {
365         return toHEXByteBuffer().array();
366     }
367 }