]> git.basschouten.com Git - openhab-addons.git/blob
0ef1c81010172efef36b3c69f8f5ac3484d2e8b6
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.nibeheatpump.internal.protocol;
14
15 import org.apache.commons.lang3.ArrayUtils;
16 import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException;
17
18 /**
19  * This class contains useful Nibe heat pump protocol utils.
20  *
21  * @author Pauli Anttila - Initial contribution
22  */
23 public class NibeHeatPumpProtocol {
24
25     public static final byte FRAME_START_CHAR_FROM_NIBE = (byte) 0x5C;
26     public static final byte FRAME_START_CHAR_TO_NIBE = (byte) 0xC0;
27
28     public static final byte OFFSET_START = 0;
29     public static final byte OFFSET_ADR = 2;
30     public static final byte OFFSET_CMD = 3;
31     public static final byte OFFSET_LEN = 4;
32     public static final byte OFFSET_DATA = 5;
33
34     public static final byte CMD_RMU_DATA_MSG = (byte) 0x62;
35     public static final byte CMD_MODBUS_DATA_MSG = (byte) 0x68;
36     public static final byte CMD_MODBUS_READ_REQ = (byte) 0x69;
37     public static final byte CMD_MODBUS_READ_RESP = (byte) 0x6A;
38     public static final byte CMD_MODBUS_WRITE_REQ = (byte) 0x6B;
39     public static final byte CMD_MODBUS_WRITE_RESP = (byte) 0x6C;
40
41     public static final byte ADR_SMS40 = (byte) 0x16;
42     public static final byte ADR_RMU40 = (byte) 0x19;
43     public static final byte ADR_MODBUS40 = (byte) 0x20;
44
45     public static boolean isModbus40DataReadOut(byte[] data) {
46         if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
47                 && data[OFFSET_ADR] == ADR_MODBUS40) {
48             return data[OFFSET_CMD] == CMD_MODBUS_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x50;
49         }
50
51         return false;
52     }
53
54     public static boolean isModbus40ReadResponse(byte[] data) {
55         if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
56                 && data[OFFSET_ADR] == ADR_MODBUS40) {
57             return data[OFFSET_CMD] == CMD_MODBUS_READ_RESP && data[OFFSET_LEN] >= (byte) 0x06;
58         }
59
60         return false;
61     }
62
63     public static boolean isRmu40DataReadOut(byte[] data) {
64         if (data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_RMU40) {
65             return data[OFFSET_CMD] == CMD_RMU_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x18;
66         }
67
68         return false;
69     }
70
71     public static boolean isModbus40WriteResponsePdu(byte[] data) {
72         return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
73                 && data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_WRITE_RESP;
74     }
75
76     public static boolean isModbus40WriteTokenPdu(byte[] data) {
77         return data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_MODBUS40
78                 && data[OFFSET_CMD] == CMD_MODBUS_WRITE_REQ && data[OFFSET_LEN] == 0x00;
79     }
80
81     public static boolean isModbus40ReadTokenPdu(byte[] data) {
82         return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
83                 && data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_READ_REQ
84                 && data[OFFSET_LEN] == 0x00;
85     }
86
87     public static boolean isModbus40WriteRequestPdu(byte[] data) {
88         return data[0] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_WRITE_REQ;
89     }
90
91     public static boolean isModbus40ReadRequestPdu(byte[] data) {
92         return data[OFFSET_START] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_READ_REQ;
93     }
94
95     public static byte calculateChecksum(byte[] data) {
96         return calculateChecksum(data, 0, data.length);
97     }
98
99     public static byte calculateChecksum(byte[] data, int startIndex, int stopIndex) {
100         byte checksum = 0;
101         // calculate XOR checksum
102         for (int i = startIndex; i < stopIndex; i++) {
103             checksum ^= data[i];
104         }
105         return checksum;
106     }
107
108     public static byte getMessageType(byte[] data) {
109         byte messageType = 0;
110
111         if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE) {
112             messageType = data[NibeHeatPumpProtocol.OFFSET_CMD];
113         } else if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_TO_NIBE) {
114             messageType = data[1];
115         }
116
117         return messageType;
118     }
119
120     public static byte[] checkMessageChecksumAndRemoveDoubles(byte[] data) throws NibeHeatPumpException {
121         int msglen;
122         int startIndex;
123         int stopIndex;
124
125         if (NibeHeatPumpProtocol.isModbus40ReadRequestPdu(data)
126                 || NibeHeatPumpProtocol.isModbus40WriteRequestPdu(data)) {
127             msglen = 3 + data[2];
128             startIndex = 0;
129             stopIndex = msglen;
130         } else {
131             msglen = 5 + data[OFFSET_LEN];
132             startIndex = 2;
133             stopIndex = msglen;
134         }
135
136         final byte checksum = calculateChecksum(data, startIndex, stopIndex);
137         final byte msgChecksum = data[msglen];
138
139         // if checksum is 0x5C (start character), heat pump seems to send 0xC5 checksum
140
141         if (checksum == msgChecksum || (checksum == FRAME_START_CHAR_FROM_NIBE && msgChecksum == (byte) 0xC5)) {
142             // if data contains 0x5C (start character), data seems to contains double 0x5C characters
143
144             // let's remove doubles
145             for (int i = 1; i < msglen; i++) {
146                 if (data[i] == FRAME_START_CHAR_FROM_NIBE) {
147                     data = ArrayUtils.remove(data, i);
148                     msglen--;
149
150                     // fix message len
151                     data[OFFSET_LEN]--;
152                 }
153             }
154         } else {
155             throw new NibeHeatPumpException(
156                     "Checksum does not match. Checksum=" + (msgChecksum & 0xFF) + ", expected=" + (checksum & 0xFF));
157         }
158
159         return data;
160     }
161 }