[mqtt.generic] Support transformationPattern for thing's availabilityTopic (#12167)

* [mqtt.generic] Support transformationPattern for thing's availability payload
* [mqtt] Remove org.apache.commons.lang3.StringUtils dependency

Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
jimtng 2022-02-06 19:30:06 +10:00 committed by GitHub
parent 85ffbe37db
commit a3bd8caba9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 14 deletions

View File

@ -58,6 +58,7 @@ The following optional parameters can be set for the Thing:
* __availabilityTopic__: The MQTT topic that represents the availability of the thing. This can be the thing's LWT topic.
* __payloadAvailable__: Payload of the `Availability Topic`, when the device is available. Default: `ON`.
* __payloadNotAvailable__: Payload of the `Availability Topic`, when the device is *not* available. Default: `OFF`.
* __transformationPattern__: An optional transformation pattern like [JSONPath](https://goessner.net/articles/JsonPath/index.html#e2) that is applied to the incoming availability payload. Transformations can be chained by separating them with the mathematical intersection character "∩". The result of the transformations is then checked against `payloadAvailable` and `payloadNotAvailable`.
## Supported Channels

View File

@ -289,6 +289,12 @@ public abstract class AbstractMQTTThingHandler extends BaseThingHandler
@Override
public void addAvailabilityTopic(String availability_topic, String payload_available,
String payload_not_available) {
addAvailabilityTopic(availability_topic, payload_available, payload_not_available, null, null);
}
public void addAvailabilityTopic(String availability_topic, String payload_available, String payload_not_available,
@Nullable String transformation_pattern,
@Nullable TransformationServiceProvider transformationServiceProvider) {
availabilityStates.computeIfAbsent(availability_topic, topic -> {
Value value = new OnOffValue(payload_available, payload_not_available);
ChannelGroupUID groupUID = new ChannelGroupUID(getThing().getUID(), "availablility");
@ -308,6 +314,9 @@ public abstract class AbstractMQTTThingHandler extends BaseThingHandler
public void postChannelCommand(ChannelUID channelUID, Command value) {
}
});
if (transformation_pattern != null && transformationServiceProvider != null) {
state.addTransformation(transformation_pattern, transformationServiceProvider);
}
MqttBrokerConnection connection = getConnection();
if (connection != null) {
state.start(connection, scheduler, 0);

View File

@ -20,6 +20,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -94,6 +95,10 @@ public class ChannelState implements MqttMessageSubscriber {
transformationsIn.add(transformation);
}
public void addTransformation(String transformation, TransformationServiceProvider transformationServiceProvider) {
parseTransformation(transformation, transformationServiceProvider).forEach(t -> addTransformation(t));
}
/**
* Add a transformation that is applied for each value to be published.
* The transformations are executed in order.
@ -104,6 +109,18 @@ public class ChannelState implements MqttMessageSubscriber {
transformationsOut.add(transformation);
}
public void addTransformationOut(String transformation,
TransformationServiceProvider transformationServiceProvider) {
parseTransformation(transformation, transformationServiceProvider).forEach(t -> addTransformationOut(t));
}
public static Stream<ChannelStateTransformation> parseTransformation(String transformation,
TransformationServiceProvider transformationServiceProvider) {
String[] transformations = transformation.split("");
return Stream.of(transformations).filter(t -> !t.isBlank())
.map(t -> new ChannelStateTransformation(t, transformationServiceProvider));
}
/**
* Clear transformations
*/

View File

@ -19,15 +19,12 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler;
import org.openhab.binding.mqtt.generic.ChannelConfig;
import org.openhab.binding.mqtt.generic.ChannelState;
import org.openhab.binding.mqtt.generic.ChannelStateTransformation;
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
@ -126,19 +123,11 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
*/
protected ChannelState createChannelState(ChannelConfig channelConfig, ChannelUID channelUID, Value valueState) {
ChannelState state = new ChannelState(channelConfig, channelUID, valueState, this);
String[] transformations;
// Incoming value transformations
transformations = channelConfig.transformationPattern.split("");
Stream.of(transformations).filter(StringUtils::isNotBlank)
.map(t -> new ChannelStateTransformation(t, transformationServiceProvider))
.forEach(t -> state.addTransformation(t));
state.addTransformation(channelConfig.transformationPattern, transformationServiceProvider);
// Outgoing value transformations
transformations = channelConfig.transformationPatternOut.split("");
Stream.of(transformations).filter(StringUtils::isNotBlank)
.map(t -> new ChannelStateTransformation(t, transformationServiceProvider))
.forEach(t -> state.addTransformationOut(t));
state.addTransformationOut(channelConfig.transformationPatternOut, transformationServiceProvider);
return state;
}
@ -195,7 +184,8 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
String availabilityTopic = config.availabilityTopic;
if (availabilityTopic != null) {
addAvailabilityTopic(availabilityTopic, config.payloadAvailable, config.payloadNotAvailable);
addAvailabilityTopic(availabilityTopic, config.payloadAvailable, config.payloadNotAvailable,
config.transformationPattern, transformationServiceProvider);
} else {
clearAllAvailabilityTopics();
}

View File

@ -38,4 +38,9 @@ public class GenericThingConfiguration {
* payload for the availability topic when the device is *not* available.
*/
public String payloadNotAvailable = OnOffType.OFF.toString();
/**
* transformation pattern for the availability payload
*/
public @Nullable String transformationPattern;
}

View File

@ -30,6 +30,19 @@
<description>Payload of the 'Availability Topic', when the device is *not* available. Default: 'OFF'</description>
<advanced>true</advanced>
</parameter>
<parameter name="transformationPattern" type="text">
<label>Availability Payload Transformations</label>
<description>
<![CDATA[
Applies transformations to the incoming availability payload.
A transformation example for a received JSON would be "JSONPATH:$.status" for
a json {status: "Online"}.
You can chain transformations by separating them with the intersection character ∩.
]]>
</description>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>