mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[knx] Code rework (#17420)
* [knx] Code rework Signed-off-by: Holger Friedrich <mail@holger-friedrich.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
87303e4356
commit
ada40c1fc5
@ -131,7 +131,7 @@ public abstract class KNXChannel {
|
||||
return new WriteSpecImpl(entry.getValue(), dpt, command);
|
||||
}
|
||||
}
|
||||
// if we didn't find a match, check if we find a sub-type match
|
||||
// if we didn't find a match, check if we find a subtype match
|
||||
for (Map.Entry<String, GroupAddressConfiguration> entry : groupAddressConfigurations.entrySet()) {
|
||||
String dpt = Objects.requireNonNullElse(entry.getValue().getDPT(), getDefaultDPT(entry.getKey()));
|
||||
Set<Class<? extends Type>> expectedTypeClasses = DPTUtil.getAllowedTypes(dpt);
|
||||
|
@ -56,7 +56,7 @@ public final class KNXChannelFactory {
|
||||
.map(Map.Entry::getValue).findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException(channelTypeUID + " is not a valid channel type ID"));
|
||||
|
||||
// typecast to avoid warning about unsafe return type; we know that the lookup returns non null values
|
||||
// typecast to avoid warning about unsafe return type; we know that the lookup returns non-null values
|
||||
return (KNXChannel) supplier.apply(channel);
|
||||
}
|
||||
}
|
||||
|
@ -46,18 +46,12 @@ class TypeColor extends KNXChannel {
|
||||
|
||||
@Override
|
||||
protected String getDefaultDPT(String gaConfigKey) {
|
||||
if (gaConfigKey.equals(HSB_GA)) {
|
||||
return DPTXlatorRGB.DPT_RGB.getID();
|
||||
}
|
||||
if (gaConfigKey.equals(INCREASE_DECREASE_GA)) {
|
||||
return DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID();
|
||||
}
|
||||
if (gaConfigKey.equals(SWITCH_GA)) {
|
||||
return DPTXlatorBoolean.DPT_SWITCH.getID();
|
||||
}
|
||||
if (gaConfigKey.equals(POSITION_GA)) {
|
||||
return DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
}
|
||||
throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
return switch (gaConfigKey) {
|
||||
case HSB_GA -> DPTXlatorRGB.DPT_RGB.getID();
|
||||
case INCREASE_DECREASE_GA -> DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID();
|
||||
case SWITCH_GA -> DPTXlatorBoolean.DPT_SWITCH.getID();
|
||||
case POSITION_GA -> DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
default -> throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ package org.openhab.binding.knx.internal.channel;
|
||||
import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -45,15 +44,11 @@ class TypeDimmer extends KNXChannel {
|
||||
|
||||
@Override
|
||||
protected String getDefaultDPT(String gaConfigKey) {
|
||||
if (Objects.equals(gaConfigKey, INCREASE_DECREASE_GA)) {
|
||||
return DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID();
|
||||
}
|
||||
if (Objects.equals(gaConfigKey, SWITCH_GA)) {
|
||||
return DPTXlatorBoolean.DPT_SWITCH.getID();
|
||||
}
|
||||
if (Objects.equals(gaConfigKey, POSITION_GA)) {
|
||||
return DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
}
|
||||
throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
return switch (gaConfigKey) {
|
||||
case INCREASE_DECREASE_GA -> DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID();
|
||||
case SWITCH_GA -> DPTXlatorBoolean.DPT_SWITCH.getID();
|
||||
case POSITION_GA -> DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
default -> throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ package org.openhab.binding.knx.internal.channel;
|
||||
import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -45,15 +44,11 @@ class TypeRollershutter extends KNXChannel {
|
||||
|
||||
@Override
|
||||
protected String getDefaultDPT(String gaConfigKey) {
|
||||
if (Objects.equals(gaConfigKey, UP_DOWN_GA)) {
|
||||
return DPTXlatorBoolean.DPT_UPDOWN.getID();
|
||||
}
|
||||
if (Objects.equals(gaConfigKey, STOP_MOVE_GA)) {
|
||||
return DPTXlatorBoolean.DPT_START.getID();
|
||||
}
|
||||
if (Objects.equals(gaConfigKey, POSITION_GA)) {
|
||||
return DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
}
|
||||
throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
return switch (gaConfigKey) {
|
||||
case UP_DOWN_GA -> DPTXlatorBoolean.DPT_UPDOWN.getID();
|
||||
case STOP_MOVE_GA -> DPTXlatorBoolean.DPT_START.getID();
|
||||
case POSITION_GA -> DPTXlator8BitUnsigned.DPT_SCALING.getID();
|
||||
default -> throw new IllegalArgumentException("GA configuration '" + gaConfigKey + "' is not supported");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ public abstract class AbstractKNXClient implements NetworkLinkListener, KNXClien
|
||||
// Protected ctor using given ManagementClientImpl is available (custom class to be inherited)
|
||||
managementProcedures = new CustomManagementProceduresImpl(managementClient, tl);
|
||||
|
||||
// OH helper for reading device info, based on managementClient above
|
||||
// OpenHab helper for reading device info, based on managementClient above
|
||||
deviceInfoClient = new DeviceInfoClientImpl(managementClient);
|
||||
|
||||
// ProcessCommunicator provides main KNX communication (Calimero).
|
||||
|
@ -51,23 +51,7 @@ public class DeviceInspector {
|
||||
private final DeviceInfoClient client;
|
||||
private final IndividualAddress address;
|
||||
|
||||
public static class Result {
|
||||
private final Map<String, String> properties;
|
||||
private final Set<GroupAddress> groupAddresses;
|
||||
|
||||
public Result(Map<String, String> properties, Set<GroupAddress> groupAddresses) {
|
||||
super();
|
||||
this.properties = properties;
|
||||
this.groupAddresses = groupAddresses;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public Set<GroupAddress> getGroupAddresses() {
|
||||
return groupAddresses;
|
||||
}
|
||||
public record Result(Map<String, String> properties, Set<GroupAddress> groupAddresses) {
|
||||
}
|
||||
|
||||
public DeviceInspector(DeviceInfoClient client, IndividualAddress address) {
|
||||
@ -114,7 +98,7 @@ public class DeviceInspector {
|
||||
* task immediately on connection loss or thing deconstruction.
|
||||
*
|
||||
* @param address Individual address of KNX device
|
||||
* @return List of device properties
|
||||
* @return Map of device properties
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private Map<String, String> readDeviceProperties(IndividualAddress address) throws InterruptedException {
|
||||
@ -179,7 +163,7 @@ public class DeviceInspector {
|
||||
if (!maxApdu.isEmpty()) {
|
||||
logger.trace("Max APDU of device {} is {} bytes (routing)", address, maxApdu);
|
||||
} else {
|
||||
// fallback: MAX_APDU_LENGTH; if availble set the default is 14 according to spec
|
||||
// fallback: MAX_APDU_LENGTH; if available set the default is 14 according to spec
|
||||
Thread.sleep(OPERATION_INTERVAL);
|
||||
try {
|
||||
byte[] result = getClient().readDeviceProperties(address, ADDRESS_TABLE_OBJECT,
|
||||
@ -247,7 +231,7 @@ public class DeviceInspector {
|
||||
logger.debug("Identified device {} as \"{}\"", address, result);
|
||||
ret.put(FRIENDLY_NAME, result);
|
||||
} else {
|
||||
// this is due to devices which have a buggy implememtation (and show a broken string also
|
||||
// this is due to devices which have a buggy implementation (and show a broken string also
|
||||
// in ETS tool)
|
||||
logger.debug("Ignoring FRIENDLY_NAME of device {} as it contains non-printable characters",
|
||||
address);
|
||||
@ -288,7 +272,7 @@ public class DeviceInspector {
|
||||
* Currently only data from DD0 is returned; DD2 is just logged in debug mode.
|
||||
*
|
||||
* @param address Individual address of KNX device
|
||||
* @return List of device properties
|
||||
* @return Map of device properties
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private Map<String, String> readDeviceDescription(IndividualAddress address) throws InterruptedException {
|
||||
@ -315,7 +299,7 @@ public class DeviceInspector {
|
||||
if (data != null) {
|
||||
try {
|
||||
final DD2 dd = DeviceDescriptor.DD2.from(data);
|
||||
logger.debug("The device with address {} is has DD2 {}", address, dd.toString());
|
||||
logger.debug("The device with address {} is has DD2 {}", address, dd);
|
||||
} catch (KNXIllegalArgumentException e) {
|
||||
logger.warn("Can not parse device descriptor 2 of device with address {}: {}", address,
|
||||
e.getMessage());
|
||||
@ -342,21 +326,14 @@ public class DeviceInspector {
|
||||
}
|
||||
|
||||
private static String getMediumType(int type) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
return "TP";
|
||||
case 1:
|
||||
return "PL";
|
||||
case 2:
|
||||
return "RF";
|
||||
case 3:
|
||||
return "TP0 (deprecated)";
|
||||
case 4:
|
||||
return "PL123 (deprecated)";
|
||||
case 5:
|
||||
return "IP";
|
||||
default:
|
||||
return "unknown (" + type + ")";
|
||||
}
|
||||
return switch (type) {
|
||||
case 0 -> "TP";
|
||||
case 1 -> "PL";
|
||||
case 2 -> "RF";
|
||||
case 3 -> "TP0 (deprecated)";
|
||||
case 4 -> "PL123 (deprecated)";
|
||||
case 5 -> "IP";
|
||||
default -> "unknown (" + type + ")";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public interface InboundSpec {
|
||||
/**
|
||||
* Get the affected group addresses.
|
||||
*
|
||||
* @return a list of group addresses.
|
||||
* @return a Set of group addresses.
|
||||
*/
|
||||
Set<GroupAddress> getGroupAddresses();
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class SerialClient extends AbstractKNXClient {
|
||||
/**
|
||||
* try automatic detection of cEMI devices via the PEI identification frame
|
||||
*
|
||||
* @implNote This is based on an vendor specific extension and may not work for other devices.
|
||||
* @implNote This is based on a vendor specific extension and may not work for other devices.
|
||||
*/
|
||||
protected boolean detectCemi() throws InterruptedException {
|
||||
final byte[] peiIdentifyReqFrame = { (byte) 0xa7 };
|
||||
|
@ -42,7 +42,7 @@ import tuwien.auto.calimero.serial.spi.SerialCom;
|
||||
* {@literal @}ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file
|
||||
* /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom
|
||||
* to register SerialTransportAdapter to the service loader.
|
||||
* Additional attributes for SerialTansportAdapter can be specified as well, e.g.
|
||||
* Additional attributes for SerialTransportAdapter can be specified as well, e.g.
|
||||
* attribute = { "position=1" }
|
||||
* and will be part of MANIFEST.MF
|
||||
*
|
||||
|
@ -54,7 +54,7 @@ public class KNXCommandExtension extends AbstractConsoleCommandExtension impleme
|
||||
if (args.length == 1 && CMD_LIST_UNKNOWN_GA.equalsIgnoreCase(args[0])) {
|
||||
for (KNXBridgeBaseThingHandler bridgeHandler : knxHandlerFactory.getBridges()) {
|
||||
console.println("KNX bridge \"" + bridgeHandler.getThing().getLabel()
|
||||
+ "\": group address, type, number of bytes, and number of occurence since last reload of binding:");
|
||||
+ "\": group address, type, number of bytes, and number of occurrence since last reload of binding:");
|
||||
for (Entry<String, Long> entry : bridgeHandler.getCommandExtensionData().unknownGA().entrySet()) {
|
||||
console.println(entry.getKey() + " " + entry.getValue());
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ public class DPTUnits {
|
||||
DPT_UNIT_MAP.put(DPTXlator4ByteFloat.DPT_ELECTROMAGNETIC_MOMENT.getID(),
|
||||
Units.AMPERE.multiply(SIUnits.SQUARE_METRE).toString());
|
||||
|
||||
// 64 bit signed (DPT 29)
|
||||
// 64-bit signed (DPT 29)
|
||||
DPT_UNIT_MAP.put(DPTXlator64BitSigned.DPT_REACTIVE_ENERGY.getID(), Units.VAR_HOUR.toString());
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ import tuwien.auto.calimero.dptxlator.DPTXlatorString;
|
||||
public class DPTUtil {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DPTUtil.class);
|
||||
|
||||
// DPT: "123.001", 1-3 digits main type (no leading zero), optional sub-type 3-4 digits (leading zeros allowed)
|
||||
// DPT: "123.001", 1-3 digits main type (no leading zero), optional subtype 3-4 digits (leading zeros allowed)
|
||||
public static final Pattern DPT_PATTERN = Pattern.compile("^(?<main>[1-9][0-9]{0,2})(?:\\.(?<sub>\\d{3,5}))?$");
|
||||
|
||||
// used to map vendor-specific data to standard DPT
|
||||
|
@ -241,32 +241,34 @@ public class ValueDecoder {
|
||||
|
||||
private static Type handleDpt1(String subType, DPTXlator translator, Class<? extends Type> preferredType) {
|
||||
DPTXlatorBoolean translatorBoolean = (DPTXlatorBoolean) translator;
|
||||
switch (subType) {
|
||||
case "008":
|
||||
return translatorBoolean.getValueBoolean() ? UpDownType.DOWN : UpDownType.UP;
|
||||
case "009":
|
||||
case "019":
|
||||
return switch (subType) {
|
||||
case "008" -> translatorBoolean.getValueBoolean() ? UpDownType.DOWN : UpDownType.UP;
|
||||
case "009", "019" -> {
|
||||
// default is OpenClosedType (Contact), but it may be mapped to OnOffType as well
|
||||
if (OnOffType.class.equals(preferredType)) {
|
||||
return OnOffType.from(translatorBoolean.getValueBoolean());
|
||||
yield OnOffType.from(translatorBoolean.getValueBoolean());
|
||||
}
|
||||
|
||||
// This is wrong for DPT 1.009. It should be true -> CLOSE, false -> OPEN, but unfortunately
|
||||
// can't be fixed without breaking a lot of working installations.
|
||||
// The documentation has been updated to reflect that. / @J-N-K
|
||||
return translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||
case "010":
|
||||
return translatorBoolean.getValueBoolean() ? StopMoveType.MOVE : StopMoveType.STOP;
|
||||
case "022":
|
||||
return DecimalType.valueOf(translatorBoolean.getValueBoolean() ? "1" : "0");
|
||||
default:
|
||||
yield translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||
|
||||
// This is wrong for DPT 1.009. It should be true -> CLOSE, false -> OPEN, but unfortunately
|
||||
// can't be fixed without breaking a lot of working installations.
|
||||
// The documentation has been updated to reflect that. / @J-N-K
|
||||
}
|
||||
case "010" -> translatorBoolean.getValueBoolean() ? StopMoveType.MOVE : StopMoveType.STOP;
|
||||
case "022" -> DecimalType.valueOf(translatorBoolean.getValueBoolean() ? "1" : "0");
|
||||
default -> {
|
||||
// default is OnOffType (Switch), but it may be mapped to OpenClosedType as well
|
||||
if (OpenClosedType.class.equals(preferredType)) {
|
||||
return translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||
yield translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||
}
|
||||
|
||||
return OnOffType.from(translatorBoolean.getValueBoolean());
|
||||
}
|
||||
yield OnOffType.from(translatorBoolean.getValueBoolean());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable Type handleDpt3(String subType, DPTXlator translator) {
|
||||
@ -275,17 +277,16 @@ public class ValueDecoder {
|
||||
LOGGER.debug("convertRawDataToType: KNX DPT_Control_Dimming: break received.");
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
switch (subType) {
|
||||
case "007":
|
||||
return translator3BitControlled.getControlBit() ? IncreaseDecreaseType.INCREASE
|
||||
: IncreaseDecreaseType.DECREASE;
|
||||
case "008":
|
||||
return translator3BitControlled.getControlBit() ? UpDownType.DOWN : UpDownType.UP;
|
||||
default:
|
||||
return switch (subType) {
|
||||
case "007" -> translator3BitControlled.getControlBit() ? IncreaseDecreaseType.INCREASE
|
||||
: IncreaseDecreaseType.DECREASE;
|
||||
case "008" -> translator3BitControlled.getControlBit() ? UpDownType.DOWN : UpDownType.UP;
|
||||
default -> {
|
||||
// should never happen unless Calimero introduces new subtypes
|
||||
LOGGER.warn("DPT3, subtype '{}' is unknown. Please open an issue.", subType);
|
||||
return null;
|
||||
}
|
||||
yield null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Type handleDpt10(String value) throws ParseException {
|
||||
|
@ -198,14 +198,14 @@ public class ValueEncoder {
|
||||
if (DPTXlator2ByteFloat.DPT_TEMPERATURE_DIFFERENCE.getID().equals(dptId)
|
||||
|| DPTXlator2ByteFloat.DPT_TEMPERATURE_GRADIENT.getID().equals(dptId)
|
||||
|| DPTXlator2ByteFloat.DPT_KELVIN_PER_PERCENT.getID().equals(dptId)) {
|
||||
// match unicode character or °C
|
||||
// match Unicode character or °C
|
||||
if (value.toString().contains(SIUnits.CELSIUS.getSymbol()) || value.toString().contains("°C")) {
|
||||
if (unit != null) {
|
||||
unit = unit.replace("K", "°C");
|
||||
}
|
||||
} else if (value.toString().contains("°F")) {
|
||||
// an new approach to handle temperature differences was introduced to core
|
||||
// after 4.0, stripping the unit and and creating a new QuantityType works
|
||||
// A new approach to handle temperature differences was introduced to core
|
||||
// after 4.0, stripping the unit and creating a new QuantityType works
|
||||
// both with core release 4.0 and current snapshot
|
||||
boolean perPercent = value.toString().contains("/%");
|
||||
value = new QuantityType<>(((QuantityType<?>) value).doubleValue() * 5.0 / 9.0, Units.KELVIN);
|
||||
@ -248,16 +248,12 @@ public class ValueEncoder {
|
||||
return bigDecimal.stripTrailingZeros().toPlainString();
|
||||
case "2":
|
||||
DPT valueDPT = ((DPTXlator1BitControlled.DPT1BitControlled) dpt).getValueDPT();
|
||||
switch (bigDecimal.intValue()) {
|
||||
case 0:
|
||||
return "0 " + valueDPT.getLowerValue();
|
||||
case 1:
|
||||
return "0 " + valueDPT.getUpperValue();
|
||||
case 2:
|
||||
return "1 " + valueDPT.getLowerValue();
|
||||
default:
|
||||
return "1 " + valueDPT.getUpperValue();
|
||||
}
|
||||
return switch (bigDecimal.intValue()) {
|
||||
case 0 -> "0 " + valueDPT.getLowerValue();
|
||||
case 1 -> "0 " + valueDPT.getUpperValue();
|
||||
case 2 -> "1 " + valueDPT.getLowerValue();
|
||||
default -> "1 " + valueDPT.getUpperValue();
|
||||
};
|
||||
case "18":
|
||||
int intVal = bigDecimal.intValue();
|
||||
if (intVal > 63) {
|
||||
@ -281,7 +277,7 @@ public class ValueEncoder {
|
||||
/**
|
||||
* convert 0...100% to 1 byte 0..255
|
||||
*
|
||||
* @param percent
|
||||
* @param percent percentage 0..1
|
||||
* @return int 0..255
|
||||
*/
|
||||
private static int convertPercentToByte(PercentType percent) {
|
||||
|
@ -370,9 +370,8 @@ public class DeviceThingHandler extends BaseThingHandler implements GroupAddress
|
||||
logger.trace(
|
||||
"onGroupWrite Thing '{}' processes a GroupValueWrite telegram for destination '{}' for channel '{}'",
|
||||
getThing().getUID(), destination, knxChannel.getChannelUID());
|
||||
/**
|
||||
* Remember current KNXIO outboundSpec only if it is a control channel.
|
||||
*/
|
||||
|
||||
// Remember current KNXIO outboundSpec only if it is a control channel
|
||||
if (knxChannel.isControl()) {
|
||||
logger.trace("onGroupWrite isControl");
|
||||
Type value = ValueDecoder.decode(listenSpec.getDPT(), asdu, knxChannel.preferredType());
|
||||
@ -478,7 +477,7 @@ public class DeviceThingHandler extends BaseThingHandler implements GroupAddress
|
||||
DeviceInspector.Result result = inspector.readDeviceInfo();
|
||||
if (result != null) {
|
||||
Map<String, String> properties = editProperties();
|
||||
properties.putAll(result.getProperties());
|
||||
properties.putAll(result.properties());
|
||||
updateProperties(properties);
|
||||
return true;
|
||||
}
|
||||
|
@ -367,7 +367,7 @@ public abstract class KNXBridgeBaseThingHandler extends BaseBridgeHandler implem
|
||||
/***
|
||||
* Show all secure group addresses and surrogates. A surrogate is the device which is asked to carry out an indirect
|
||||
* read/write request.
|
||||
* Simpler approach w/o surrogates: Security.defaultInstallation().groupSenders().toString());
|
||||
* Simpler approach w/o surrogates: Security.defaultInstallation().groupSenders().toString();
|
||||
*/
|
||||
public static String secHelperGetSecureGroupAddresses(final Security openhabSecurity) {
|
||||
Map<GroupAddress, Set<String>> groupSendersWithSurrogate = new HashMap<GroupAddress, Set<String>>();
|
||||
@ -379,7 +379,7 @@ public abstract class KNXBridgeBaseThingHandler extends BaseBridgeHandler implem
|
||||
IndividualAddress surrogate = null;
|
||||
try {
|
||||
surrogate = senders.getOrDefault(ga, Set.of()).stream().findAny().get();
|
||||
} catch (NoSuchElementException e) {
|
||||
} catch (NoSuchElementException ignored) {
|
||||
}
|
||||
Set<String> devices = new HashSet<String>();
|
||||
for (var device : entry.getValue()) {
|
||||
|
@ -26,7 +26,7 @@ import org.osgi.framework.FrameworkUtil;
|
||||
* This class provides translations. It is a helper class for i18n / localization efforts.
|
||||
*
|
||||
* @implNote It is implemented as a static singleton, enforced by the single-element enum pattern.
|
||||
* @apiNote {@link #setProvider(LocaleProvider, TranslationProvider)} must be called to provide tanslation service,
|
||||
* @apiNote {@link #setProvider(LocaleProvider, TranslationProvider)} must be called to provide translation service,
|
||||
* otherwise all functions will return untranslated text.
|
||||
* Thread safety is ensured.
|
||||
* @author Holger Friedrich - Initial contribution
|
||||
@ -38,9 +38,9 @@ public enum KNXTranslationProvider {
|
||||
|
||||
private @Nullable LocaleProvider localeProvider;
|
||||
private @Nullable TranslationProvider translationProvider;
|
||||
private Bundle bundle;
|
||||
private final Bundle bundle;
|
||||
|
||||
private KNXTranslationProvider() {
|
||||
KNXTranslationProvider() {
|
||||
localeProvider = null;
|
||||
translationProvider = null;
|
||||
bundle = FrameworkUtil.getBundle(this.getClass());
|
||||
@ -59,7 +59,7 @@ public enum KNXTranslationProvider {
|
||||
final TranslationProvider translationProvider = this.translationProvider;
|
||||
final LocaleProvider localeProvider = this.localeProvider;
|
||||
if (translationProvider != null) {
|
||||
// localeProvider might be null, but if not, getLocale will return NonNull Locale
|
||||
// localeProvider might be null, but if not, getLocale will return NonNull Locale;
|
||||
// locale cannot be cached, as getLocale() will return different result once locale is changed by user
|
||||
final Locale locale = (localeProvider != null) ? localeProvider.getLocale() : Locale.getDefault();
|
||||
final String res = translationProvider.getText(bundle, text, text, locale, arguments);
|
||||
@ -67,7 +67,7 @@ public enum KNXTranslationProvider {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// translating not possible, we still have the original text without any subsititutions
|
||||
// translating not possible, we still have the original text without any substitutions
|
||||
if (arguments == null || arguments.length == 0) {
|
||||
return text;
|
||||
}
|
||||
@ -80,14 +80,14 @@ public enum KNXTranslationProvider {
|
||||
*
|
||||
* @param e any exception
|
||||
* @return localized message in form [description (translated)] [class name], [e.getLocalizedMessage (not
|
||||
* translated)]), empty string for null. May possibly change in further releases.
|
||||
* translated)], empty string for null. May change in further releases.
|
||||
*/
|
||||
public String getLocalizedException(final Throwable e) {
|
||||
StringBuilder res = new StringBuilder();
|
||||
final String exName = e.getClass().getSimpleName();
|
||||
final String key = "exception." + exName;
|
||||
final String translatedDescription = KNXTranslationProvider.I18N.get(key);
|
||||
Boolean foundTranslation = !key.equals(translatedDescription);
|
||||
boolean foundTranslation = !key.equals(translatedDescription);
|
||||
// detailed message cannot be translated, e.getLocalizedMessage will likely return English
|
||||
String detail = e.getLocalizedMessage();
|
||||
if (detail == null) {
|
||||
|
@ -45,9 +45,7 @@ class KNXChannelFactoryTest {
|
||||
public void testNullChannelUidFails() {
|
||||
Channel channel = Objects.requireNonNull(mock(Channel.class));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
KNXChannelFactory.createKnxChannel(channel);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> KNXChannelFactory.createKnxChannel(channel));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -55,9 +53,7 @@ class KNXChannelFactoryTest {
|
||||
Channel channel = Objects.requireNonNull(mock(Channel.class));
|
||||
when(channel.getChannelTypeUID()).thenReturn(new ChannelTypeUID("a:b:c"));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
KNXChannelFactory.createKnxChannel(channel);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> KNXChannelFactory.createKnxChannel(channel));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ -65,11 +61,11 @@ class KNXChannelFactoryTest {
|
||||
CHANNEL_DATETIME, CHANNEL_DATETIME_CONTROL, CHANNEL_DIMMER, CHANNEL_DIMMER_CONTROL, CHANNEL_NUMBER,
|
||||
CHANNEL_NUMBER_CONTROL, CHANNEL_ROLLERSHUTTER, CHANNEL_ROLLERSHUTTER_CONTROL, CHANNEL_STRING,
|
||||
CHANNEL_STRING_CONTROL, CHANNEL_SWITCH, CHANNEL_SWITCH_CONTROL })
|
||||
public void testSuccess(String channeltype) {
|
||||
public void testSuccess(String channelType) {
|
||||
Channel channel = Objects.requireNonNull(mock(Channel.class));
|
||||
Configuration configuration = new Configuration(
|
||||
Map.of("key1", "5.001:<1/2/3+4/5/6+1/5/6", "key2", "1.001:7/1/9+1/1/2"));
|
||||
when(channel.getChannelTypeUID()).thenReturn(new ChannelTypeUID("knx:" + channeltype));
|
||||
when(channel.getChannelTypeUID()).thenReturn(new ChannelTypeUID("knx:" + channelType));
|
||||
when(channel.getConfiguration()).thenReturn(configuration);
|
||||
when(channel.getAcceptedItemType()).thenReturn("none");
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.knx.internal.client;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.knx.internal.handler.KNXBridgeBaseThingHandler.CommandExtensionData;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
import tuwien.auto.calimero.KNXException;
|
||||
import tuwien.auto.calimero.link.KNXNetworkLink;
|
||||
|
||||
/**
|
||||
* {@link AbstractKNXClient} implementation for test, using {@link DummyKNXNetworkLink}.
|
||||
*
|
||||
* @author Holger Friedrich - initial contribution and API.
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DummyClient extends AbstractKNXClient {
|
||||
|
||||
public DummyClient() {
|
||||
super(0, new ThingUID("dummy connection"), 0, 0, 0, null, new CommandExtensionData(Collections.emptyMap()),
|
||||
null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KNXNetworkLink establishConnection() throws KNXException, InterruptedException {
|
||||
return new DummyKNXNetworkLink();
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.knx.internal.itests.Back2BackTest;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
@ -36,8 +35,6 @@ import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.util.ColorUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import tuwien.auto.calimero.dptxlator.DPTXlator2ByteUnsigned;
|
||||
import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat;
|
||||
@ -54,8 +51,6 @@ import tuwien.auto.calimero.dptxlator.DptXlator2ByteSigned;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class DPTTest {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(Back2BackTest.class);
|
||||
|
||||
@Test
|
||||
void testDptBroken() {
|
||||
assertNull(ValueEncoder.encode(new DecimalType(), "9.042.1"));
|
||||
@ -529,7 +524,7 @@ class DPTTest {
|
||||
assertNotEquals(DPTXlator4ByteFloat.DPT_ELECTROMAGNETIC_MOMENT.getUnit(),
|
||||
Units.AMPERE.multiply(SIUnits.SQUARE_METRE).toString());
|
||||
|
||||
// 64 bit signed (DPT 29)
|
||||
// 64-bit signed (DPT 29)
|
||||
assertNotEquals(DPTXlator64BitSigned.DPT_REACTIVE_ENERGY.getUnit(), Units.VAR_HOUR.toString());
|
||||
}
|
||||
|
||||
|
@ -58,28 +58,21 @@ class KNXBridgeBaseThingHandlerTest {
|
||||
// router password configured, length must be 16 bytes in hex notation
|
||||
assertTrue(handler.initializeSecurity("", "", "D947B12DDECAD528B1D5A88FD347F284", "", "", "", ""));
|
||||
assertTrue(handler.initializeSecurity("", "", "0xD947B12DDECAD528B1D5A88FD347F284", "", "", "", ""));
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
handler.initializeSecurity("", "", "wrongLength", "", "", "", "");
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> handler.initializeSecurity("", "", "wrongLength", "", "", "", ""));
|
||||
|
||||
// tunnel configuration
|
||||
assertTrue(handler.initializeSecurity("", "", "", "da", "1", "pw", ""));
|
||||
// cTunnelUser is restricted to a number >0
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
handler.initializeSecurity("", "", "", "da", "0", "pw", "");
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
handler.initializeSecurity("", "", "", "da", "eins", "pw", "");
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> handler.initializeSecurity("", "", "", "da", "0", "pw", ""));
|
||||
assertThrows(KnxSecureException.class, () -> handler.initializeSecurity("", "", "", "da", "eins", "pw", ""));
|
||||
// at least one setting for tunnel is given, count as try to configure secure tunnel
|
||||
// plausibility is checked during initialize()
|
||||
assertTrue(handler.initializeSecurity("", "", "", "da", "", "", ""));
|
||||
assertTrue(handler.initializeSecurity("", "", "", "", "1", "", ""));
|
||||
assertTrue(handler.initializeSecurity("", "", "", "", "", "pw", ""));
|
||||
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
handler.initializeSecurity("nonExistingFile.xml", "", "", "", "", "", "");
|
||||
});
|
||||
assertThrows(KnxSecureException.class,
|
||||
() -> handler.initializeSecurity("nonExistingFile.xml", "", "", "", "", "", ""));
|
||||
|
||||
Properties pBackup = new Properties(System.getProperties());
|
||||
try {
|
||||
@ -91,12 +84,12 @@ class KNXBridgeBaseThingHandlerTest {
|
||||
p.put(OpenHAB.CONFIG_DIR_PROG_ARGUMENT, testFile.getParent().replaceAll("misc$", ""));
|
||||
System.setProperties(p);
|
||||
|
||||
assertTrue(handler.initializeSecurity(testFile.getName().toString(), passwordString, "", "", "", "pw", ""));
|
||||
assertTrue(handler.initializeSecurity(testFile.getName(), passwordString, "", "", "", "pw", ""));
|
||||
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
assertTrue(handler.initializeSecurity(testFile.getName().toString(), "wrong", "", "", "", "pw", ""));
|
||||
});
|
||||
assertThrows(KnxSecureException.class,
|
||||
() -> assertTrue(handler.initializeSecurity(testFile.getName(), "wrong", "", "", "", "pw", "")));
|
||||
} catch (URISyntaxException e) {
|
||||
fail("should never happen");
|
||||
} finally {
|
||||
// properties are not persistent, but change may interference with other tests -> restore
|
||||
System.setProperties(pBackup);
|
||||
|
@ -12,10 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.knx.internal.itests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@ -71,7 +68,6 @@ import tuwien.auto.calimero.process.ProcessCommunicatorImpl;
|
||||
* handled by this test. However, new subtypes are not detected.
|
||||
*
|
||||
* @see DummyKNXNetworkLink
|
||||
* @see DummyClient
|
||||
* @author Holger Friedrich - Initial contribution
|
||||
*
|
||||
*/
|
||||
@ -80,7 +76,6 @@ public class Back2BackTest {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(Back2BackTest.class);
|
||||
static Set<Integer> dptTested = new HashSet<>();
|
||||
static final byte[] F32_MINUS_ONE = new byte[] { (byte) 0xbf, (byte) 0x80, 0, 0 };
|
||||
boolean testsMissing = false;
|
||||
|
||||
/**
|
||||
* helper method for integration tests
|
||||
@ -90,7 +85,7 @@ public class Back2BackTest {
|
||||
* @param ohReferenceData OpenHAB data type, initialized to known good value
|
||||
* @param maxDistance byte array containing maximal deviations when comparing byte arrays (rawData against created
|
||||
* KNX frame), may be empty if no deviation is considered
|
||||
* @param bitmask to mask certain bits in the raw to raw comparison, required for multi-valued KNX frames
|
||||
* @param bitmask to mask certain bits in the raw to raw comparison, required for multivalued KNX frames
|
||||
*/
|
||||
void helper(String dpt, byte[] rawData, Type ohReferenceData, byte[] maxDistance, byte[] bitmask) {
|
||||
try {
|
||||
@ -104,7 +99,7 @@ public class Back2BackTest {
|
||||
DPTUtil.NORMALIZED_DPT.getOrDefault(dpt, dpt));
|
||||
|
||||
// 0) check usage of helper()
|
||||
assertEquals(true, rawData.length > 0);
|
||||
assertTrue(rawData.length > 0);
|
||||
if (maxDistance.length == 0) {
|
||||
maxDistance = new byte[rawData.length];
|
||||
}
|
||||
@ -115,7 +110,7 @@ public class Back2BackTest {
|
||||
}
|
||||
assertEquals(rawData.length, bitmask.length, "incorrect length of bitmask array");
|
||||
int mainType = Integer.parseUnsignedInt(dpt.substring(0, dpt.indexOf('.')));
|
||||
dptTested.add(Integer.valueOf(mainType));
|
||||
dptTested.add(mainType);
|
||||
// check if OH would be able to send out a frame, given the type
|
||||
Set<Integer> knownWorking = Set.of(1, 3, 5);
|
||||
if (!knownWorking.contains(mainType)) {
|
||||
@ -146,7 +141,8 @@ public class Back2BackTest {
|
||||
|
||||
// 2) check the encoding (ohData to raw data)
|
||||
//
|
||||
// Test approach is to a) encode the value into String format using ValueEncoder.encode(),
|
||||
// Test approach is to
|
||||
// a) encode the value into String format using ValueEncoder.encode(),
|
||||
// b) pass it to Calimero for conversion into a raw representation, and
|
||||
// c) finally grab raw data bytes from a custom KNXNetworkLink implementation
|
||||
String enc = ValueEncoder.encode(ohData, dpt);
|
||||
@ -178,7 +174,7 @@ public class Back2BackTest {
|
||||
|
||||
pc.close();
|
||||
} catch (KNXException e) {
|
||||
LOGGER.warn("exception occurred", e.toString());
|
||||
LOGGER.warn("exception occurred: {}", e.toString());
|
||||
assertEquals("", e.toString());
|
||||
}
|
||||
}
|
||||
@ -949,10 +945,10 @@ public class Back2BackTest {
|
||||
};
|
||||
TranslatorTypes.getAllMainTypes().forEach((i, t) -> {
|
||||
if (!dptTested.contains(i)) {
|
||||
LOGGER.warn("missing tests for main DPT type " + i);
|
||||
LOGGER.warn("missing tests for main DPT type {}", i);
|
||||
wrapper.testsMissing = true;
|
||||
}
|
||||
});
|
||||
assertEquals(false, wrapper.testsMissing, "add tests for new DPT main types");
|
||||
assertFalse(wrapper.testsMissing, "add tests for new DPT main types");
|
||||
}
|
||||
}
|
||||
|
@ -138,18 +138,15 @@ public class KNXSecurityTest {
|
||||
Security openhabSecurity = Security.newSecurity();
|
||||
openhabSecurity.useKeyring(keys, password);
|
||||
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
|
||||
});
|
||||
assertThrows(KnxSecureException.class,
|
||||
() -> KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString));
|
||||
assertTrue(KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys), passwordString)
|
||||
.isPresent());
|
||||
|
||||
// now check tunnel (expected to fail, not included)
|
||||
IndividualAddress secureTunnelSourceAddr = new IndividualAddress(2, 8, 20);
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
|
||||
secureTunnelSourceAddr);
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.empty(), passwordString, secureTunnelSourceAddr));
|
||||
assertTrue(KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, secureTunnelSourceAddr)
|
||||
.isEmpty());
|
||||
@ -170,18 +167,15 @@ public class KNXSecurityTest {
|
||||
Security openhabSecurity = Security.newSecurity();
|
||||
openhabSecurity.useKeyring(keys, password);
|
||||
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
|
||||
});
|
||||
assertThrows(KnxSecureException.class,
|
||||
() -> KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString));
|
||||
assertTrue(KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys), passwordString)
|
||||
.isEmpty());
|
||||
|
||||
// now check tunnel
|
||||
IndividualAddress secureTunnelSourceAddr = new IndividualAddress(1, 1, 2);
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
|
||||
secureTunnelSourceAddr);
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.empty(), passwordString, secureTunnelSourceAddr));
|
||||
assertTrue(KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, secureTunnelSourceAddr)
|
||||
.isPresent());
|
||||
@ -205,9 +199,8 @@ public class KNXSecurityTest {
|
||||
openhabSecurity.useKeyring(keys, password);
|
||||
|
||||
// now check router settings:
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
|
||||
});
|
||||
assertThrows(KnxSecureException.class,
|
||||
() -> KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString));
|
||||
String bbKeyHex = "D947B12DDECAD528B1D5A88FD347F284";
|
||||
byte[] bbKeyParsedLower = KNXBridgeBaseThingHandler.secHelperParseBackboneKey(bbKeyHex.toLowerCase());
|
||||
byte[] bbKeyParsedUpper = KNXBridgeBaseThingHandler.secHelperParseBackboneKey(bbKeyHex);
|
||||
@ -225,10 +218,8 @@ public class KNXSecurityTest {
|
||||
// now check tunnel settings:
|
||||
IndividualAddress secureTunnelSourceAddr = new IndividualAddress(1, 1, 2);
|
||||
IndividualAddress noSecureTunnelSourceAddr = new IndividualAddress(2, 8, 20);
|
||||
assertThrows(KnxSecureException.class, () -> {
|
||||
KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
|
||||
secureTunnelSourceAddr);
|
||||
});
|
||||
assertThrows(KnxSecureException.class, () -> KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.empty(), passwordString, secureTunnelSourceAddr));
|
||||
assertTrue(KNXBridgeBaseThingHandler
|
||||
.secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, noSecureTunnelSourceAddr)
|
||||
.isEmpty());
|
||||
|
Loading…
Reference in New Issue
Block a user