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

[lcn] Add configuration option invertOpenClosed to binary sensor channel #8102

Merged
merged 5 commits into from
Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundles/org.openhab.binding.lcn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ S0 counter Channels need to be the pulses per kWh configured. If the value is le

The Rollershutter Channels provide the boolean parameter `invertUpDown`, which can be set to 'true' if the Up/Down wires are interchanged.

The Binarysensor Channels provide the boolean parameter `invertState`, which can be set to 'true' if the binary sensor connected uses reverse logic for signaling open/closed.

### Transponder

LCN transponder readers can be integrated in openHAB e.g. for access control.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.openhab.binding.lcn.internal.connection.ModInfo;
import org.openhab.binding.lcn.internal.converter.Converter;
import org.openhab.binding.lcn.internal.converter.Converters;
import org.openhab.binding.lcn.internal.converter.InversionConverter;
import org.openhab.binding.lcn.internal.converter.S0Converter;
import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler;
import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler;
Expand All @@ -68,21 +69,22 @@
@NonNullByDefault
public class LcnModuleHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class);
private static final Map<String, Converter> CONVERTERS = new HashMap<>();
private static final Map<String, Converter> VALUE_CONVERTERS = new HashMap<>();
private static final InversionConverter INVERSION_CONVERTER = new InversionConverter();
private static final String SERIAL_NUMBER = "serialNumber";
private @Nullable LcnAddrMod moduleAddress;
private final Map<LcnChannelGroup, @Nullable AbstractLcnModuleSubHandler> subHandlers = new HashMap<>();
private final List<AbstractLcnModuleSubHandler> metadataSubHandlers = new ArrayList<>();
private final Map<ChannelUID, @Nullable Converter> converters = new HashMap<>();

static {
CONVERTERS.put("temperature", Converters.TEMPERATURE);
CONVERTERS.put("light", Converters.LIGHT);
CONVERTERS.put("co2", Converters.CO2);
CONVERTERS.put("current", Converters.CURRENT);
CONVERTERS.put("voltage", Converters.VOLTAGE);
CONVERTERS.put("angle", Converters.ANGLE);
CONVERTERS.put("windspeed", Converters.WINDSPEED);
VALUE_CONVERTERS.put("temperature", Converters.TEMPERATURE);
VALUE_CONVERTERS.put("light", Converters.LIGHT);
VALUE_CONVERTERS.put("co2", Converters.CO2);
VALUE_CONVERTERS.put("current", Converters.CURRENT);
VALUE_CONVERTERS.put("voltage", Converters.VOLTAGE);
VALUE_CONVERTERS.put("angle", Converters.ANGLE);
VALUE_CONVERTERS.put("windspeed", Converters.WINDSPEED);
}

