2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.plugwise.internal.protocol;
15 import java.nio.charset.StandardCharsets;
17 import org.openhab.binding.plugwise.internal.protocol.field.MACAddress;
18 import org.openhab.binding.plugwise.internal.protocol.field.MessageType;
21 * Base class to represent Plugwise protocol data units.
23 * In general a message consists of a hex string containing the following parts:
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
29 * <li>a CRC checksum that is calculated using the previously mentioned segments of the message
32 * Before sending off a message in the Plugwise network they are prepended with a protocol header and trailer is
35 * @author Wouter Born, Karel Goderis - Initial contribution
37 public abstract class Message {
39 public static String getCRC(String string) {
41 int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
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);
56 return (String.format("%04X", crc));
59 protected MessageType type;
60 protected Integer sequenceNumber;
61 protected MACAddress macAddress;
63 protected String payload;
65 protected Message(MessageType messageType) {
66 this(messageType, null, null, null);
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;
75 if (payload != null) {
80 protected Message(MessageType messageType, Integer sequenceNumber, String payload) {
81 this(messageType, sequenceNumber, null, payload);
84 protected Message(MessageType messageType, MACAddress macAddress) {
85 this(messageType, null, macAddress, null);
88 protected Message(MessageType messageType, MACAddress macAddress, String payload) {
89 this(messageType, null, macAddress, payload);
92 protected Message(MessageType messageType, String payload) {
93 this(messageType, null, null, payload);
96 public MACAddress getMACAddress() {
100 public String getPayload() {
104 public int getSequenceNumber() {
105 return sequenceNumber;
108 public MessageType getType() {
112 // Method that implementation classes have to override, and that is responsible for parsing the payload into
114 protected void parsePayload() {
117 protected String payloadToHexString() {
118 return payload != null ? payload : "";
121 private String sequenceNumberToHexString() {
122 return String.format("%04X", sequenceNumber);
125 public void setSequenceNumber(Integer sequenceNumber) {
126 this.sequenceNumber = sequenceNumber;
129 public String toHexString() {
130 StringBuilder sb = new StringBuilder();
131 sb.append(typeToHexString());
132 if (sequenceNumber != null) {
133 sb.append(sequenceNumberToHexString());
135 if (macAddress != null) {
136 sb.append(macAddress);
138 sb.append(payloadToHexString());
140 String string = sb.toString();
141 String crc = getCRC(string);
147 public String toString() {
148 return "Message [type=" + (type != null ? type.name() : null) + ", macAddress=" + macAddress
149 + ", sequenceNumber=" + sequenceNumber + ", payload=" + payload + "]";
152 private String typeToHexString() {
153 return String.format("%04X", type.toInt());