Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[homematic] Better support for rollershutters + several smaller fixes #8242

Merged
merged 10 commits into from
Aug 10, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -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<HmRssiInfo> rssiInfos = getRpcClient(hmInterface).loadRssiInfo(hmInterface);
for (HmRssiInfo hmRssiInfo : rssiInfos) {
updateRssiInfo(hmRssiInfo.getAddress(), DATAPOINT_NAME_RSSI_DEVICE, hmRssiInfo.getDevice());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ protected synchronized Object[] sendMessage(int port, RpcRequest<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<? extends Type> typeClass) {
return dp.isNumberType() && dp.getMaxValue() != null && dp.getMinValue() != null
Expand All @@ -65,10 +67,16 @@ protected boolean toBindingValidation(HmDatapoint dp, Class<? extends Type> 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);
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ 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";
public static final String CHANNEL_TYPE_AKKU = "AKKU";
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}

/**
Expand Down