public LcnModuleHandler(Thing thing) {
Expand Down Expand Up @@ -129,24 +131,32 @@ && getThing().getProperties().get(SERIAL_NUMBER).isEmpty()) {
metadataSubHandlers.add(new LcnModuleMetaAckSubHandler(this, info));
metadataSubHandlers.add(new LcnModuleMetaFirmwareSubHandler(this, info));

// initialize variable value converters
// initialize converters
for (Channel channel : thing.getChannels()) {
Object unitObject = channel.getConfiguration().get("unit");
Object parameterObject = channel.getConfiguration().get("parameter");
Object invertConfig = channel.getConfiguration().get("invertState");

// Initialize value converters
if (unitObject instanceof String) {
switch ((String) unitObject) {
case "power":
case "energy":
converters.put(channel.getUID(), new S0Converter(parameterObject));
break;
default:
if (CONVERTERS.containsKey(unitObject)) {
converters.put(channel.getUID(), CONVERTERS.get(unitObject));
if (VALUE_CONVERTERS.containsKey(unitObject)) {
converters.put(channel.getUID(), VALUE_CONVERTERS.get(unitObject));
}
break;
}
}

// Initialize inversion converter
if (invertConfig instanceof Boolean && invertConfig.equals(true)) {
converters.put(channel.getUID(), INVERSION_CONVERTER);
}

}

// module is assumed as online, when the corresponding Bridge (PckGatewayHandler) is online.
Expand Down Expand Up @@ -316,6 +326,7 @@ public void updateChannel(LcnChannelGroup channelGroup, String channelId, State
if (converter != null) {
convertedState = converter.onStateUpdateFromHandler(state);
}

updateState(channelUid, convertedState);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -820,5 +820,4 @@ private static int convertMsecToLCNTimer(double ms) {
}
return lcntimer;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,27 @@
*/
package org.openhab.binding.lcn.internal.converter;

import java.util.function.Function;

import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.types.State;
import org.openhab.binding.lcn.internal.common.LcnException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Base class for all LCN variable value converters.
* Base class for all converters.
*
* @author Fabian Wolter - Initial Contribution
*/
@NonNullByDefault
public class Converter {
private final Logger logger = LoggerFactory.getLogger(Converter.class);
private @Nullable final Unit<?> unit;
private final Function<Long, Double> toHuman;
private final Function<Double, Long> toNative;

public Converter(@Nullable Unit<?> unit, Function<Long, Double> toHuman, Function<Double, Long> toNative) {
this.unit = unit;
this.toHuman = toHuman;
this.toNative = toNative;
}

/**
* Converts the given human readable value into the native LCN value.
*
* @param humanReadableValue the value to convert
* @return the native value
*/
protected long toNative(double humanReadableValue) {
return toNative.apply(humanReadableValue);
}

/**
* Converts the given native LCN value into a human readable value.
* Converts a state update from the Thing into a human readable representational State.
*
* @param nativeValue the value to convert
* @return the human readable value
* @param state from the Thing
* @return human readable representational State
*/
protected double toHumanReadable(long nativeValue) {
return toHuman.apply(nativeValue);
public State onStateUpdateFromHandler(State state) {
return state;
}

/**
Expand All @@ -70,7 +42,7 @@ protected double toHumanReadable(long nativeValue) {
* @return the native LCN value
*/
public DecimalType onCommandFromItem(double humanReadable) {
return new DecimalType(toNative(humanReadable));
return new DecimalType(humanReadable);
}

/**
Expand All @@ -81,38 +53,6 @@ public DecimalType onCommandFromItem(double humanReadable) {
* @throws LcnException when the value could not be converted to the base unit
*/
public DecimalType onCommandFromItem(QuantityType<?> quantityType) throws LcnException {
Unit<?> localUnit = unit;
if (localUnit == null) {
return onCommandFromItem(quantityType.doubleValue());
}

QuantityType<?> quantityInBaseUnit = quantityType.toUnit(localUnit);

if (quantityInBaseUnit != null) {
return onCommandFromItem(quantityInBaseUnit.doubleValue());
} else {
throw new LcnException(quantityType + ": Incompatible Channel unit configured: " + localUnit);
}
}

/**
* Converts a state update from the Thing into a human readable unit.
*
* @param state from the Thing
* @return human readable State
*/
public State onStateUpdateFromHandler(State state) {
State result = state;

if (state instanceof DecimalType) {
Unit<?> localUnit = unit;
if (localUnit != null) {
result = QuantityType.valueOf(toHumanReadable(((DecimalType) state).longValue()), localUnit);
}
} else {
logger.warn("Unexpected state type: {}", state.getClass().getSimpleName());
}

return result;
return onCommandFromItem(quantityType.doubleValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ public class Converters {
public static final Converter IDENTITY;

static {
TEMPERATURE = new Converter(SIUnits.CELSIUS, n -> (n - 1000) / 10d, h -> Math.round(h * 10) + 1000);
LIGHT = new Converter(SmartHomeUnits.LUX, Converters::lightToHumanReadable, Converters::lightToNative);
CO2 = new Converter(SmartHomeUnits.PARTS_PER_MILLION, n -> (double) n, Math::round);
CURRENT = new Converter(SmartHomeUnits.AMPERE, n -> n / 100d, h -> Math.round(h * 100));
VOLTAGE = new Converter(SmartHomeUnits.VOLT, n -> n / 400d, h -> Math.round(h * 400));
ANGLE = new Converter(SmartHomeUnits.DEGREE_ANGLE, n -> (n - 1000) / 10d, Converters::angleToNative);
WINDSPEED = new Converter(SmartHomeUnits.METRE_PER_SECOND, n -> n / 10d, h -> Math.round(h * 10));
IDENTITY = new Converter(null, n -> (double) n, Math::round);
TEMPERATURE = new ValueConverter(SIUnits.CELSIUS, n -> (n - 1000) / 10d, h -> Math.round(h * 10) + 1000);
LIGHT = new ValueConverter(SmartHomeUnits.LUX, Converters::lightToHumanReadable, Converters::lightToNative);
CO2 = new ValueConverter(SmartHomeUnits.PARTS_PER_MILLION, n -> (double) n, Math::round);
CURRENT = new ValueConverter(SmartHomeUnits.AMPERE, n -> n / 100d, h -> Math.round(h * 100));
VOLTAGE = new ValueConverter(SmartHomeUnits.VOLT, n -> n / 400d, h -> Math.round(h * 400));
ANGLE = new ValueConverter(SmartHomeUnits.DEGREE_ANGLE, n -> (n - 1000) / 10d, Converters::angleToNative);
WINDSPEED = new ValueConverter(SmartHomeUnits.METRE_PER_SECOND, n -> n / 10d, h -> Math.round(h * 10));
IDENTITY = new ValueConverter(null, n -> (double) n, Math::round);
}

private static long lightToNative(double value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.lcn.internal.converter;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.OpenClosedType;
import org.eclipse.smarthome.core.library.types.UpDownType;
import org.eclipse.smarthome.core.types.State;

/**
* Converter for all states representing antonyms.
*
* @author Thomas Weiler - Initial Contribution
*/
@NonNullByDefault
public class InversionConverter extends Converter {
/**
* Converts a state into its antonym where applicable.
*
* @param state to be inverted
* @return inverted state
*/
@Override
public State onStateUpdateFromHandler(State state) {
State convertedState = state;

if (state instanceof OpenClosedType) {
convertedState = state.equals(OpenClosedType.OPEN) ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
} else if (state instanceof OnOffType) {
convertedState = state.equals(OnOffType.ON) ? OnOffType.OFF : OnOffType.ON;
} else if (state instanceof UpDownType) {
convertedState = state.equals(UpDownType.UP) ? UpDownType.DOWN : UpDownType.UP;
}

return convertedState;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* @author Fabian Wolter - Initial Contribution
*/
@NonNullByDefault
public class S0Converter extends Converter {
public class S0Converter extends ValueConverter {
private final Logger logger = LoggerFactory.getLogger(S0Converter.class);
protected double pulsesPerKwh;

Expand Down
Loading