]> git.basschouten.com Git - openhab-addons.git/blob
fdfa0359cbf9e7c15a1ca945ac071189400c93c0
[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.plugwise.internal.protocol;
14
15 import java.io.UnsupportedEncodingException;
16
17 import org.openhab.binding.plugwise.internal.protocol.field.MACAddress;
18 import org.openhab.binding.plugwise.internal.protocol.field.MessageType;
19
20 /**
21  * Base class to represent Plugwise protocol data units.
22  *
23  * In general a message consists of a hex string containing the following parts:
24  * <ul>
25  * <li>a type indicator - many types are yet to be reverse engineered
26  * <li>a sequence number - messages are numbered so that we can keep track of them in an application
27  * <li>a MAC address - the destination of the message
28  * <li>a payload
29  * <li>a CRC checksum that is calculated using the previously mentioned segments of the message
30  * </ul>
31  *
32  * Before sending off a message in the Plugwise network they are prepended with a protocol header and trailer is
33  * added at the end.
34  *
35  * @author Wouter Born, Karel Goderis - Initial contribution
36  */
37 public abstract class Message {
38
39     public static String getCRC(String string) {
40         int crc = 0x0000;
41         int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
42
43         byte[] bytes = new byte[0];
44         try {
45             bytes = string.getBytes("ASCII");
46         } catch (UnsupportedEncodingException e) {
47             return "";
48         }
49
50         for (byte b : bytes) {
51             for (int i = 0; i < 8; i++) {
52                 boolean bit = ((b >> (7 - i) & 1) == 1);
53                 boolean c15 = ((crc >> 15 & 1) == 1);
54                 crc <<= 1;
55                 if (c15 ^ bit) {
56                     crc ^= polynomial;
57                 }
58             }
59         }
60
61         crc &= 0xFFFF;
62
63         return (String.format("%04X", crc));
64     }
65
66     protected MessageType type;
67     protected Integer sequenceNumber;
68     protected MACAddress macAddress;
69
70     protected String payload;
71
72     public Message(MessageType messageType) {
73         this(messageType, null, null, null);
74     }
75
76     public Message(MessageType messageType, Integer sequenceNumber, MACAddress macAddress, String payload) {
77         this.type = messageType;
78         this.sequenceNumber = sequenceNumber;
79         this.macAddress = macAddress;
80         this.payload = payload;
81
82         if (payload != null) {
83             parsePayload();
84         }
85     }
86
87     public Message(MessageType messageType, Integer sequenceNumber, String payload) {
88         this(messageType, sequenceNumber, null, payload);
89     }
90
91     public Message(MessageType messageType, MACAddress macAddress) {
92         this(messageType, null, macAddress, null);
93     }
94
95     public Message(MessageType messageType, MACAddress macAddress, String payload) {
96         this(messageType, null, macAddress, payload);
97     }
98
99     public Message(MessageType messageType, String payload) {
100         this(messageType, null, null, payload);
101     }
102
103     public MACAddress getMACAddress() {
104         return macAddress;
105     }
106
107     public String getPayload() {
108         return payload;
109     }
110
111     public int getSequenceNumber() {
112         return sequenceNumber;
113     }
114
115     public MessageType getType() {
116         return type;
117     }
118
119     // Method that implementation classes have to override, and that is responsible for parsing the payload into
120     // meaningful fields
121     protected void parsePayload() {
122     }
123
124     protected String payloadToHexString() {
125         return payload != null ? payload : "";
126     }
127
128     private String sequenceNumberToHexString() {
129         return String.format("%04X", sequenceNumber);
130     }
131
132     public void setSequenceNumber(Integer sequenceNumber) {
133         this.sequenceNumber = sequenceNumber;
134     }
135
136     public String toHexString() {
137         StringBuilder sb = new StringBuilder();
138         sb.append(typeToHexString());
139         if (sequenceNumber != null) {
140             sb.append(sequenceNumberToHexString());
141         }
142         if (macAddress != null) {
143             sb.append(macAddress);
144         }
145         sb.append(payloadToHexString());
146
147         String string = sb.toString();
148         String crc = getCRC(string);
149
150         return string + crc;
151     }
152
153     @Override
154     public String toString() {
155         return "Message [type=" + (type != null ? type.name() : null) + ", macAddress=" + macAddress
156                 + ", sequenceNumber=" + sequenceNumber + ", payload=" + payload + "]";
157     }
158
159     private String typeToHexString() {
160         return String.format("%04X", type.toInt());
161     }
162 }