]> git.basschouten.com Git - openhab-addons.git/blob
7954e8c220c8ccd91b4ccf4539f1985b95ab9f75
[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.nio.charset.StandardCharsets;
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         for (byte b : string.getBytes(StandardCharsets.US_ASCII)) {
44             for (int i = 0; i < 8; i++) {
45                 boolean bit = ((b >> (7 - i) & 1) == 1);
46                 boolean c15 = ((crc >> 15 & 1) == 1);
47                 crc <<= 1;
48                 if (c15 ^ bit) {
49                     crc ^= polynomial;
50                 }
51             }
52         }
53
54         crc &= 0xFFFF;
55
56         return (String.format("%04X", crc));
57     }
58
59     protected MessageType type;
60     protected Integer sequenceNumber;
61     protected MACAddress macAddress;
62
63     protected String payload;
64
65     protected Message(MessageType messageType) {
66         this(messageType, null, null, null);
67     }
68
69     protected Message(MessageType messageType, Integer sequenceNumber, MACAddress macAddress, String payload) {
70         this.type = messageType;
71         this.sequenceNumber = sequenceNumber;
72         this.macAddress = macAddress;
73         this.payload = payload;
74
75         if (payload != null) {
76             parsePayload();
77         }
78     }
79
80     protected Message(MessageType messageType, Integer sequenceNumber, String payload) {
81         this(messageType, sequenceNumber, null, payload);
82     }
83
84     protected Message(MessageType messageType, MACAddress macAddress) {
85         this(messageType, null, macAddress, null);
86     }
87
88     protected Message(MessageType messageType, MACAddress macAddress, String payload) {
89         this(messageType, null, macAddress, payload);
90     }
91
92     protected Message(MessageType messageType, String payload) {
93         this(messageType, null, null, payload);
94     }
95
96     public MACAddress getMACAddress() {
97         return macAddress;
98     }
99
100     public String getPayload() {
101         return payload;
102     }
103
104     public int getSequenceNumber() {
105         return sequenceNumber;
106     }
107
108     public MessageType getType() {
109         return type;
110     }
111
112     // Method that implementation classes have to override, and that is responsible for parsing the payload into
113     // meaningful fields
114     protected void parsePayload() {
115     }
116
117     protected String payloadToHexString() {
118         return payload != null ? payload : "";
119     }
120
121     private String sequenceNumberToHexString() {
122         return String.format("%04X", sequenceNumber);
123     }
124
125     public void setSequenceNumber(Integer sequenceNumber) {
126         this.sequenceNumber = sequenceNumber;
127     }
128
129     public String toHexString() {
130         StringBuilder sb = new StringBuilder();
131         sb.append(typeToHexString());
132         if (sequenceNumber != null) {
133             sb.append(sequenceNumberToHexString());
134         }
135         if (macAddress != null) {
136             sb.append(macAddress);
137         }
138         sb.append(payloadToHexString());
139
140         String string = sb.toString();
141         String crc = getCRC(string);
142
143         return string + crc;
144     }
145
146     @Override
147     public String toString() {
148         return "Message [type=" + (type != null ? type.name() : null) + ", macAddress=" + macAddress
149                 + ", sequenceNumber=" + sequenceNumber + ", payload=" + payload + "]";
150     }
151
152     private String typeToHexString() {
153         return String.format("%04X", type.toInt());
154     }
155 }