If it is set to `IPADDRESS`, an SNMP IP address object is constructed from the item's value.
The `HEXSTRING` datatype converts a hexadecimal string (e.g. `aa bb 11`) to the respective octet string before sending data to the target (and vice versa for receiving data).
+`number`-type channels can have a parameter `unit` if their `mode` is set to `READ`. This will result in a state update applying [UoM](https://www.openhab.org/docs/concepts/units-of-measurement.html) to the received data if the UoM symbol is recognised.
+
`switch`-type channels send a pre-defined value if they receive `ON` or `OFF` command in `WRITE` or `READ_WRITE` mode.
In `READ`, `READ_WRITE` or `TRAP` mode they change to either `ON` or `OFF` on these values.
The parameters used for defining the values are `onvalue` and `offvalue`.
```
Thing snmp:target:router [ hostname="192.168.0.1", protocol="v2c" ] {
Channels:
- Type number : inBytes [ oid=".1.3.6.1.2.1.31.1.1.1.6.2", mode="READ" ]
+ Type number : inBytes [ oid=".1.3.6.1.2.1.31.1.1.1.6.2", mode="READ", unit="B" ]
Type number : outBytes [ oid=".1.3.6.1.2.1.31.1.1.1.10.2", mode="READ" ]
Type number : if4Status [ oid="1.3.6.1.2.1.2.2.1.7.4", mode="TRAP" ]
Type switch : if4Command [ oid="1.3.6.1.2.1.2.2.1.7.4", mode="READ_WRITE", datatype="UINT32", onvalue="2", offvalue="0" ]
```
Number inBytes "Router bytes in [%d]" { channel="snmp:target:router:inBytes" }
+Number inGigaBytes "Router gigabytes in [%d GB]" { channel="snmp:target:router:inBytes" }
Number outBytes "Router bytes out [%d]" { channel="snmp:target:router:outBytes" }
Number if4Status "Router interface 4 status [%d]" { channel="snmp:target:router:if4Status" }
Switch if4Command "Router interface 4 switch [%s]" { channel="snmp:target:router:if4Command" }
import static org.openhab.binding.snmp.internal.SnmpBindingConstants.*;
import java.io.IOException;
+import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import javax.measure.Unit;
+import javax.measure.format.MeasurementParseException;
+
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.SnmpTargetConfiguration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
+import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.AbstractTarget;
Variable onValue = null;
Variable offValue = null;
State exceptionValue = UnDefType.UNDEF;
+ Unit<?> unit = null;
if (CHANNEL_TYPE_UID_NUMBER.equals(channel.getChannelTypeUID())) {
if (datatype == null) {
if (configExceptionValue != null) {
exceptionValue = DecimalType.valueOf(configExceptionValue);
}
+ if (config.unit != null) {
+ if (config.mode != SnmpChannelMode.READ) {
+ logger.warn("units only supported for readonly channels, ignored for channel {}", channel.getUID());
+ } else {
+ try {
+ unit = UnitUtils.parseUnit(config.unit);
+ } catch (MeasurementParseException e) {
+ logger.warn("unrecognised unit '{}', ignored for channel '{}'", config.unit, channel.getUID());
+ }
+ }
+ }
} else if (CHANNEL_TYPE_UID_STRING.equals(channel.getChannelTypeUID())) {
if (datatype == null) {
datatype = SnmpDatatype.STRING;
return null;
}
return new SnmpInternalChannelConfiguration(channel.getUID(), new OID(oid), config.mode, datatype, onValue,
- offValue, exceptionValue, config.doNotLogException);
+ offValue, exceptionValue, config.doNotLogException, unit);
}
private void generateChannelConfigs() {
state = channelConfig.exceptionValue;
} else if (CHANNEL_TYPE_UID_NUMBER.equals(channel.getChannelTypeUID())) {
try {
+ BigDecimal numericState;
+ final @Nullable Unit<?> unit = channelConfig.unit;
if (channelConfig.datatype == SnmpDatatype.FLOAT) {
- state = new DecimalType(value.toString());
+ numericState = new BigDecimal(value.toString());
+ } else {
+ numericState = BigDecimal.valueOf(value.toLong());
+ }
+ if (unit != null) {
+ state = new QuantityType<>(numericState, unit);
} else {
- state = new DecimalType(value.toLong());
+ state = new DecimalType(numericState);
}
} catch (UnsupportedOperationException e) {
logger.warn("could not convert {} to number for channel {}", value, channelUID);
public @Nullable String exceptionValue;
public boolean doNotLogException = false;
+
+ public @Nullable String unit;
}
*/
package org.openhab.binding.snmp.internal.config;
+import javax.measure.Unit;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.snmp.internal.SnmpChannelMode;
public final @Nullable Variable offValue;
public final State exceptionValue;
public final boolean doNotLogException;
+ public final @Nullable Unit<?> unit;
public SnmpInternalChannelConfiguration(ChannelUID channelUID, OID oid, SnmpChannelMode mode, SnmpDatatype datatype,
- @Nullable Variable onValue, @Nullable Variable offValue, State exceptionValue, boolean doNotLogException) {
+ @Nullable Variable onValue, @Nullable Variable offValue, State exceptionValue, boolean doNotLogException,
+ @Nullable Unit<?> unit) {
this.channelUID = channelUID;
this.oid = oid;
this.mode = mode;
this.offValue = offValue;
this.exceptionValue = exceptionValue;
this.doNotLogException = doNotLogException;
+ this.unit = unit;
}
}
<description>Value to send if an SNMP exception occurs (default: UNDEF)</description>
<advanced>true</advanced>
</parameter>
+ <parameter name="unit" type="text">
+ <label>Unit Of Measurement</label>
+ <description>Unit of measurement (optional). The unit is used for representing the value in the GUI as well as for
+ converting incoming values (like from '°F' to '°C'). Examples: "°C", "°F"</description>
+ <advanced>true</advanced>
+ </parameter>
</config-description>
</channel-type>
protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode, SnmpDatatype datatype,
String onValue, String offValue, String exceptionValue) {
+ setup(channelTypeUID, channelMode, datatype, onValue, offValue, exceptionValue, null);
+ }
+
+ protected void setup(ChannelTypeUID channelTypeUID, SnmpChannelMode channelMode, SnmpDatatype datatype,
+ String onValue, String offValue, String exceptionValue, String unit) {
Map<String, Object> channelConfig = new HashMap<>();
Map<String, Object> thingConfig = new HashMap<>();
mocks = MockitoAnnotations.openMocks(this);
if (exceptionValue != null) {
channelConfig.put("exceptionValue", exceptionValue);
}
+ if (unit != null) {
+ channelConfig.put("unit", unit);
+ }
Channel channel = ChannelBuilder.create(CHANNEL_UID, itemType).withType(channelTypeUID)
.withConfiguration(new Configuration(channelConfig)).build();
thingBuilder.withChannel(channel);
import org.junit.jupiter.api.Test;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.ThingStatus;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
verifyStatus(ThingStatus.ONLINE);
}
+ @Test
+ public void testNumberChannelsProperlyHandlingUnits() throws IOException {
+ setup(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpChannelMode.READ, SnmpDatatype.FLOAT, null, null, null,
+ "°C");
+ 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 QuantityType<>(12.4, SIUnits.CELSIUS)));
+ verifyStatus(ThingStatus.ONLINE);
+ }
+
@Test
public void testCancelingAsyncRequest() {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpChannelMode.READ, SnmpDatatype.FLOAT);