From e03d0a1d7184c2bff581ede315b7488796f7dac1 Mon Sep 17 00:00:00 2001 From: Martin Herbst Date: Mon, 10 Aug 2020 21:20:15 +0200 Subject: [PATCH] [homematic] Better support for rollershutters + several smaller fixes (#8242) * Set bridge state to offline for duty cycle = -1 * Use correct type Contact for state channel of tilt sensor * Support type Int64 for messages from Homegear * Correct support of UP/DOWN for rollershutter devices - Important: UP command must send 1.0 to the device instead of the maximum value returned by Homematic which is 1.01 and does not work. * Correct max data point value of certain HmIP devices - Some data points of HmIP are returning 1.01d as max value instead of the correct value of 1.0. * HM channel "Blind Transmitter" also indicates a rollershutter * Reduced log level for certain datapoints to avoid flooding the logs - HmIP devices introduced new channel configuration datapoints that can't currently be detected automatically, but the CCU sometimes sends events and the logs were flooded with warnings. * CuxD interface apparently does not support the rssiInfo method * equals method call not necessary because of enum usage * HM method does not return any data if HmIP only is used Fixes #6360 Fixes #6145 Fixes #5042 Fixes #5674 Fixes #8081 Fixes #6688 Fixes #5048 Fixes #6743 Fixes #5062 Signed-off-by: Martin Herbst --- .../AbstractHomematicGateway.java | 2 +- .../communicator/client/XmlRpcClient.java | 3 ++ .../communicator/message/BinRpcMessage.java | 10 ++++++ .../parser/GetParamsetParser.java | 9 ++++- .../converter/type/PercentTypeConverter.java | 29 +++++++++++----- .../handler/HomematicBridgeHandler.java | 34 +++++++++++++------ .../internal/misc/HomematicConstants.java | 3 ++ .../internal/type/MetadataUtils.java | 7 ++-- 8 files changed, 73 insertions(+), 24 deletions(-) diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java index 340bf933ce41d..95e1cd2270a53 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java @@ -530,7 +530,7 @@ public void loadDatapointValue(HmDatapoint dp) throws IOException { @Override public void loadRssiValues() throws IOException { for (HmInterface hmInterface : availableInterfaces.keySet()) { - if (hmInterface == HmInterface.RF || hmInterface == HmInterface.CUXD) { + if (hmInterface == HmInterface.RF) { List rssiInfos = getRpcClient(hmInterface).loadRssiInfo(hmInterface); for (HmRssiInfo hmRssiInfo : rssiInfos) { updateRssiInfo(hmRssiInfo.getAddress(), DATAPOINT_NAME_RSSI_DEVICE, hmRssiInfo.getDevice()); diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java index 82ece040f30a8..cf1e431c143e5 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java @@ -79,6 +79,9 @@ protected synchronized Object[] sendMessage(int port, RpcRequest request for (int rpcRetryCounter = 1; rpcRetryCounter <= MAX_RPC_RETRY; rpcRetryCounter++) { try { byte[] response = send(port, request); + if (response.length == 0 && "setInstallMode".equals(request.getMethodName())) { + return new Object[] {}; + } Object[] data = new XmlRpcResponse(new ByteArrayInputStream(response), config.getEncoding()) .getResponseData(); return new RpcResponseParser(request).parse(data); diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/message/BinRpcMessage.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/message/BinRpcMessage.java index be99a87716bfb..169cdebf813c2 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/message/BinRpcMessage.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/message/BinRpcMessage.java @@ -204,6 +204,13 @@ private int readInt() { return (new BigInteger(bi)).intValue(); } + private long readInt64() { + byte bi[] = new byte[8]; + System.arraycopy(binRpcData, offset, bi, 0, 8); + offset += 8; + return (new BigInteger(bi)).longValue(); + } + private String readString() throws UnsupportedEncodingException { int len = readInt(); offset += len; @@ -226,6 +233,9 @@ private Object readRpcValue() throws IOException { return bd.setScale(6, RoundingMode.HALF_DOWN).doubleValue(); case 5: return new Date(readInt() * 1000); + case 0xD1: + // Int64 + return new Long(readInt64()); case 0x100: // Array int numElements = readInt(); diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/GetParamsetParser.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/GetParamsetParser.java index 620d7722d533b..81a4824c1fe16 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/GetParamsetParser.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/GetParamsetParser.java @@ -18,6 +18,7 @@ import org.openhab.binding.homematic.internal.model.HmChannel; import org.openhab.binding.homematic.internal.model.HmDatapoint; import org.openhab.binding.homematic.internal.model.HmDatapointInfo; +import org.openhab.binding.homematic.internal.model.HmInterface; import org.openhab.binding.homematic.internal.model.HmParamsetType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +61,13 @@ public Void parse(Object[] message) throws IOException { boolean isHmSenMdirNextTrans = dpInfo.getName().equals("NEXT_TRANSMISSION") && (deviceType.startsWith("HM-Sen-MDIR-O") || deviceType.startsWith("HM-Sen-MDIR-WM55")); if (!isHmSenMdirNextTrans) { - logger.warn("Can't set value for datapoint '{}'", dpInfo); + if (dpInfo.getAddress().contains(":M_") + && channel.getDevice().getHmInterface() == HmInterface.HMIP) { + // These data points can't currently be recognized and therefore can't be created + logger.debug("Can't set value for channel configuration datapoint '{}'", dpInfo); + } else { + logger.warn("Can't set value for datapoint '{}'", dpInfo); + } } } } diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/converter/type/PercentTypeConverter.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/converter/type/PercentTypeConverter.java index 6330fa289a0bb..f9c2dc1c782f2 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/converter/type/PercentTypeConverter.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/converter/type/PercentTypeConverter.java @@ -20,6 +20,7 @@ import org.eclipse.smarthome.core.types.Type; import org.openhab.binding.homematic.internal.converter.ConverterException; import org.openhab.binding.homematic.internal.model.HmDatapoint; +import org.openhab.binding.homematic.internal.model.HmInterface; import org.openhab.binding.homematic.internal.type.MetadataUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,16 +48,17 @@ protected Object commandToBinding(Command command, HmDatapoint dp) throws Conver PercentType type = new PercentType(command.equals(OnOffType.ON) ? 100 : 0); return convertToBinding(type, dp); } else if (command.getClass() == UpDownType.class) { - int result = command.equals(UpDownType.UP) ? 100 : 0; - if (MetadataUtils.isRollerShutter(dp)) { - result = command.equals(UpDownType.UP) ? 0 : 100; - } - return convertToBinding(new PercentType(result), dp); + return convertToBinding(command.equals(UpDownType.UP) ? PercentType.ZERO : PercentType.HUNDRED, dp); } else { return super.commandToBinding(command, dp); } } + private double getCorrectedMaxValue(HmDatapoint dp) { + double max = dp.getMaxValue().doubleValue(); + return (max == 1.01 && dp.getChannel().getDevice().getHmInterface() == HmInterface.HMIP ? 1.0d : max); + } + @Override protected boolean toBindingValidation(HmDatapoint dp, Class typeClass) { return dp.isNumberType() && dp.getMaxValue() != null && dp.getMinValue() != null @@ -65,10 +67,16 @@ protected boolean toBindingValidation(HmDatapoint dp, Class type @Override protected Object toBinding(PercentType type, HmDatapoint dp) throws ConverterException { - Double number = (type.doubleValue() / 100) * dp.getMaxValue().doubleValue(); + double maxValue = getCorrectedMaxValue(dp); + Double number = (type.doubleValue() / 100) * maxValue; if (MetadataUtils.isRollerShutter(dp)) { - number = dp.getMaxValue().doubleValue() - number; + if (type == PercentType.HUNDRED) { // means DOWN + return dp.getMinValue().doubleValue(); + } else if (type == PercentType.ZERO) { // means UP + return maxValue; + } + return maxValue - number; } if (number < 0.0 || number > 100.0) { logger.warn("Percent value '{}' out of range, truncating value for {}", number, dp); @@ -89,8 +97,11 @@ protected boolean fromBindingValidation(HmDatapoint dp) { @Override protected PercentType fromBinding(HmDatapoint dp) throws ConverterException { Double number = ((Number) dp.getValue()).doubleValue(); - int percent = (int) ((100 / dp.getMaxValue().doubleValue()) * number); - + int percent = (int) (100 / getCorrectedMaxValue(dp) * number); + if (percent > 100) { + logger.warn("Percent value '{}' out of range, truncating value for {}", number, dp); + percent = 100; + } if (MetadataUtils.isRollerShutter(dp)) { percent = 100 - percent; } diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java index a14cb70199433..1d0f0467aadbf 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java @@ -60,6 +60,7 @@ public class HomematicBridgeHandler extends BaseBridgeHandler implements Homemat private final Logger logger = LoggerFactory.getLogger(HomematicBridgeHandler.class); private static final long REINITIALIZE_DELAY_SECONDS = 10; private static final int DUTY_CYCLE_RATIO_LIMIT = 99; + private static final int DUTY_CYCLE_DISCONNECTED = -1; private static SimplePortPool portPool = new SimplePortPool(); private final Object dutyCycleRatioUpdateLock = new Object(); @@ -349,19 +350,30 @@ public void onDutyCycleRatioUpdate(int dutyCycleRatio) { this.dutyCycleRatio = dutyCycleRatio; Channel dutyCycleRatioChannel = thing.getChannel(CHANNEL_TYPE_DUTY_CYCLE_RATIO); if (dutyCycleRatioChannel != null) { - this.updateState(dutyCycleRatioChannel.getUID(), new DecimalType(dutyCycleRatio)); + this.updateState(dutyCycleRatioChannel.getUID(), + new DecimalType(dutyCycleRatio < 0 ? 0 : dutyCycleRatio)); } - if (!isInDutyCycle && dutyCycleRatio >= DUTY_CYCLE_RATIO_LIMIT) { - logger.info("Duty cycle threshold exceeded by homematic bridge {}, it will go OFFLINE.", - thing.getUID()); - isInDutyCycle = true; - this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE); - } else if (isInDutyCycle && dutyCycleRatio < DUTY_CYCLE_RATIO_LIMIT) { - logger.info("Homematic bridge {} fell below duty cycle threshold and will come ONLINE again.", - thing.getUID()); - isInDutyCycle = false; - this.updateStatus(ThingStatus.ONLINE); + if (!isInDutyCycle) { + if (dutyCycleRatio >= DUTY_CYCLE_RATIO_LIMIT) { + logger.info("Duty cycle threshold exceeded by homematic bridge {}, it will go OFFLINE.", + thing.getUID()); + isInDutyCycle = true; + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DUTY_CYCLE); + } else if (dutyCycleRatio == DUTY_CYCLE_DISCONNECTED) { + logger.info( + "Duty cycle indicates a communication problem by homematic bridge {}, it will go OFFLINE.", + thing.getUID()); + isInDutyCycle = true; + this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + } + } else { + if (dutyCycleRatio < DUTY_CYCLE_RATIO_LIMIT && dutyCycleRatio != DUTY_CYCLE_DISCONNECTED) { + logger.info("Homematic bridge {}: duty cycle back to normal and bridge will come ONLINE again.", + thing.getUID()); + isInDutyCycle = false; + this.updateStatus(ThingStatus.ONLINE); + } } } } diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/misc/HomematicConstants.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/misc/HomematicConstants.java index 25f18ead58880..106888e4033e9 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/misc/HomematicConstants.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/misc/HomematicConstants.java @@ -40,6 +40,7 @@ public class HomematicConstants { public static final String CHANNEL_TYPE_RAINDETECTOR = "RAINDETECTOR"; public static final String CHANNEL_TYPE_POWERMETER = "POWERMETER"; public static final String CHANNEL_TYPE_SHUTTER_CONTACT = "SHUTTER_CONTACT"; + public static final String CHANNEL_TYPE_TILT_SENSOR = "TILT_SENSOR"; public static final String CHANNEL_TYPE_SENSOR = "SENSOR"; public static final String CHANNEL_TYPE_BLIND = "BLIND"; public static final String CHANNEL_TYPE_WINMATIC = "WINMATIC"; @@ -47,6 +48,8 @@ public class HomematicConstants { public static final String CHANNEL_TYPE_JALOUSIE = "JALOUSIE"; public static final String CHANNEL_TYPE_SHUTTER_TRANSMITTER = "SHUTTER_TRANSMITTER"; public static final String CHANNEL_TYPE_SHUTTER_VIRTUAL_RECEIVER = "SHUTTER_VIRTUAL_RECEIVER"; + public static final String CHANNEL_TYPE_BLIND_TRANSMITTER = "BLIND_TRANSMITTER"; + public static final String CHANNEL_TYPE_BLIND_VIRTUAL_RECEIVER = "BLIND_VIRTUAL_RECEIVER"; public static final String DATAPOINT_NAME_CONFIG_PENDING = "CONFIG_PENDING"; public static final String DATAPOINT_NAME_UPDATE_PENDING = "UPDATE_PENDING"; diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/type/MetadataUtils.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/type/MetadataUtils.java index 55f2f75cd0ebf..f13f852d9ad95 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/type/MetadataUtils.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/type/MetadataUtils.java @@ -277,7 +277,8 @@ public static String getItemType(HmDatapoint dp) { if (dp.isBooleanType()) { if (((dpName.equals(DATAPOINT_NAME_STATE) || dpName.equals(VIRTUAL_DATAPOINT_NAME_STATE_CONTACT)) - && channelType.equals(CHANNEL_TYPE_SHUTTER_CONTACT)) + && (channelType.equals(CHANNEL_TYPE_SHUTTER_CONTACT) + || channelType.contentEquals(CHANNEL_TYPE_TILT_SENSOR))) || (dpName.equals(DATAPOINT_NAME_SENSOR) && channelType.equals(CHANNEL_TYPE_SENSOR))) { return ITEM_TYPE_CONTACT; } else { @@ -344,8 +345,10 @@ public static String getItemType(HmDatapoint dp) { public static boolean isRollerShutter(HmDatapoint dp) { String channelType = dp.getChannel().getType(); return channelType.equals(CHANNEL_TYPE_BLIND) || channelType.equals(CHANNEL_TYPE_JALOUSIE) + || channelType.equals(CHANNEL_TYPE_BLIND_TRANSMITTER) || channelType.equals(CHANNEL_TYPE_SHUTTER_TRANSMITTER) - || channelType.equals(CHANNEL_TYPE_SHUTTER_VIRTUAL_RECEIVER); + || channelType.equals(CHANNEL_TYPE_SHUTTER_VIRTUAL_RECEIVER) + || channelType.contentEquals(CHANNEL_TYPE_BLIND_VIRTUAL_RECEIVER); } /**