added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.snmp-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-snmp" description="SNMP Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.snmp/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,36 @@
/**
* 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.snmp.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link SnmpBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class SnmpBindingConstants {
private static final String BINDING_ID = "snmp";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_TARGET = new ThingTypeUID(BINDING_ID, "target");
public static final ChannelTypeUID CHANNEL_TYPE_UID_NUMBER = new ChannelTypeUID(BINDING_ID, "number");
public static final ChannelTypeUID CHANNEL_TYPE_UID_STRING = new ChannelTypeUID(BINDING_ID, "string");
public static final ChannelTypeUID CHANNEL_TYPE_UID_SWITCH = new ChannelTypeUID(BINDING_ID, "switch");
}

View File

@@ -0,0 +1,26 @@
/**
* 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.snmp.internal;
/**
* The {@link SnmpChannelMode} enum defines the mode of SNMP channels
*
* @author Jan N. Klug - Initial contribution
*/
public enum SnmpChannelMode {
READ,
WRITE,
READ_WRITE,
TRAP
}

View File

@@ -0,0 +1,29 @@
/**
* 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.snmp.internal;
/**
* The {@link SnmpDatatype} enum defines the datatype of SNMP channels
*
* @author Jan N. Klug - Initial contribution
*/
public enum SnmpDatatype {
INT32,
UINT32,
COUNTER64,
FLOAT,
STRING,
HEXSTRING,
IPADDRESS
}

View File

