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.satel.internal.protocol;
15 import java.util.Arrays;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * Represents a message sent to (a command) or received from (a response)
24 * {@link SatelModule}. It consists of one byte that specifies command type or
25 * response status and certain number of payload bytes. Number of payload byte
26 * depends on command type. The class allows to serialize a command to bytes and
27 * deserialize a response from given bytes. It also computes and validates
30 * @author Krzysztof Goworek - Initial contribution
33 public class SatelMessage {
34 private static final Logger LOGGER = LoggerFactory.getLogger(SatelMessage.class);
37 private byte[] payload;
39 protected static final byte[] EMPTY_PAYLOAD = new byte[0];
42 * Creates new instance with specified command code and payload.
49 public SatelMessage(byte command, byte[] payload) {
50 this.command = command;
51 this.payload = payload;
55 * Creates new instance with specified command code and empty payload.
57 * @param command command code
59 public SatelMessage(byte command) {
60 this(command, EMPTY_PAYLOAD);
64 * Deserializes new message instance from specified byte buffer.
66 * @param buffer bytes to deserialize a message from
67 * @return deserialized message instance
69 public static @Nullable SatelMessage fromBytes(byte[] buffer) {
70 // we need at least command and checksum
71 if (buffer.length < 3) {
72 LOGGER.error("Invalid message length: {}", buffer.length);
77 int receivedCrc = 0xffff & ((buffer[buffer.length - 2] << 8) | (buffer[buffer.length - 1] & 0xff));
78 int expectedCrc = calculateChecksum(buffer, buffer.length - 2);
79 if (receivedCrc != expectedCrc) {
80 LOGGER.error("Invalid message checksum: received = {}, expected = {}", receivedCrc, expectedCrc);
84 SatelMessage message = new SatelMessage(buffer[0], new byte[buffer.length - 3]);
85 if (message.payload.length > 0) {
86 System.arraycopy(buffer, 1, message.payload, 0, buffer.length - 3);
92 * Returns command byte.
96 public byte getCommand() {
101 * Returns the payload bytes.
103 * @return payload as byte array
105 public byte[] getPayload() {
110 * Returns the message serialized as array of bytes with checksum calculated
113 * @return the message as array of bytes
115 public byte[] getBytes() {
116 byte buffer[] = new byte[this.payload.length + 3];
117 buffer[0] = this.command;
118 if (this.payload.length > 0) {
119 System.arraycopy(this.payload, 0, buffer, 1, this.payload.length);
121 int checksum = calculateChecksum(buffer, buffer.length - 2);
122 buffer[buffer.length - 2] = (byte) ((checksum >> 8) & 0xff);
123 buffer[buffer.length - 1] = (byte) (checksum & 0xff);
127 private String getPayloadAsHex() {
128 StringBuilder result = new StringBuilder();
129 for (int i = 0; i < this.payload.length; ++i) {
133 result.append(String.format("%02X", this.payload[i]));
135 return result.toString();
139 * Calculates a checksum for the specified buffer.
141 * @param buffer the buffer to calculate.
142 * @return the checksum value.
144 private static int calculateChecksum(byte[] buffer, int length) {
145 int checkSum = 0x147a;
146 for (int i = 0; i < length; i++) {
147 checkSum = ((checkSum << 1) | ((checkSum >> 15) & 1));
149 checkSum += ((checkSum >> 8) & 0xff) + (buffer[i] & 0xff);
152 LOGGER.trace("Calculated checksum = {}", String.format("%04X", checkSum));
157 public String toString() {
158 return String.format("Message: command = %02X, payload = %s", this.command, getPayloadAsHex());
161 @SuppressWarnings("PMD.SimplifyBooleanReturns")
163 public boolean equals(@Nullable Object obj) {
172 if (!obj.getClass().equals(this.getClass())) {
176 SatelMessage other = (SatelMessage) obj;
178 if (other.command != this.command) {
182 if (!Arrays.equals(other.payload, this.payload)) {
190 public int hashCode() {
191 final int prime = 31;
193 result = prime * result + command;
194 result = prime * result + Arrays.hashCode(payload);