[comfoair] improve data handling (#9401)

* improve data handling and suspend
* fix command position
* add ACK to subResponse
* fix data position
* fix reactivation, trim too long responses
* ensure correct setting of activation channel
* Remove @SuppressWarnings(null), which is not needed anymore
* Remove updateState on dispose

Signed-off-by: Hans Böhm <h.boehm@gmx.at>
This commit is contained in:
boehan 2020-12-28 18:03:54 +01:00 committed by GitHub
parent 1cc6577b20
commit 2ea32faa5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 39 deletions

View File

@ -75,7 +75,7 @@ public enum ComfoAirCommandType {
*/
ACTIVATE(ComfoAirBindingConstants.CG_CONTROL_PREFIX + ComfoAirBindingConstants.CHANNEL_ACTIVATE,
DataTypeBoolean.getInstance(), new int[] { 0x03 }, Constants.REQUEST_SET_RS232, 1, 0,
Constants.EMPTY_TYPE_ARRAY, Constants.REPLY_SET_RS232, Constants.REPLY_SET_RS232, new int[] { 0 }, 0x03),
Constants.EMPTY_TYPE_ARRAY),
MENU20_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU20_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x01),
@ -771,7 +771,7 @@ public enum ComfoAirCommandType {
public static @Nullable ComfoAirCommand getReadCommand(String key) {
ComfoAirCommandType commandType = getCommandTypeByKey(key);
if (commandType != null) {
if (commandType != null && commandType.readCommand > 0) {
return new ComfoAirCommand(key);
}
return null;
@ -891,7 +891,6 @@ public enum ComfoAirCommandType {
return null;
}
@SuppressWarnings("null")
private static void uniteCommandsMap(Map<Integer, ComfoAirCommand> commands, ComfoAirCommandType commandType) {
if (commandType.readReplyCommand != 0) {
int replyCmd = commandType.readReplyCommand;

View File

@ -24,6 +24,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.datatypes.ComfoAirDataType;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
@ -55,6 +56,8 @@ public class ComfoAirHandler extends BaseThingHandler {
private @Nullable ComfoAirSerialConnector comfoAirConnector;
public static final int BAUDRATE = 9600;
public static final String ACTIVATE_CHANNEL_ID = ComfoAirBindingConstants.CG_CONTROL_PREFIX
+ ComfoAirBindingConstants.CHANNEL_ACTIVATE;
public ComfoAirHandler(Thing thing, final SerialPortManager serialPortManager) {
super(thing);
@ -64,29 +67,36 @@ public class ComfoAirHandler extends BaseThingHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
String channelId = channelUID.getId();
if (comfoAirConnector != null) {
boolean isActive = !comfoAirConnector.getIsSuspended();
if (command instanceof RefreshType) {
Channel channel = this.thing.getChannel(channelUID);
if (channel != null) {
updateChannelState(channel);
}
} else {
ComfoAirCommand changeCommand = ComfoAirCommandType.getChangeCommand(channelId, command);
if (isActive || channelId.equals(ACTIVATE_CHANNEL_ID)) {
if (command instanceof RefreshType) {
Channel channel = this.thing.getChannel(channelUID);
if (channel != null) {
updateChannelState(channel);
}
} else {
ComfoAirCommand changeCommand = ComfoAirCommandType.getChangeCommand(channelId, command);
if (changeCommand != null) {
Set<String> keysToUpdate = getThing().getChannels().stream().map(Channel::getUID).filter(this::isLinked)
.map(ChannelUID::getId).collect(Collectors.toSet());
sendCommand(changeCommand, channelId);
if (changeCommand != null) {
Set<String> keysToUpdate = getThing().getChannels().stream().map(Channel::getUID)
.filter(this::isLinked).map(ChannelUID::getId).collect(Collectors.toSet());
sendCommand(changeCommand, channelId);
Collection<ComfoAirCommand> affectedReadCommands = ComfoAirCommandType
.getAffectedReadCommands(channelId, keysToUpdate);
Collection<ComfoAirCommand> affectedReadCommands = ComfoAirCommandType
.getAffectedReadCommands(channelId, keysToUpdate);
if (affectedReadCommands.size() > 0) {
Runnable updateThread = new AffectedItemsUpdateThread(affectedReadCommands);
affectedItemsPoller = scheduler.schedule(updateThread, 3, TimeUnit.SECONDS);
if (affectedReadCommands.size() > 0) {
Runnable updateThread = new AffectedItemsUpdateThread(affectedReadCommands);
affectedItemsPoller = scheduler.schedule(updateThread, 3, TimeUnit.SECONDS);
}
} else {
logger.warn("Unhandled command type: {}, channelId: {}", command.toString(), channelId);
}
}
} else {
logger.warn("Unhandled command type: {}, channelId: {}", command.toString(), channelId);
logger.debug("Binding control is currently not active.");
}
}
}
@ -115,6 +125,8 @@ public class ComfoAirHandler extends BaseThingHandler {
updateStatus(ThingStatus.ONLINE);
pullDeviceProperties();
updateState(ACTIVATE_CHANNEL_ID, OnOffType.ON);
List<Channel> channels = this.thing.getChannels();
poller = scheduler.scheduleWithFixedDelay(() -> {
@ -159,14 +171,38 @@ public class ComfoAirHandler extends BaseThingHandler {
if (!isLinked(channel.getUID())) {
return;
}
String commandKey = channel.getUID().getId();
ComfoAirCommand readCommand = ComfoAirCommandType.getReadCommand(commandKey);
if (readCommand != null) {
scheduler.submit(() -> {
State state = sendCommand(readCommand, commandKey);
if (comfoAirConnector != null) {
boolean isActive = !comfoAirConnector.getIsSuspended();
String commandKey = channel.getUID().getId();
if (commandKey.equals(ACTIVATE_CHANNEL_ID)) {
State state = OnOffType.from(isActive);
updateState(channel.getUID(), state);
});
return;
}
if (!isActive) {
logger.debug("Binding control is currently not active.");
return;
}
ComfoAirCommand readCommand = ComfoAirCommandType.getReadCommand(commandKey);
if (readCommand != null && readCommand.getRequestCmd() != null) {
scheduler.submit(() -> {
State state = sendCommand(readCommand, commandKey);
updateState(channel.getUID(), state);
});
}
}
}
@Override
public void channelLinked(ChannelUID channelUID) {
super.channelLinked(channelUID);
Channel channel = this.thing.getChannel(channelUID);
if (channel != null) {
updateChannelState(channel);
}
}

View File

@ -47,8 +47,7 @@ public class ComfoAirSerialConnector {
private static final byte[] END = { CTRL, (byte) 0x0f };
private static final byte[] ACK = { CTRL, (byte) 0xf3 };
private static final int RS232_ENABLED_VALUE = 0x03;
private static final int RS232_DISABLED_VALUE = 0x00;
private static final int MAX_RETRIES = 5;
private boolean isSuspended = true;
@ -150,14 +149,17 @@ public class ComfoAirSerialConnector {
*/
public synchronized int[] sendCommand(ComfoAirCommand command, int[] preRequestData) {
Integer requestCmd = command.getRequestCmd();
Integer requestValue = command.getRequestValue();
int retry = 0;
if (requestCmd != null) {
// Switch support for app or ccease control
if (requestCmd == ComfoAirCommandType.Constants.REQUEST_SET_RS232) {
isSuspended = !isSuspended;
} else if (requestCmd == ComfoAirCommandType.Constants.REPLY_SET_RS232) {
return new int[] { isSuspended ? RS232_DISABLED_VALUE : RS232_ENABLED_VALUE };
if (requestCmd == ComfoAirCommandType.Constants.REQUEST_SET_RS232 && requestValue != null) {
if (requestValue == 1) {
isSuspended = false;
} else if (requestValue == 0) {
isSuspended = true;
}
} else if (isSuspended) {
logger.trace("Ignore cmd. Service is currently suspended");
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
@ -226,13 +228,44 @@ public class ComfoAirSerialConnector {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
boolean isValidData = false;
// check for start and end sequence and if the response cmd
// matches
// 11 is the minimum response length with one data byte
if (responseBlock.length >= 11 && responseBlock[2] == START[0] && responseBlock[3] == START[1]
&& responseBlock[responseBlock.length - 2] == END[0]
&& responseBlock[responseBlock.length - 1] == END[1]
&& (responseBlock[5] & 0xff) == command.getReplyCmd()) {
&& responseBlock[responseBlock.length - 1] == END[1]) {
if ((responseBlock[5] & 0xff) == command.getReplyCmd()) {
isValidData = true;
} else {
int startIndex = -1;
int endIndex = -1;
for (int i = 4; i < (responseBlock.length - 11) && endIndex < 0; i++) {
if (responseBlock[i] == START[0] && responseBlock[i + 1] == START[1]
&& ((responseBlock[i + 3] & 0xff) == command.getReplyCmd())) {
startIndex = i;
for (int j = startIndex; j < responseBlock.length; j++) {
if (responseBlock[j] == END[0] && responseBlock[j + 1] == END[1]) {
endIndex = j + 1;
break;
}
}
}
}
if (startIndex > -1 && endIndex > -1) {
byte[] subResponse = new byte[endIndex - startIndex + 3];
System.arraycopy(responseBlock, 0, subResponse, 0, 2);
System.arraycopy(responseBlock, startIndex, subResponse, 2, subResponse.length - 2);
responseBlock = subResponse;
isValidData = true;
}
}
}
if (isValidData) {
if (logger.isTraceEnabled()) {
logger.trace("receive RAW DATA: {}", dumpData(responseBlock));
}
@ -294,9 +327,9 @@ public class ComfoAirSerialConnector {
logger.warn("Transmission was interrupted: {}", e.getMessage());
throw new RuntimeException(e);
}
} while (retry++ < 5);
} while (retry++ < MAX_RETRIES);
if (retry == 5) {
if (retry >= MAX_RETRIES) {
logger.debug("Unable to send command. {} retries failed.", retry);
}
}
@ -375,6 +408,10 @@ public class ComfoAirSerialConnector {
}
cleanedBuffer[pos] = processBuffer[i];
pos++;
// Trim unrequested data in response
if (END[0] == processBuffer[i + 1] && END[1] == processBuffer[i + 2]) {
break;
}
}
return Arrays.copyOf(cleanedBuffer, pos);
}
@ -560,4 +597,8 @@ public class ComfoAirSerialConnector {
}
return 0;
}
public boolean getIsSuspended() {
return isSuspended;
}
}

View File

@ -49,7 +49,6 @@ public class DataTypeBoolean implements ComfoAirDataType {
} else {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
int readReplyDataBits = commandType.getReadReplyDataBits();
int readCommand = commandType.getReadCommand();
boolean result;
if (readReplyDataPos != null && readReplyDataPos[0] < data.length) {
@ -59,8 +58,6 @@ public class DataTypeBoolean implements ComfoAirDataType {
result = (data[readReplyDataPos[0]] & readReplyDataBits) == readReplyDataBits;
}
return OnOffType.from(result);
} else if (readCommand == 0) {
return OnOffType.OFF; // handle write-only commands (resets)
} else {
return UnDefType.NULL;
}