@@ -0,0 +1,62 @@
/**
* 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.snmp.internal;
import static org.openhab.binding.snmp.internal.SnmpBindingConstants.THING_TYPE_TARGET;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link SnmpHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.snmp")
public class SnmpHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_TARGET);
private final SnmpService snmpService;
@Activate
public SnmpHandlerFactory(@Reference SnmpService snmpService) {
this.snmpService = snmpService;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_TARGET.equals(thingTypeUID)) {
return new SnmpTargetHandler(thing, snmpService);
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
/**
* 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.snmp.internal;
/**
* The {@link SnmpProtocolVersion} enum defines the datatype of SNMP channels
*
* @author Jan N. Klug - Initial contribution
*/
public enum SnmpProtocolVersion {
v1(0),
V1(0),
v2c(1),
V2C(1);
private final int value;
private SnmpProtocolVersion(int value) {
this.value = value;
}
public int toInteger() {
return value;
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.snmp.internal;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.snmp4j.CommandResponder;
import org.snmp4j.PDU;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseListener;
/**
* The {@link SnmpService} is responsible for SNMP communication
* handlers.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public interface SnmpService {
public void addCommandResponder(CommandResponder listener);
public void removeCommandResponder(CommandResponder listener);
public void send(PDU pdu, Target target, @Nullable Object userHandle, ResponseListener listener) throws IOException;
}

View File

@@ -0,0 +1,139 @@
/**
* 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.snmp.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.snmp.internal.config.SnmpServiceConfiguration;
import org.openhab.core.config.core.Configuration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.CommandResponder;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.transport.DefaultUdpTransportMapping;
/**
* The {@link SnmpServiceImpl} implements SnmpService
* handlers.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.snmp", service = SnmpService.class)
public class SnmpServiceImpl implements SnmpService {
private final Logger logger = LoggerFactory.getLogger(SnmpServiceImpl.class);
private @NonNullByDefault({}) SnmpServiceConfiguration config;
private @Nullable Snmp snmp;
private @Nullable DefaultUdpTransportMapping transport;
private List<CommandResponder> listeners = new ArrayList<>();
@Activate
public SnmpServiceImpl(Map<String, Object> config) {
modified(config);
}
@Modified
protected void modified(Map<String, Object> config) {
this.config = new Configuration(config).as(SnmpServiceConfiguration.class);
try {
shutdownSnmp();
final DefaultUdpTransportMapping transport;
if (this.config.port > 0) {
transport = new DefaultUdpTransportMapping(new UdpAddress(this.config.port), true);
} else {
transport = new DefaultUdpTransportMapping();
}
SecurityProtocols.getInstance().addDefaultProtocols();
SecurityProtocols.getInstance().addPrivacyProtocol(new Priv3DES());
final Snmp snmp = new Snmp(transport);
listeners.forEach(listener -> snmp.addCommandResponder(listener));
snmp.listen();
this.snmp = snmp;
this.transport = transport;
logger.debug("initialized SNMP at {}", transport.getAddress());
} catch (IOException e) {
logger.warn("could not open SNMP instance on port {}: {}", this.config.port, e.getMessage());
}
}
@Deactivate
public void deactivate() {
try {
shutdownSnmp();
} catch (IOException e) {
logger.info("could not end SNMP: {}", e.getMessage());
}
}
private void shutdownSnmp() throws IOException {
if (transport != null) {
transport.close();
transport = null;
}
if (snmp != null) {
snmp.close();
snmp = null;
}
}
@Override
public void addCommandResponder(CommandResponder listener) {
if (snmp != null) {
snmp.addCommandResponder(listener);
}
listeners.add(listener);
}
@Override
public void removeCommandResponder(CommandResponder listener) {
if (snmp != null) {
snmp.removeCommandResponder(listener);
}
listeners.remove(listener);
}
@Override
public void send(PDU pdu, Target target, @Nullable Object userHandle, ResponseListener listener)
throws IOException {
if (snmp != null) {
snmp.send(pdu, target, userHandle, listener);
logger.trace("send {} to {}", pdu, target);
} else {
logger.warn("SNMP service not initialized, can't send {} to {}", pdu, target);
}
}
}

View File

@@ -0,0 +1,442 @@
/**
* 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.snmp.internal;
import static org.openhab.binding.snmp.internal.SnmpBindingConstants.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.snmp.internal.config.SnmpChannelConfiguration;
import org.openhab.binding.snmp.internal.config.SnmpInternalChannelConfiguration;
import org.openhab.binding.snmp.internal.config.SnmpTargetConfiguration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.AbstractTarget;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.PDUv1;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Counter64;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.IpAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.UnsignedInteger32;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
/**
* The {@link SnmpTargetHandler} is responsible for handling commands, which are
* sent to one of the channels or update remote channels
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class SnmpTargetHandler extends BaseThingHandler implements ResponseListener, CommandResponder {
private static final Pattern HEXSTRING_VALIDITY = Pattern.compile("([a-f0-9]{2}[ :-]?)+");
private static final Pattern HEXSTRING_EXTRACTOR = Pattern.compile("[^a-f0-9]");
private final Logger logger = LoggerFactory.getLogger(SnmpTargetHandler.class);
private @NonNullByDefault({}) SnmpTargetConfiguration config;
private final SnmpService snmpService;
private @Nullable ScheduledFuture<?> refresh;
private int timeoutCounter = 0;
private @NonNullByDefault({}) AbstractTarget target;
private @NonNullByDefault({}) String targetAddressString;
private @NonNullByDefault({}) Set<SnmpInternalChannelConfiguration> readChannelSet;
private @NonNullByDefault({}) Set<SnmpInternalChannelConfiguration> writeChannelSet;
private @NonNullByDefault({}) Set<SnmpInternalChannelConfiguration> trapChannelSet;
public SnmpTargetHandler(Thing thing, SnmpService snmpService) {
super(thing);
this.snmpService = snmpService;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (target.getAddress() == null && !renewTargetAddress()) {
logger.info("failed to renew target address, can't process '{}' to '{}'.", command, channelUID);
return;
}
try {
if (command instanceof RefreshType) {
SnmpInternalChannelConfiguration channel = readChannelSet.stream()
.filter(c -> channelUID.equals(c.channelUID)).findFirst()
.orElseThrow(() -> new IllegalArgumentException("no writable channel found"));
PDU pdu = new PDU(PDU.GET, Collections.singletonList(new VariableBinding(channel.oid)));
snmpService.send(pdu, target, null, this);
} else if (command instanceof DecimalType || command instanceof StringType
|| command instanceof OnOffType) {
SnmpInternalChannelConfiguration channel = writeChannelSet.stream()
.filter(config -> channelUID.equals(config.channelUID)).findFirst()
.orElseThrow(() -> new IllegalArgumentException("no writable channel found"));
Variable variable;
if (command instanceof OnOffType) {
variable = OnOffType.ON.equals(command) ? channel.onValue : channel.offValue;
if (variable == null) {
logger.debug("skipping {} to {}: no value defined", command, channelUID);
return;
}
} else {
variable = convertDatatype(command, channel.datatype);
}
PDU pdu = new PDU(PDU.SET, Collections.singletonList(new VariableBinding(channel.oid, variable)));
snmpService.send(pdu, target, null, this);
}
} catch (IllegalArgumentException e) {
logger.warn("can't process command {} to {}: {}", command, channelUID, e.getMessage());
} catch (IOException e) {
logger.warn("Could not send PDU while processing command {} to {}", command, channelUID);
}
}
@Override
public void initialize() {
config = getConfigAs(SnmpTargetConfiguration.class);
generateChannelConfigs();
if (config.protocol.toInteger() == SnmpConstants.version1
|| config.protocol.toInteger() == SnmpConstants.version2c) {
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(config.community));
target.setRetries(config.retries);
target.setTimeout(config.timeout);
target.setVersion(config.protocol.toInteger());
target.setAddress(null);
this.target = target;
snmpService.addCommandResponder(this);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "SNMP version not supported");
return;
}
timeoutCounter = 0;
updateStatus(ThingStatus.UNKNOWN);
refresh = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.SECONDS);
}
@Override
public void dispose() {
final ScheduledFuture<?> r = refresh;
if (r != null && !r.isCancelled()) {
r.cancel(true);
}
snmpService.removeCommandResponder(this);
}
@Override
public void onResponse(@Nullable ResponseEvent event) {
if (event == null) {
return;
}
PDU response = event.getResponse();
if (response == null) {
Exception e = event.getError();
if (e == null) { // no response, no error -> request timed out
timeoutCounter++;
if (timeoutCounter > config.retries) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "request timed out");
target.setAddress(null);
}
return;
}
logger.warn("{} requested {} and got error: {}", thing.getUID(), event.getRequest(), e.getMessage());
return;
}
timeoutCounter = 0;
logger.trace("{} received {}", thing.getUID(), response);
response.getVariableBindings().forEach(variable -> {
OID oid = variable.getOid();
Variable value = variable.getVariable();
updateChannels(oid, value, readChannelSet);
});
}
@Override
public void processPdu(@Nullable CommandResponderEvent event) {
if (event == null) {
return;
}
logger.trace("{} received trap {}", thing.getUID(), event);
final PDU pdu = event.getPDU();
final String address = ((UdpAddress) event.getPeerAddress()).getInetAddress().getHostAddress();
final String community = new String(event.getSecurityName());
if ((pdu.getType() == PDU.V1TRAP) && config.community.equals(community) && (pdu instanceof PDUv1)) {
logger.trace("{} received trap is PDUv1.", thing.getUID());
PDUv1 pduv1 = (PDUv1) pdu;
OID oidEnterprise = pduv1.getEnterprise();
int trapValue = pduv1.getGenericTrap();
if (trapValue == PDUv1.ENTERPRISE_SPECIFIC) {
trapValue = pduv1.getSpecificTrap();
}
updateChannels(oidEnterprise, new UnsignedInteger32(trapValue), trapChannelSet);
}
if ((pdu.getType() == PDU.TRAP || pdu.getType() == PDU.V1TRAP) && config.community.equals(community)
&& targetAddressString.equals(address)) {
pdu.getVariableBindings().forEach(variable -> {
OID oid = variable.getOid();
Variable value = variable.getVariable();
updateChannels(oid, value, trapChannelSet);
});
}
}
private @Nullable SnmpInternalChannelConfiguration getChannelConfigFromChannel(Channel channel) {
SnmpChannelConfiguration config = channel.getConfiguration().as(SnmpChannelConfiguration.class);
SnmpDatatype datatype;
Variable onValue = null;
Variable offValue = null;
State exceptionValue = UnDefType.UNDEF;
if (CHANNEL_TYPE_UID_NUMBER.equals(channel.getChannelTypeUID())) {
if (config.datatype == null) {
datatype = SnmpDatatype.INT32;
} else if (config.datatype == SnmpDatatype.IPADDRESS || config.datatype == SnmpDatatype.STRING) {
return null;
} else {
datatype = config.datatype;
}
if (config.exceptionValue != null) {
exceptionValue = DecimalType.valueOf(config.exceptionValue);
}
} else if (CHANNEL_TYPE_UID_STRING.equals(channel.getChannelTypeUID())) {
if (config.datatype == null) {
datatype = SnmpDatatype.STRING;
} else if (config.datatype != SnmpDatatype.IPADDRESS && config.datatype != SnmpDatatype.STRING
&& config.datatype != SnmpDatatype.HEXSTRING) {
return null;
} else {
datatype = config.datatype;
}
if (config.exceptionValue != null) {
exceptionValue = StringType.valueOf(config.exceptionValue);
}
} else if (CHANNEL_TYPE_UID_SWITCH.equals(channel.getChannelTypeUID())) {
if (config.datatype == null) {
datatype = SnmpDatatype.UINT32;
} else {
datatype = config.datatype;
}
try {
if (config.onvalue != null) {
onValue = convertDatatype(new StringType(config.onvalue), config.datatype);
}
if (config.offvalue != null) {
offValue = convertDatatype(new StringType(config.offvalue), config.datatype);
}
} catch (IllegalArgumentException e) {
logger.warn("illegal value configuration for channel {}", channel.getUID());
return null;
}
if (config.exceptionValue != null) {
exceptionValue = OnOffType.from(config.exceptionValue);
}
} else {
logger.warn("unknown channel type found for channel {}", channel.getUID());
return null;
}
return new SnmpInternalChannelConfiguration(channel.getUID(), new OID(config.oid), config.mode, datatype,
onValue, offValue, exceptionValue, config.doNotLogException);
}
private void generateChannelConfigs() {
Set<SnmpInternalChannelConfiguration> channelConfigs = Collections
.unmodifiableSet(thing.getChannels().stream().map(channel -> getChannelConfigFromChannel(channel))
.filter(Objects::nonNull).collect(Collectors.toSet()));
this.readChannelSet = channelConfigs.stream()
.filter(c -> c.mode == SnmpChannelMode.READ || c.mode == SnmpChannelMode.READ_WRITE)
.collect(Collectors.toSet());
this.writeChannelSet = channelConfigs.stream()
.filter(c -> c.mode == SnmpChannelMode.WRITE || c.mode == SnmpChannelMode.READ_WRITE)
.collect(Collectors.toSet());
this.trapChannelSet = channelConfigs.stream().filter(c -> c.mode == SnmpChannelMode.TRAP)
.collect(Collectors.toSet());
}
private void updateChannels(OID oid, Variable value, Set<SnmpInternalChannelConfiguration> channelConfigs) {
Set<SnmpInternalChannelConfiguration> updateChannelConfigs = channelConfigs.stream()
.filter(c -> c.oid.equals(oid)).collect(Collectors.toSet());
if (!updateChannelConfigs.isEmpty()) {
updateChannelConfigs.forEach(channelConfig -> {
ChannelUID channelUID = channelConfig.channelUID;
final Channel channel = thing.getChannel(channelUID);
State state;
if (channel == null) {
logger.warn("channel uid {} in channel config set but channel not found", channelUID);
return;
}
if (value.isException()) {
if (!channelConfig.doNotLogException) {
logger.info("SNMP Exception: request {} returned '{}'", oid, value);
}
state = channelConfig.exceptionValue;
} else if (CHANNEL_TYPE_UID_NUMBER.equals(channel.getChannelTypeUID())) {
try {
if (channelConfig.datatype == SnmpDatatype.FLOAT) {
state = new DecimalType(value.toString());
} else {
state = new DecimalType(value.toLong());
}
} catch (UnsupportedOperationException e) {
logger.warn("could not convert {} to number for channel {}", value, channelUID);
return;
}
} else if (CHANNEL_TYPE_UID_STRING.equals(channel.getChannelTypeUID())) {
if (channelConfig.datatype == SnmpDatatype.HEXSTRING) {
String rawString = ((OctetString) value).toHexString(' ');
state = new StringType(rawString.toLowerCase());
} else {
state = new StringType(value.toString());
}
} else if (CHANNEL_TYPE_UID_SWITCH.equals(channel.getChannelTypeUID())) {
if (value.equals(channelConfig.onValue)) {
state = OnOffType.ON;
} else if (value.equals(channelConfig.offValue)) {
state = OnOffType.OFF;
} else {
logger.debug("channel {} received unmapped value {} ", channelUID, value);
return;
}
} else {
logger.warn("channel {} has unknown ChannelTypeUID", channelUID);
return;
}
updateState(channelUID, state);
});
} else {
logger.debug("received value {} for unknown OID {}, skipping", value, oid);
}
}
private Variable convertDatatype(Command command, SnmpDatatype datatype) {
switch (datatype) {
case INT32:
if (command instanceof DecimalType) {
return new Integer32(((DecimalType) command).intValue());
} else if (command instanceof StringType) {
return new Integer32((new DecimalType(((StringType) command).toString())).intValue());
}
break;
case UINT32:
if (command instanceof DecimalType) {
return new UnsignedInteger32(((DecimalType) command).intValue());
} else if (command instanceof StringType) {
return new UnsignedInteger32((new DecimalType(((StringType) command).toString())).intValue());
}
break;
case COUNTER64:
if (command instanceof DecimalType) {
return new Counter64(((DecimalType) command).longValue());
} else if (command instanceof StringType) {
return new Counter64((new DecimalType(((StringType) command).toString())).longValue());
}
break;
case FLOAT:
case STRING:
if (command instanceof DecimalType) {
return new OctetString(((DecimalType) command).toString());
} else if (command instanceof StringType) {
return new OctetString(((StringType) command).toString());
}
break;
case HEXSTRING:
if (command instanceof StringType) {
String commandString = ((StringType) command).toString().toLowerCase();
Matcher commandMatcher = HEXSTRING_VALIDITY.matcher(commandString);
if (commandMatcher.matches()) {
commandString = HEXSTRING_EXTRACTOR.matcher(commandString).replaceAll("");
return OctetString.fromHexStringPairs(commandString);
}
}
break;
case IPADDRESS:
if (command instanceof StringType) {
return new IpAddress(((StringType) command).toString());
}
break;
default:
}
throw new IllegalArgumentException("illegal conversion of " + command + " to " + datatype);
}
private boolean renewTargetAddress() {
try {
target.setAddress(new UdpAddress(InetAddress.getByName(config.hostname), config.port));
targetAddressString = ((UdpAddress) target.getAddress()).getInetAddress().getHostAddress();
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
return true;
} catch (UnknownHostException e) {
target.setAddress(null);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Cannot resolve target host");
return false;
}
}
private void refresh() {
if (target.getAddress() == null) {
if (!renewTargetAddress()) {
logger.info("failed to renew target address, waiting for next refresh cycle");
return;
}
}
PDU pdu = new PDU(PDU.GET,
readChannelSet.stream().map(c -> new VariableBinding(c.oid)).collect(Collectors.toList()));
if (!pdu.getVariableBindings().isEmpty()) {
try {
snmpService.send(pdu, target, null, this);
} catch (IOException e) {
logger.info("Could not send PDU", e);
}
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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.snmp.internal.config;
import org.openhab.binding.snmp.internal.SnmpChannelMode;
import org.openhab.binding.snmp.internal.SnmpDatatype;
/**
* The {@link SnmpChannelConfiguration} class contains fields mapping channel configuration parameters.
*
* @author Jan N. Klug - Initial contribution
*/
public class SnmpChannelConfiguration {
public String oid;
public SnmpChannelMode mode = SnmpChannelMode.READ;
public SnmpDatatype datatype;
public String onvalue;
public String offvalue;
public String exceptionValue;
public boolean doNotLogException = false;
}

View File

@@ -0,0 +1,53 @@
/**
* 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.snmp.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.snmp.internal.SnmpChannelMode;
import org.openhab.binding.snmp.internal.SnmpDatatype;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.Variable;
/**
* The {@link SnmpInternalChannelConfiguration} class contains fields mapping channel configuration parameters.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class SnmpInternalChannelConfiguration {
public final ChannelUID channelUID;
public final OID oid;
public final SnmpChannelMode mode;
public final SnmpDatatype datatype;
public final @Nullable Variable onValue;
public final @Nullable Variable offValue;
public final State exceptionValue;
public final boolean doNotLogException;
public SnmpInternalChannelConfiguration(ChannelUID channelUID, OID oid, SnmpChannelMode mode, SnmpDatatype datatype,
@Nullable Variable onValue, @Nullable Variable offValue, State exceptionValue, boolean doNotLogException) {
this.channelUID = channelUID;
this.oid = oid;
this.mode = mode;
this.datatype = datatype;
this.onValue = onValue;
this.offValue = offValue;
this.exceptionValue = exceptionValue;
this.doNotLogException = doNotLogException;
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.snmp.internal.config;
/**
* The {@link SnmpServiceConfiguration} class contains fields mapping binding configuration parameters.
*
* @author Jan N. Klug - Initial contribution
*/
public class SnmpServiceConfiguration {
public int port = 0;
}

View File

@@ -0,0 +1,30 @@
/**
* 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.snmp.internal.config;
import org.openhab.binding.snmp.internal.SnmpProtocolVersion;
/**
* The {@link SnmpTargetConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Jan N. Klug - Initial contribution
*/
public class SnmpTargetConfiguration {
public String hostname;
public int port = 161;
public String community = "public";
public int refresh = 60;
public SnmpProtocolVersion protocol = SnmpProtocolVersion.v1;
public int timeout = 1500;
public int retries = 2;
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="snmp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>SNMP Binding</name>
<description>This is the binding for SNMP.</description>
<author>Jan N. Klug</author>
<config-description>
<parameter name="port" type="integer" min="0" max="65535">
<default>0</default>
<label>Incoming SNMP Port</label>
<description>Port for receiving traps, set to 0 to disable.</description>
</parameter>
</config-description>
</binding:binding>

View File

@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="snmp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="target" extensible="number,string,switch">
<label>SNMP Target</label>
<config-description>
<!-- required -->
<parameter name="hostname" type="text" required="true">
<label>Target Host</label>
<description>Hostname or IP address of target host</description>
<context>network-address</context>
</parameter>
<!-- optional -->
<parameter name="protocol" type="text">
<label>SNMP Version</label>
<options>
<option value="v1">V1</option>
<option value="v2c">V2c</option>
</options>
<limitToOptions>true</limitToOptions>
<default>v1</default>
</parameter>
<parameter name="community" type="text">
<label>SNMP Community</label>
<default>public</default>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Refresh time in s (default 60s)</description>
<default>60</default>
</parameter>
<!-- optional advanced -->
<parameter name="port" type="integer">
<label>Port</label>
<default>161</default>
<advanced>true</advanced>
</parameter>
<parameter name="timeout" type="integer" min="0">
<label>Timeout</label>
<description>Timeout in ms for a single update request</description>
<default>1500</default>
<advanced>true</advanced>
</parameter>
<parameter name="retries" type="integer" min="0">
<label>Retries</label>
<description>Number of retries for an update request</description>
<default>2</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-type id="number">
<item-type>Number</item-type>
<label>Number</label>
<config-description>
<parameter name="oid" type="text" required="true">
<label>OID</label>
<description>OID in dotted format (eg. .1.3.6.1.4.1.6574.3.1.1.3.0)</description>
</parameter>
<parameter name="mode" type="text">
<label>Mode</label>
<description>the mode of this channel</description>
<options>
<option value="READ">Read</option>
<option value="WRITE">Write</option>
<option value="READ_WRITE">Read/Write</option>
<option value="TRAP">Trap</option>
</options>
<default>READ</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="datatype" type="text">
<label>Datatype</label>
<description>Content data type</description>
<options>
<option value="UINT32">Unsigned Integer (32 bit)</option>
<option value="INT32">Integer (32 bit)</option>
<option value="COUNTER64">Counter (64 bit)</option>
<option value="FLOAT">Float</option>
</options>
<default>UINT32</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="doNotLogException" type="boolean">
<label>Don't Log Exception</label>
<description>If enabled, ignore faulty values/exceptions in this channel</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="exceptionValue" type="integer">
<label>Exception Value</label>
<description>Value to send if an SNMP exception occurs (default: UNDEF)</description>
<advanced>true</advanced>
</parameter>
</config-description>
</channel-type>
<channel-type id="string">
<item-type>String</item-type>
<label>String</label>
<config-description>
<parameter name="oid" type="text" required="true">
<label>OID</label>
<description>OID in dotted format (eg. .1.3.6.1.4.1.6574.3.1.1.3.0)</description>
</parameter>
<parameter name="mode" type="text">
<label>Mode</label>
<description>the mode of this channel</description>
<options>
<option value="READ">Read</option>
<option value="WRITE">Write</option>
<option value="READ_WRITE">Read/Write</option>
<option value="TRAP">Trap</option>
</options>
<default>READ</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="datatype" type="text">
<label>Datatype</label>
<description>Content data type</description>
<options>
<option value="STRING">String</option>
<option value="HEXSTRING">Hex-String</option>
<option value="IPADDRESS">IP Address</option>
</options>
<default>STRING</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="doNotLogException" type="boolean">
<label>Don't Log Exception</label>
<description>If enabled, ignore faulty values/exceptions in this channel</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="exceptionValue" type="text">
<label>Exception Value</label>
<description>Value to send if an SNMP exception occurs (default: UNDEF)</description>
<advanced>true</advanced>
</parameter>
</config-description>
</channel-type>
<channel-type id="switch">
<item-type>Switch</item-type>
<label>Switch</label>
<config-description>
<parameter name="oid" type="text" required="true">
<label>OID</label>
<description>OID in dotted format (eg. .1.3.6.1.4.1.6574.3.1.1.3.0)</description>
</parameter>
<parameter name="mode" type="text">
<label>Mode</label>
<description>the mode of this channel</description>
<options>
<option value="READ">Read</option>
<option value="WRITE">Write</option>
<option value="READ_WRITE">Read/Write</option>
<option value="TRAP">Trap</option>
</options>
<default>READ</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="datatype" type="text">
<label>Datatype</label>
<description>Content data type</description>
<options>
<option value="UINT32">Unsigned Integer (32 bit)</option>
<option value="INT32">Integer (32 bit)</option>
<option value="COUNTER64">Counter (64 bit)</option>
<option value="STRING">String</option>
<option value="HEXSTRING">Hex-String</option>
<option value="IPADDRESS">IP Address</option>
</options>
<default>UINT32</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="onvalue" type="text">
<label>On-Value</label>
<description>Value that equals ON</description>
</parameter>
<parameter name="offvalue" type="text">
<label>Off-Value</label>
<description>Value that equals OFF</description>
</parameter>
<parameter name="doNotLogException" type="boolean">
<label>Don't Log Exception</label>
<description>If enabled, faulty values/exceptions will not be logged in this channel</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="exceptionValue" type="text">
<label>Exception Value</label>
<description>Value to send if an SNMP exception occurs (ON, OFF, default: UNDEF)</description>
<options>
<option value="ON">ON</option>
<option value="OFF">OFF</option>
</options>
<limitToOptions>true</limitToOptions>
<advanced>true</advanced>
</parameter>
</config-description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,211 @@
/**
* 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.snmp.internal;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.snmp.internal.SnmpBindingConstants.THING_TYPE_TARGET;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.StringType;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.snmp4j.PDU;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
/**
* Tests cases for {@link SnmpTargetHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
public abstract class AbstractSnmpTargetHandlerTest extends JavaTest {
protected static final ThingUID THING_UID = new ThingUID(THING_TYPE_TARGET, "testthing");
protected static final ChannelUID CHANNEL_UID = new ChannelUID(THING_UID, "testchannel");
protected static final String TEST_OID = "1.2.3.4";
protected static final String TEST_ADDRESS = "192.168.0.1";
protected static final String TEST_STRING = "foo.";
protected @Mock SnmpServiceImpl snmpService;
protected @Mock ThingHandlerCallback thingHandlerCallback;
protected Thing thing;
protected SnmpTargetHandler thingHandler;
protected VariableBinding handleCommandSwitchChannel(SnmpDatatype datatype, Command command, String onValue,
String offValue, boolean refresh) throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.WRITE, datatype, onValue, offValue);
thingHandler.handleCommand(CHANNEL_UID, command);
if (refresh) {
ArgumentCaptor<PDU> pduCaptor = ArgumentCaptor.forClass(PDU.class);
verify(snmpService, times(1)).send(pduCaptor.capture(), any(), eq(null), eq(thingHandler));
return pduCaptor.getValue().getVariableBindings().stream().findFirst().orElse(null);
} else {
verify(snmpService, never()).send(any(), any(), eq(null), eq(thingHandler));
return null;
}
}
protected VariableBinding handleCommandNumberStringChannel(ChannelTypeUID channelTypeUID, SnmpDatatype datatype,
Command command, boolean refresh) throws IOException {
setup(channelTypeUID, SnmpChannelMode.WRITE, datatype);
thingHandler.handleCommand(CHANNEL_UID, command);
if (refresh) {
ArgumentCaptor<PDU> pduCaptor = ArgumentCaptor.forClass(PDU.class);
verify(snmpService, times(1)).send(pduCaptor.capture(), any(), eq(null), eq(thingHandler));
return pduCaptor.getValue().getVariableBindings().stream().findFirst().orElse(null);
} else {
verify(snmpService, never()).send(any(), any(), eq(null), eq(thingHandler));
return null;
}
}
protected void onResponseNumberStringChannel(SnmpChannelMode channelMode, boolean refresh) {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING, channelMode);
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new OctetString(TEST_STRING))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
if (refresh) {
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(new StringType(TEST_STRING)));
} else {
verify(thingHandlerCallback, never()).stateUpdated(any(), any());
}
}
protected State onResponseSwitchChannel(SnmpChannelMode channelMode, SnmpDatatype datatype, String onValue,
String offValue, Variable value, boolean refresh) {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, channelMode, datatype, onValue, offValue);
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), value)));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
if (refresh) {
ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), stateCaptor.capture());
return stateCaptor.getValue();
} else {
verify(thingHandlerCallback, never()).stateUpdated(any(), any());
return null;
}
}
protected void refresh(SnmpChannelMode channelMode, boolean refresh) throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING, channelMode);
waitForAssert(() -> assertEquals(ThingStatus.ONLINE, thingHandler.getThing().getStatusInfo().getStatus()));
verify(snmpService).addCommandResponder(any());
if (refresh) {
ArgumentCaptor<PDU> pduCaptor = ArgumentCaptor.forClass(PDU.class);
verify(snmpService, atLeast(1)).send(pduCaptor.capture(), any(), eq(null), eq(thingHandler));
Vector<? extends VariableBinding> variables = pduCaptor.getValue().getVariableBindings();
assertTrue(variables.stream().filter(v -> v.getOid().toDottedString().equals(TEST_OID)).findFirst()
.isPresent());
} else {
verify(snmpService, never()).send(any(), any(), eq(null), eq(thingHandler));
}
}
protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode) {
setup(channelTypeUID, channelMode, null);
}
protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode, SnmpDatatype datatype) {
setup(channelTypeUID, channelMode, datatype, null, null);
}
protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode, SnmpDatatype datatype,
String onValue, String offValue) {
setup(channelTypeUID, channelMode, datatype, onValue, offValue, null);
}
protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode, SnmpDatatype datatype,
String onValue, String offValue, String exceptionValue) {
Map<String, Object> channelConfig = new HashMap<>();
Map<String, Object> thingConfig = new HashMap<>();
MockitoAnnotations.initMocks(this);
thingConfig.put("hostname", "localhost");
ThingBuilder thingBuilder = ThingBuilder.create(THING_TYPE_TARGET, THING_UID).withLabel("Test thing")
.withConfiguration(new Configuration(thingConfig));
if (channelTypeUID != null && channelMode != null) {
String itemType = SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER.equals(channelTypeUID) ? "Number" : "String";
channelConfig.put("oid", TEST_OID);
channelConfig.put("mode", channelMode.name());
if (datatype != null) {
channelConfig.put("datatype", datatype.name());
}
if (onValue != null) {
channelConfig.put("onvalue", onValue);
}
if (offValue != null) {
channelConfig.put("offvalue", offValue);
}
if (exceptionValue != null) {
channelConfig.put("exceptionValue", exceptionValue);
}
Channel channel = ChannelBuilder.create(CHANNEL_UID, itemType).withType(channelTypeUID)
.withConfiguration(new Configuration(channelConfig)).build();
thingBuilder.withChannel(channel);
}
thing = thingBuilder.build();
thingHandler = new SnmpTargetHandler(thing, snmpService);
thingHandler.getThing().setHandler(thingHandler);
thingHandler.setCallback(thingHandlerCallback);
doAnswer(answer -> {
((Thing) answer.getArgument(0)).setStatusInfo(answer.getArgument(1));
return null;
}).when(thingHandlerCallback).statusUpdated(any(), any());
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.ONLINE, thingHandler.getThing().getStatusInfo().getStatus()));
}
}

View File

@@ -0,0 +1,107 @@
/**
* 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.snmp.internal;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Collections;
import org.junit.Test;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.snmp4j.PDU;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Counter64;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UnsignedInteger32;
import org.snmp4j.smi.VariableBinding;
/**
* Tests cases for {@link SnmpTargetHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
public class SnmpTargetHandlerTest extends AbstractSnmpTargetHandlerTest {
@Test
public void testChannelsProperlyRefreshing() throws IOException {
refresh(SnmpChannelMode.READ, true);
refresh(SnmpChannelMode.READ_WRITE, true);
refresh(SnmpChannelMode.WRITE, false);
refresh(SnmpChannelMode.TRAP, false);
}
@Test
public void testChannelsProperlyUpdate() throws IOException {
onResponseNumberStringChannel(SnmpChannelMode.READ, true);
onResponseNumberStringChannel(SnmpChannelMode.READ_WRITE, true);
onResponseNumberStringChannel(SnmpChannelMode.WRITE, false);
onResponseNumberStringChannel(SnmpChannelMode.TRAP, false);
assertEquals(OnOffType.ON, onResponseSwitchChannel(SnmpChannelMode.READ, SnmpDatatype.STRING, "on", "off",
new OctetString("on"), true));
assertEquals(OnOffType.OFF, onResponseSwitchChannel(SnmpChannelMode.READ_WRITE, SnmpDatatype.INT32, "1", "2",
new Integer32(2), true));
assertNull(onResponseSwitchChannel(SnmpChannelMode.WRITE, SnmpDatatype.STRING, "on", "off",
new OctetString("on"), false));
assertNull(
onResponseSwitchChannel(SnmpChannelMode.TRAP, SnmpDatatype.INT32, "1", "2", new Integer32(2), false));
}
@Test
public void testCommandsAreProperlyHandledByNumberChannel() throws IOException {
VariableBinding variable;
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpDatatype.INT32,
new DecimalType(-5), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof Integer32);
assertEquals(-5, ((Integer32) variable.getVariable()).toInt());
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpDatatype.UINT32,
new DecimalType(10000), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof UnsignedInteger32);
assertEquals(10000, ((UnsignedInteger32) variable.getVariable()).toInt());
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER,
SnmpDatatype.COUNTER64, new DecimalType(10000), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof Counter64);
assertEquals(10000, ((Counter64) variable.getVariable()).toInt());
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpDatatype.FLOAT,
new DecimalType("12.4"), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof OctetString);
assertEquals("12.4", variable.getVariable().toString());
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpDatatype.INT32,
new StringType(TEST_STRING), false);
assertNull(variable);
}
@Test
public void testNumberChannelsProperlyUpdatingFloatValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpChannelMode.READ, SnmpDatatype.FLOAT);
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new OctetString("12.4"))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(new DecimalType("12.4")));
}
}

View File

@@ -0,0 +1,79 @@
/**
* 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.snmp.internal;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Collections;
import org.junit.Test;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.snmp4j.PDU;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.IpAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
/**
* Tests cases for {@link SnmpTargetHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
public class StringChannelTest extends AbstractSnmpTargetHandlerTest {
@Test
public void testCommandsAreProperlyHandledByStringChannel() throws IOException {
VariableBinding variable;
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING, SnmpDatatype.STRING,
new StringType(TEST_STRING), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof OctetString);
assertEquals(TEST_STRING, ((OctetString) variable.getVariable()).toString());
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING,
SnmpDatatype.IPADDRESS, new StringType(TEST_STRING), false);
assertNull(variable);
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING,
SnmpDatatype.IPADDRESS, new DecimalType(-5), false);
assertNull(variable);
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING,
SnmpDatatype.HEXSTRING, new StringType("AA bf 11"), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof OctetString);
assertEquals("aa bf 11", ((OctetString) variable.getVariable()).toHexString(' '));
variable = handleCommandNumberStringChannel(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING,
SnmpDatatype.IPADDRESS, new StringType(TEST_ADDRESS), true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof IpAddress);
assertEquals(TEST_ADDRESS, ((IpAddress) variable.getVariable()).toString());
}
@Test
public void testStringChannelsProperlyUpdatingOnHexString() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_STRING, SnmpChannelMode.READ, SnmpDatatype.HEXSTRING);
PDU responsePDU = new PDU(PDU.RESPONSE, Collections
.singletonList(new VariableBinding(new OID(TEST_OID), OctetString.fromHexStringPairs("aa11bb"))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(new StringType("aa 11 bb")));
}
}

View File

@@ -0,0 +1,120 @@
/**
* 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.snmp.internal;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Collections;
import org.junit.Test;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
import org.snmp4j.PDU;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Counter64;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
/**
* Tests cases for {@link SnmpTargetHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
public class SwitchChannelTest extends AbstractSnmpTargetHandlerTest {
@Test
public void testCommandsAreProperlyHandledBySwitchChannel() throws IOException {
VariableBinding variable;
variable = handleCommandSwitchChannel(SnmpDatatype.STRING, OnOffType.ON, "on", "off", true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof OctetString);
assertEquals("on", ((OctetString) variable.getVariable()).toString());
variable = handleCommandSwitchChannel(SnmpDatatype.STRING, OnOffType.OFF, "on", "off", true);
assertEquals(new OID(TEST_OID), variable.getOid());
assertTrue(variable.getVariable() instanceof OctetString);
assertEquals("off", ((OctetString) variable.getVariable()).toString());
variable = handleCommandSwitchChannel(SnmpDatatype.STRING, OnOffType.OFF, "on", null, false);
assertNull(variable);
}
@Test
public void testSwitchChannelsProperlyUpdatingOnValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.STRING, "on", "off");
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new OctetString("on"))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(OnOffType.ON));
}
@Test
public void testSwitchChannelsProperlyUpdatingOffValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.INT32, "0", "3");
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new Integer32(3))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(OnOffType.OFF));
}
@Test
public void testSwitchChannelsProperlyUpdatingHexValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.HEXSTRING, "AA bb 11",
"cc ba 1d");
PDU responsePDU = new PDU(PDU.RESPONSE, Collections
.singletonList(new VariableBinding(new OID(TEST_OID), OctetString.fromHexStringPairs("aabb11"))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(OnOffType.ON));
}
@Test
public void testSwitchChannelsIgnoresArbitraryValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.COUNTER64, "0", "12223");
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new Counter64(17))));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, never()).stateUpdated(eq(CHANNEL_UID), any());
}
@Test
public void testSwitchChannelSendsUndefExceptionValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.COUNTER64, "0", "12223");
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), Null.noSuchInstance)));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(UnDefType.UNDEF));
}
@Test
public void testSwitchChannelSendsConfiguredExceptionValue() throws IOException {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_SWITCH, SnmpChannelMode.READ, SnmpDatatype.COUNTER64, "0", "12223",
"OFF");
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), Null.noSuchInstance)));
ResponseEvent event = new ResponseEvent("test", null, null, responsePDU, null);
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(OnOffType.OFF));
}
}