Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mqtt] Enable unit of measurements for number values #6763

Merged
merged 1 commit into from
Jan 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 1 addition & 17 deletions bundles/org.openhab.binding.mqtt.generic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ You can connect this channel to a String item.
* __min__: An optional minimum value.
* __max__: An optional maximum value.
* __step__: For decrease, increase commands the step needs to be known
* __unit__: Unit of measurement (optional). For supported units see [OpenHAB: List of Units](https://www.openhab.org/docs/concepts/units-of-measurement.html#list-of-units). Examples: "°C", "°F"

A decimal value (like 0.2) is send to the MQTT topic if the number has a fractional part.
If you always require an integer, please use the formatter.
Expand Down Expand Up @@ -245,23 +246,6 @@ Here are a few examples:
- For an output of *May 23, 1995* use "%1$**tb** %1$**te**,%1$**tY**".
- For an output of *23.05.1995* use "%1$**td**.%1$**tm**.%1$**tY**".
- For an output of *23:15* use "%1$**tH**:%1$**tM**".

Default pattern applied for each type:
| Type | Parameter | Pattern | Comment |
| ---------------- | --------------------------------- | ------------------- | ------- |
| __string__ | String | "%s" |
| __number__ | BigDecimal | "%f" | The default will remove trailing zeros after the decimal point.
| __dimmer__ | BigDecimal | "%f" | The default will remove trailing zeros after the decimal point.
| __contact__ | String | -- | No pattern supported. Always **on** and **off** strings.
| __switch__ | String | -- | No pattern supported. Always **on** and **off** strings.
| __colorRGB__ | BigDecimal, BigDecimal, BigDecimal| "%1$d,%2$d,%3$d" | Parameters are **red**, **green** and **blue** components.
| __colorHSB__ | BigDecimal, BigDecimal, BigDecimal| "%1$d,%2$d,%3$d" | Parameters are **hue**, **saturation** and **brightness** components.
| __location__ | BigDecimal, BigDecimal | "%2$f,%3$f,%1$f" | Parameters are **altitude**, **latitude** and **longitude**, altitude is only in default pattern, if value is not '0'.
| __image__ | -- | -- | No publishing supported.
| __datetime__ | ZonedDateTime | "%1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS.%1$tN" | Trailing zeros of the nanoseconds are removed.
| __rollershutter__| String | "%s" | No pattern supported. Always **up**, **down**, **stop** string or integer percent value.

Any outgoing value transformation will **always** result in a __string__ value.

## Troubleshooting

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand All @@ -24,13 +25,11 @@
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.TypeParser;
import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection;
import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber;
import org.openhab.binding.mqtt.generic.values.TextValue;
import org.openhab.binding.mqtt.generic.values.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -159,7 +158,8 @@ public void processMessage(String topic, byte[] payload) {
if (transformedValue != null) {
strValue = transformedValue;
} else {
logger.debug("Transformation '{}' returned null on '{}', discarding message", strValue, t.serviceName);
logger.debug("Transformation '{}' returned null on '{}', discarding message", strValue,
t.serviceName);
receivedOrTimeout();
return;
}
Expand Down Expand Up @@ -332,7 +332,7 @@ public boolean hasSubscribed() {
public CompletableFuture<Boolean> publishValue(Command command) {
cachedValue.update(command);

Value mqttCommandValue = cachedValue;
String mqttCommandValue = cachedValue.getMQTTpublishValue();

final MqttBrokerConnection connection = this.connection;

Expand All @@ -352,36 +352,29 @@ public CompletableFuture<Boolean> publishValue(Command command) {

// Outgoing transformations
for (ChannelStateTransformation t : transformationsOut) {
String commandString = mqttCommandValue.getMQTTpublishValue(null);
String transformedValue = t.processValue(commandString);
String transformedValue = t.processValue(mqttCommandValue);
if (transformedValue != null) {
Value textValue = new TextValue();
textValue.update(new StringType(transformedValue));
mqttCommandValue = textValue;
mqttCommandValue = transformedValue;
} else {
logger.debug("Transformation '{}' returned null on '{}', discarding message", mqttCommandValue,
t.serviceName);
return CompletableFuture.completedFuture(false);
}
}

String commandString;

// Formatter: Applied before the channel state value is published to the MQTT broker.
if (config.formatBeforePublish.length() > 0) {
try {
commandString = mqttCommandValue.getMQTTpublishValue(config.formatBeforePublish);
try (Formatter formatter = new Formatter()) {
Formatter format = formatter.format(config.formatBeforePublish, mqttCommandValue);
mqttCommandValue = format.toString();
} catch (IllegalFormatException e) {
logger.debug("Format pattern incorrect for {}", channelUID, e);
commandString = mqttCommandValue.getMQTTpublishValue(null);
}
} else {
commandString = mqttCommandValue.getMQTTpublishValue(null);
}

int qos = (config.qos != null) ? config.qos : connection.getQos();

return connection.publish(config.commandTopic, commandString.getBytes(), qos, config.retained);
return connection.publish(config.commandTopic, mqttCommandValue.getBytes(), qos, config.retained);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,15 @@ public void dispose() {
// Remove all state descriptions of this handler
channelStateByChannelUID.forEach((uid, state) -> stateDescProvider.remove(uid));
super.dispose();
// there is a design flaw, we can't clean up our stuff because it is needed by the super-class on disposal for unsubscribing
// there is a design flaw, we can't clean up our stuff because it is needed by the super-class on disposal for
// unsubscribing
channelStateByChannelUID.clear();
}

@Override
public CompletableFuture<Void> unsubscribeAll() {
return CompletableFuture.allOf(channelStateByChannelUID.values().stream().map(ChannelState::stop)
.toArray(CompletableFuture[]::new));
return CompletableFuture.allOf(
channelStateByChannelUID.values().stream().map(ChannelState::stop).toArray(CompletableFuture[]::new));
}

/**
Expand Down Expand Up @@ -153,9 +154,12 @@ public void initialize() {
Value value = ValueFactory.createValueState(channelConfig, channelTypeUID.getId());
ChannelState channelState = createChannelState(channelConfig, channel.getUID(), value);
channelStateByChannelUID.put(channel.getUID(), channelState);
StateDescription description = value.createStateDescription(channelConfig.unit,
StringUtils.isBlank(channelConfig.commandTopic));
stateDescProvider.setDescription(channel.getUID(), description);
StateDescription description = value
.createStateDescription(StringUtils.isBlank(channelConfig.commandTopic)).build()
.toStateDescription();
if (description != null) {
stateDescProvider.setDescription(channel.getUID(), description);
}
} catch (IllegalArgumentException e) {
logger.warn("Channel configuration error", e);
configErrors.add(channel.getUID());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,22 @@ public void update(Command command) throws IllegalArgumentException {
private static BigDecimal factor = new BigDecimal(2.5);

@Override
public String getMQTTpublishValue(@Nullable String pattern) {
public String getMQTTpublishValue() {
if (state == UnDefType.UNDEF) {
return "";
}

String formatPattern = pattern;
if (formatPattern == null || "%s".equals(formatPattern)) {
formatPattern = "%1$d,%2$d,%3$d";
}
// ignore the pattern. Don't know
if (isRGB) {
PercentType[] rgb = ((HSBType) state).toRGB();
return String.format(formatPattern, rgb[0].toBigDecimal().multiply(factor).intValue(),
rgb[1].toBigDecimal().multiply(factor).intValue(),
rgb[2].toBigDecimal().multiply(factor).intValue());
StringBuilder b = new StringBuilder();
b.append(rgb[0].toBigDecimal().multiply(factor).intValue());
b.append(',');
b.append(rgb[1].toBigDecimal().multiply(factor).intValue());
b.append(',');
b.append(rgb[2].toBigDecimal().multiply(factor).intValue());
return b.toString();
} else {
return state.toString();
}
HSBType type = (HSBType) state;
return String.format(formatPattern, type.getHue().intValue(), type.getSaturation().intValue(),
type.getBrightness().intValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.CoreItemFactory;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.StringType;
Expand Down Expand Up @@ -45,14 +44,11 @@ public void update(Command command) throws IllegalArgumentException {
}

@Override
public String getMQTTpublishValue(@Nullable String pattern) {
public String getMQTTpublishValue() {
if (state == UnDefType.UNDEF) {
return "";
}
String formatPattern = pattern;
if (formatPattern == null || "%s".contentEquals(formatPattern)) {
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(((DateTimeType) state).getZonedDateTime());
}
return String.format(formatPattern, ((DateTimeType) state).getZonedDateTime());

return ((DateTimeType) state).getZonedDateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,10 @@
*/
package org.openhab.binding.mqtt.generic.values;

import java.math.BigDecimal;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.CoreItemFactory;
import org.eclipse.smarthome.core.library.types.PointType;
import org.eclipse.smarthome.core.library.types.StringType;
Expand All @@ -36,22 +32,6 @@ public LocationValue() {
super(CoreItemFactory.LOCATION, Stream.of(PointType.class, StringType.class).collect(Collectors.toList()));
}

@Override
public @NonNull String getMQTTpublishValue(@Nullable String pattern) {
String formatPattern = pattern;
PointType point = ((PointType) state);

if (formatPattern == null || "%s".equals(formatPattern)) {
if (point.getAltitude().toBigDecimal().equals(BigDecimal.ZERO)) {
formatPattern = "%2$f,%3$f";
} else {
formatPattern = "%2$f,%3$f,%1$f";
}
}
return String.format(Locale.ROOT, formatPattern, point.getAltitude().toBigDecimal(),
point.getLatitude().toBigDecimal(), point.getLongitude().toBigDecimal());
}

@Override
public void update(Command command) throws IllegalArgumentException {
if (command instanceof PointType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@
package org.openhab.binding.mqtt.generic.values;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.CoreItemFactory;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.types.UpDownType;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.StateDescription;
import org.eclipse.smarthome.core.types.StateDescriptionFragmentBuilder;
import org.eclipse.smarthome.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import tec.uom.se.AbstractUnit;

/**
* Implements a number value.
*
Expand All @@ -47,13 +48,16 @@ public class NumberValue extends Value {
private final @Nullable BigDecimal min;
private final @Nullable BigDecimal max;
private final BigDecimal step;
private final String unit;

public NumberValue(@Nullable BigDecimal min, @Nullable BigDecimal max, @Nullable BigDecimal step) {
super(CoreItemFactory.NUMBER, Stream.of(DecimalType.class, IncreaseDecreaseType.class, UpDownType.class)
public NumberValue(@Nullable BigDecimal min, @Nullable BigDecimal max, @Nullable BigDecimal step,
@Nullable String unit) {
super(CoreItemFactory.NUMBER, Stream.of(QuantityType.class, IncreaseDecreaseType.class, UpDownType.class)
.collect(Collectors.toList()));
this.min = min;
this.max = max;
this.step = step == null ? new BigDecimal(1.0) : step;
this.step = step == null ? BigDecimal.ONE : step;
this.unit = unit == null ? "" : unit;
}

protected boolean checkConditions(BigDecimal newValue, DecimalType oldvalue) {
Expand All @@ -69,24 +73,10 @@ protected boolean checkConditions(BigDecimal newValue, DecimalType oldvalue) {
return true;
}

@Override
public @NonNull String getMQTTpublishValue(@Nullable String pattern) {
if (state == UnDefType.UNDEF) {
return "";
}

String formatPattern = pattern;
if (formatPattern == null || "%s".equals(formatPattern)) {
formatPattern = "%f";
}

return state.format(formatPattern);
}

@Override
public void update(Command command) throws IllegalArgumentException {
DecimalType oldvalue = (state == UnDefType.UNDEF) ? new DecimalType() : (DecimalType) state;
BigDecimal newValue;
BigDecimal newValue = null;
if (command instanceof DecimalType) {
if (!checkConditions(((DecimalType) command).toBigDecimal(), oldvalue)) {
return;
Expand All @@ -102,6 +92,23 @@ public void update(Command command) throws IllegalArgumentException {
return;
}
state = new DecimalType(newValue);
} else if (command instanceof QuantityType<?>) {
QuantityType<?> qType = (QuantityType<?>) command;

if (qType.getUnit().isCompatible(AbstractUnit.ONE)) {
newValue = qType.toBigDecimal();
} else {
qType = qType.toUnit(unit);
if (qType != null) {
newValue = qType.toBigDecimal();
}
}
if (newValue != null) {
if (!checkConditions(newValue, oldvalue)) {
return;
}
state = new DecimalType(newValue);
}
} else {
newValue = new BigDecimal(command.toString());
if (!checkConditions(newValue, oldvalue)) {
Expand All @@ -112,7 +119,20 @@ public void update(Command command) throws IllegalArgumentException {
}

@Override
public StateDescription createStateDescription(String unit, boolean readOnly) {
return new StateDescription(min, max, step, "%s " + unit.replace("%", "%%"), readOnly, Collections.emptyList());
public StateDescriptionFragmentBuilder createStateDescription(boolean readOnly) {
StateDescriptionFragmentBuilder builder = super.createStateDescription(readOnly);
BigDecimal max = this.max;
if (max != null) {
builder = builder.withMaximum(max);
}
BigDecimal min = this.min;
if (min != null) {
builder = builder.withMinimum(min);
}
builder = builder.withStep(step);
if (this.unit.length() > 0) {
builder = builder.withPattern("%s " + this.unit.replace("%", "%%"));
}
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void update(Command command) throws IllegalArgumentException {
}

@Override
public String getMQTTpublishValue(@Nullable String pattern) {
public String getMQTTpublishValue() {
return (state == OnOffType.ON) ? onCommand : offCommand;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void update(Command command) throws IllegalArgumentException {
}

@Override
public String getMQTTpublishValue(@Nullable String pattern) {
public String getMQTTpublishValue() {
return (state == OpenClosedType.OPEN) ? openString : closeString;
}
}
Loading