]> git.basschouten.com Git - openhab-addons.git/blob
af0ce8b9d0d5d34e708cc1b8ec60ea5c9c26aba5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.bluetooth.daikinmadoka.internal.model;
14
15 import java.io.ByteArrayOutputStream;
16 import java.io.DataOutputStream;
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.BRC1HCommand;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * This class represents a message transmitted or received from the BRC1H controller as a serial protocol
31  *
32  * @author Benjamin Lafois - Initial contribution
33  */
34 @NonNullByDefault
35 public class MadokaMessage {
36
37     private static final Logger logger = LoggerFactory.getLogger(MadokaMessage.class);
38
39     private int messageId;
40     private final Map<Integer, MadokaValue> values;
41
42     private byte @Nullable [] rawMessage;
43
44     private MadokaMessage() {
45         values = new HashMap<>();
46     }
47
48     public static byte[] createRequest(BRC1HCommand command, MadokaValue... parameters) {
49         try {
50             ByteArrayOutputStream output = new ByteArrayOutputStream();
51             DataOutputStream request = new DataOutputStream(output);
52
53             // Message Length - Computed in the end
54             request.writeByte(0);
55             request.writeByte(0);
56
57             // Command ID, coded on 3 bytes
58             request.writeByte(0);
59             request.writeShort(command.getCommandId());
60
61             if (parameters.length == 0) {
62                 request.writeByte(0);
63                 request.writeByte(0);
64             } else {
65                 for (MadokaValue mv : parameters) {
66                     request.writeByte(mv.getId());
67                     request.writeByte(mv.getSize());
68                     request.write(mv.getRawValue());
69                 }
70             }
71
72             // Finally, compute array size
73             byte[] ret = output.toByteArray();
74             ret[1] = (byte) (ret.length - 1);
75
76             return ret;
77         } catch (IOException e) {
78             logger.info("Error while building request", e);
79             throw new RuntimeException(e);
80         }
81     }
82
83     public static MadokaMessage parse(byte[] msg) throws MadokaParsingException {
84         // Msg format (bytes):
85         // <Msg Length> <msg id> <msg id> <msg id> ...
86         // So MINIMAL length is 4, to cover the message length + message ID
87         if (msg.length < 4) {
88             throw new MadokaParsingException("Message received is too short to be parsed.");
89         }
90         if (msg[0] != msg.length) {
91             throw new MadokaParsingException("Message size is not valid (different from byte[0]).");
92         }
93
94         MadokaMessage m = new MadokaMessage();
95         m.setRawMessage(msg);
96         m.messageId = ByteBuffer.wrap(msg, 2, 2).getShort();
97
98         MadokaValue mv = null;
99
100         // Starting here, we are not on the safe side with previous msg.length check
101         for (int i = 4; i < msg.length;) {
102             if ((i + 1) >= msg.length) {
103                 throw new MadokaParsingException("Truncated message detected while parsing response value header");
104             }
105
106             mv = new MadokaValue();
107             mv.setId(msg[i]);
108             mv.setSize(Byte.toUnsignedInt(msg[i + 1]));
109
110             if ((i + 1 + mv.getSize()) >= msg.length) {
111                 throw new MadokaParsingException("Truncated message detected while parsing response value content");
112             }
113
114             mv.setRawValue(Arrays.copyOfRange(msg, i + 2, i + 2 + mv.getSize()));
115
116             i += 2 + mv.getSize();
117
118             m.values.put(mv.getId(), mv);
119         }
120
121         return m;
122     }
123
124     private void setRawMessage(byte[] rawMessage) {
125         this.rawMessage = rawMessage;
126     }
127
128     public byte @Nullable [] getRawMessage() {
129         return this.rawMessage;
130     }
131
132     public int getMessageId() {
133         return messageId;
134     }
135
136     public Map<Integer, MadokaValue> getValues() {
137         return values;
138     }
139
140     @Override
141     public String toString() {
142         StringBuilder sb = new StringBuilder();
143
144         sb.append(String.format("{ messageId: %d, values: [", this.messageId));
145
146         for (Map.Entry<Integer, MadokaValue> entry : values.entrySet()) {
147             sb.append(String.format(" { valueId: %d, valueSize: %d },", entry.getKey(), entry.getValue().getSize()));
148         }
149
150         sb.append("] }");
151         return sb.toString();
152     }
153 }