added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -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>
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user