2 * Copyright (c) 2010-2024 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.nibeheatpump.internal.protocol;
15 import java.nio.ByteBuffer;
17 import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException;
18 import org.openhab.core.util.HexUtils;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * The {@link NibeHeatPumpProtocolStates} implements Nibe heat pump protocol state machine states.
25 * @author Pauli Anttila - Initial contribution
27 public enum NibeHeatPumpProtocolStates implements NibeHeatPumpProtocolState {
31 public boolean process(NibeHeatPumpProtocolContext context) {
32 if (context.buffer().hasRemaining()) {
33 byte b = context.buffer().get();
34 if (LOGGER.isTraceEnabled()) {
35 LOGGER.trace("Received byte: {}", String.format("%02X", b));
37 if (b == NibeHeatPumpProtocol.FRAME_START_CHAR_RES) {
38 LOGGER.trace("Frame start found");
39 context.msg().clear();
41 context.state(WAIT_DATA);
50 public boolean process(NibeHeatPumpProtocolContext context) {
51 if (context.buffer().hasRemaining()) {
52 if (context.msg().position() >= 100) {
53 LOGGER.trace("Too long message received, rewait start char");
54 context.state(WAIT_START);
56 byte b = context.buffer().get();
57 if (LOGGER.isTraceEnabled()) {
58 LOGGER.trace("Received byte: {}", String.format("%02X", b));
63 msgStatus status = checkNibeMessage(context.msg().asReadOnlyBuffer());
66 context.state(WAIT_START);
69 context.state(OK_MESSAGE_RECEIVED);
71 case VALID_BUT_NOT_READY:
74 } catch (NibeHeatPumpException e) {
75 LOGGER.trace("Error occured during parsing message: {}", e.getMessage());
76 context.state(CHECKSUM_FAILURE);
86 public boolean process(NibeHeatPumpProtocolContext context) {
88 byte[] data = new byte[context.msg().remaining()];
89 context.msg().get(data, 0, data.length);
90 if (LOGGER.isTraceEnabled()) {
91 LOGGER.trace("Received data (len={}): {}", data.length, HexUtils.bytesToHex(data));
93 if (NibeHeatPumpProtocol.isModbus40ReadTokenPdu(data)) {
94 context.state(READ_TOKEN_RECEIVED);
95 } else if (NibeHeatPumpProtocol.isModbus40WriteTokenPdu(data)) {
96 context.state(WRITE_TOKEN_RECEIVED);
99 context.msgReceived(data);
100 context.state(WAIT_START);
105 WRITE_TOKEN_RECEIVED {
107 public boolean process(NibeHeatPumpProtocolContext context) {
108 LOGGER.trace("Write token received");
109 context.sendWriteMsg();
110 context.state(WAIT_START);
114 READ_TOKEN_RECEIVED {
116 public boolean process(NibeHeatPumpProtocolContext context) {
117 LOGGER.trace("Read token received");
118 context.sendReadMsg();
119 context.state(WAIT_START);
125 public boolean process(NibeHeatPumpProtocolContext context) {
126 LOGGER.trace("CRC failure");
128 context.state(WAIT_START);
133 private enum msgStatus {
140 * Throws NibeHeatPumpException when checksum fails
142 private static msgStatus checkNibeMessage(ByteBuffer byteBuffer) throws NibeHeatPumpException {
144 int len = byteBuffer.remaining();
147 if (byteBuffer.get(0) != NibeHeatPumpProtocol.FRAME_START_CHAR_RES) {
148 return msgStatus.INVALID;
152 int datalen = byteBuffer.get(NibeHeatPumpProtocol.RES_OFFS_LEN);
154 // check if all bytes received
155 if (len < datalen + 6) {
156 return msgStatus.VALID_BUT_NOT_READY;
159 // calculate XOR checksum
160 byte calcChecksum = 0;
161 for (int i = 1; i < (datalen + 5); i++) {
162 calcChecksum ^= byteBuffer.get(i);
165 byte msgChecksum = byteBuffer.get(datalen + 5);
167 if (calcChecksum != msgChecksum) {
168 // if checksum is 0x5C (start character), heat pump seems to
169 // send 0xC5 checksum
170 if (calcChecksum != 0x5C && msgChecksum != 0xC5) {
171 throw new NibeHeatPumpException(String.format(
172 "Checksum failure, expected checksum 0x%02X was 0x%02X", msgChecksum, calcChecksum));
176 return msgStatus.VALID;
180 return msgStatus.VALID_BUT_NOT_READY;
183 private static final Logger LOGGER = LoggerFactory.getLogger(NibeHeatPumpProtocolStates.class);