Skip to content

Commit

Permalink
[lcn] Add configuration option invertOpenClosed to binary sensor chan…
Browse files Browse the repository at this point in the history
…nel (openhab#8102)

Closes openhab#8069

Signed-off-by: Thomas Weiler <[email protected]>
Signed-off-by: MPH80 <[email protected]>
  • Loading branch information
toweosp authored and MPH80 committed Aug 3, 2020
1 parent 83e2ff8 commit 05e6152
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 89 deletions.
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

0 comments on commit 05e6152

Please sign in to comment.