mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[mqtt.generic] Support chaining transformations without an intersection symbol (#17290)
* [mqtt.generic] Support chaining transformations without an intersection symbol Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
bd399e8e2a
commit
c1307a2764
@ -58,7 +58,8 @@ 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`.
|
||||
- **transformationPattern**: An optional transformation pattern like [JSONPath](https://goessner.net/articles/JsonPath/index.html#e2) that is applied to the incoming availability payload.
|
||||
The result of the transformations is then checked against `payloadAvailable` and `payloadNotAvailable`.
|
||||
|
||||
## Supported Channels
|
||||
|
||||
@ -266,7 +267,16 @@ If the availability status is available, it can be configured to set the Thing s
|
||||
```java
|
||||
Thing mqtt:topic:bedroom1-switch (mqtt:broker:myInsecureBroker) [ availabilityTopic="tele/bedroom1-switch/LWT", payloadAvailable="Online", payloadNotAvailable="Offline" ] {
|
||||
Channels:
|
||||
Type switch : power [ stateTopic="stat/bedroom1-switch/RESULT", transformationPattern="REGEX:(.*POWER.*)∩JSONPATH:$.POWER", commandTopic="cmnd/bedroom1-switch/POWER" ]
|
||||
Type switch : power [ stateTopic="stat/bedroom1-switch/RESULT", transformationPattern="REGEX((.*POWER.*))∩JSONPATH($.POWER)", commandTopic="cmnd/bedroom1-switch/POWER" ]
|
||||
}
|
||||
```
|
||||
|
||||
The transformation pattern can be chained using the intersection character "∩" as above, or by listing them separately:
|
||||
|
||||
```java
|
||||
Thing mqtt:topic:bedroom1-switch (mqtt:broker:myInsecureBroker) [ availabilityTopic="tele/bedroom1-switch/LWT", payloadAvailable="Online", payloadNotAvailable="Offline" ] {
|
||||
Channels:
|
||||
Type switch : power [ stateTopic="stat/bedroom1-switch/RESULT", transformationPattern="REGEX((.*POWER.*))","JSONPATH($.POWER)", commandTopic="cmnd/bedroom1-switch/POWER" ]
|
||||
}
|
||||
```
|
||||
|
||||
@ -277,6 +287,19 @@ Thing mqtt:topic:bedroom1-switch (mqtt:broker:myInsecureBroker) [ availabilityTo
|
||||
- The HomeAssistant Light Component does not support XY color changes.
|
||||
- The HomeAssistant Climate Components is not yet supported.
|
||||
|
||||
## Value Transformations
|
||||
|
||||
[Transformations](/docs/configuration/transformations.html) can be applied to:
|
||||
|
||||
- Incoming availability payload
|
||||
- Incoming value
|
||||
- Outgoing value
|
||||
|
||||
Transformations can be chained in the UI by listing each transformation on a separate line, or by separating them with the mathematical intersection character "∩".
|
||||
Transformations are defined using this syntax: `TYPE(FUNCTION)`, e.g.: `JSONPATH($.path)`.
|
||||
The syntax: `TYPE:FUNCTION` is still supported, e.g.: `JSONPATH:$.path`.
|
||||
Please note that the values will be discarded if one of the transformations failed (e.g. REGEX did not match) or returned `null`.
|
||||
|
||||
## Incoming Value Transformation
|
||||
|
||||
All mentioned channels allow an optional transformation for incoming MQTT topic values.
|
||||
@ -285,20 +308,22 @@ This is required if your received value is wrapped in a JSON or XML response.
|
||||
|
||||
Here are a few examples to unwrap a value from a complex response:
|
||||
|
||||
| Received value | Tr. Service | Transformation |
|
||||
|---------------------------------------------------------------------|-------------|-------------------------------------------|
|
||||
| `{device: {status: { temperature: 23.2 }}}` | JSONPATH | `JSONPATH:$.device.status.temperature` |
|
||||
| `<device><status><temperature>23.2</temperature></status></device>` | XPath | `XPath:/device/status/temperature/text()` |
|
||||
| `THEVALUE:23.2°C` | REGEX | `REGEX::(.*?)°` |
|
||||
|
||||
Transformations can be chained by separating them with the mathematical intersection character "∩".
|
||||
Please note that the incoming value will be discarded if one transformation fails (e.g. REGEX did not match).
|
||||
| Received value | Tr. Service | Transformation |
|
||||
| ------------------------------------------------------------------- | ---------------- | ------------------------------------------ |
|
||||
| `{device: {status: { temperature: 23.2 }}}` | JSONPATH | `JSONPATH($.device.status.temperature)` |
|
||||
| `<device><status><temperature>23.2</temperature></status></device>` | XPath | `XPath(/device/status/temperature/text())` |
|
||||
| `THEVALUE:23.2°C` | REGEX | `REGEX(:(.*?)°)` |
|
||||
| `abc` | JS (UI defined) | `JS(config:js:35edb3735a)` |
|
||||
| `abc` | JS (file based) | `JS(to_uppercase.js)` |
|
||||
| `abc` | JS (inline) | `JS(\| input.toUpperCase() )` |
|
||||
| `true` | MAP (UI defined) | `MAP(config:map:54facda0f7)` |
|
||||
| `true` | MAP (file based) | `MAP(status.map)` |
|
||||
| `true` | MAP (inline) | `MAP(\|true=ON;false=OFF)` |
|
||||
|
||||
## Outgoing Value Transformation
|
||||
|
||||
All mentioned channels allow an optional transformation for outgoing values.
|
||||
Please prefer formatting as described in the next section whenever possible.
|
||||
Please note that value will be discarded and not sent if one transformation fails (e.g. REGEX did not match).
|
||||
|
||||
## Format before Publish
|
||||
|
||||
@ -321,19 +346,19 @@ Here are a few examples:
|
||||
- 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.
|
||||
| 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.
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
package org.openhab.binding.mqtt.generic;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -301,18 +302,19 @@ 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);
|
||||
addAvailabilityTopic(availability_topic, payload_available, payload_not_available, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAvailabilityTopic(String availability_topic, String payload_available, String payload_not_available,
|
||||
@Nullable String transformation_pattern,
|
||||
@Nullable TransformationServiceProvider transformationServiceProvider) {
|
||||
List<String> transformation_pattern) {
|
||||
availabilityStates.computeIfAbsent(availability_topic, topic -> {
|
||||
Value value = new OnOffValue(payload_available, payload_not_available);
|
||||
ChannelGroupUID groupUID = new ChannelGroupUID(getThing().getUID(), "availability");
|
||||
ChannelUID channelUID = new ChannelUID(groupUID, UIDUtils.encode(topic));
|
||||
ChannelState state = new ChannelState(ChannelConfigBuilder.create().withStateTopic(topic).build(),
|
||||
ChannelState state = new ChannelState(
|
||||
ChannelConfigBuilder.create().withStateTopic(topic)
|
||||
.withTransformationPattern(transformation_pattern).build(),
|
||||
channelUID, value, new ChannelStateUpdateListener() {
|
||||
@Override
|
||||
public void updateChannelState(ChannelUID channelUID, State value) {
|
||||
@ -328,9 +330,6 @@ 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);
|
||||
|
@ -12,8 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.mqtt.generic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to keep track of the availability of device using an availability topic or messages received
|
||||
@ -72,12 +73,9 @@ public interface AvailabilityTracker {
|
||||
* @param payload_not_available The value for the topic to indicate the device is offline.
|
||||
* @param transformation_pattern A transformation pattern to process the value before comparing to
|
||||
* payload_available/payload_not_available.
|
||||
* @param transformationServiceProvider The service provider to obtain the transformation service (required only if
|
||||
* transformation_pattern is not null).
|
||||
*/
|
||||
void addAvailabilityTopic(String availability_topic, String payload_available, String payload_not_available,
|
||||
@Nullable String transformation_pattern,
|
||||
@Nullable TransformationServiceProvider transformationServiceProvider);
|
||||
List<String> transformation_pattern);
|
||||
|
||||
void removeAvailabilityTopic(String availability_topic);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
package org.openhab.binding.mqtt.generic;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@ -47,8 +48,8 @@ public class ChannelConfig {
|
||||
public boolean trigger = false;
|
||||
public String unit = "";
|
||||
|
||||
public String transformationPattern = "";
|
||||
public String transformationPatternOut = "";
|
||||
public List<String> transformationPattern = List.of();
|
||||
public List<String> transformationPatternOut = List.of();
|
||||
public String formatBeforePublish = "%s";
|
||||
public String allowedStates = "";
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.mqtt.generic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
@ -80,4 +82,14 @@ public class ChannelConfigBuilder {
|
||||
config.trigger = trigger;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChannelConfigBuilder withTransformationPattern(List<String> pattern) {
|
||||
config.transformationPattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChannelConfigBuilder withTransformationPatternOut(List<String> pattern) {
|
||||
config.transformationPatternOut = pattern;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,12 @@
|
||||
package org.openhab.binding.mqtt.generic;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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;
|
||||
@ -33,6 +31,7 @@ import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.binding.generic.ChannelTransformation;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
@ -60,8 +59,8 @@ public class ChannelState implements MqttMessageSubscriber {
|
||||
|
||||
// Runtime variables
|
||||
private @Nullable MqttBrokerConnection connection;
|
||||
protected final List<ChannelStateTransformation> transformationsIn = new ArrayList<>();
|
||||
protected final List<ChannelStateTransformation> transformationsOut = new ArrayList<>();
|
||||
protected final ChannelTransformation incomingTransformation;
|
||||
protected final ChannelTransformation outgoingTransformation;
|
||||
private @Nullable ChannelStateUpdateListener channelStateUpdateListener;
|
||||
protected boolean hasSubscribed = false;
|
||||
private @Nullable ScheduledFuture<?> scheduledFuture;
|
||||
@ -84,56 +83,14 @@ public class ChannelState implements MqttMessageSubscriber {
|
||||
this.channelUID = channelUID;
|
||||
this.cachedValue = cachedValue;
|
||||
this.readOnly = config.commandTopic.isBlank();
|
||||
this.incomingTransformation = new ChannelTransformation(config.transformationPattern);
|
||||
this.outgoingTransformation = new ChannelTransformation(config.transformationPatternOut);
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return this.readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a transformation that is applied for each received MQTT topic value.
|
||||
* The transformations are executed in order.
|
||||
*
|
||||
* @param transformation A transformation
|
||||
*/
|
||||
public void addTransformation(ChannelStateTransformation transformation) {
|
||||
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.
|
||||
*
|
||||
* @param transformation A transformation
|
||||
*/
|
||||
public void addTransformationOut(ChannelStateTransformation transformation) {
|
||||
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
|
||||
*/
|
||||
public void clearTransformations() {
|
||||
transformationsIn.clear();
|
||||
transformationsOut.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached value state object of this message subscriber.
|
||||
* <p>
|
||||
@ -176,15 +133,15 @@ public class ChannelState implements MqttMessageSubscriber {
|
||||
|
||||
// String value: Apply transformations
|
||||
String strValue = new String(payload, StandardCharsets.UTF_8);
|
||||
for (ChannelStateTransformation t : transformationsIn) {
|
||||
String transformedValue = t.processValue(strValue);
|
||||
if (transformedValue != null) {
|
||||
strValue = transformedValue;
|
||||
} else {
|
||||
logger.debug("Transformation '{}' returned null on '{}', discarding message", strValue, t.serviceName);
|
||||
if (incomingTransformation.isPresent()) {
|
||||
Optional<String> transformedValue = incomingTransformation.apply(strValue);
|
||||
if (transformedValue.isEmpty()) {
|
||||
logger.debug("Transformation '{}' returned null on '{}', discarding message", strValue,
|
||||
incomingTransformation);
|
||||
receivedOrTimeout();
|
||||
return;
|
||||
}
|
||||
strValue = transformedValue.get();
|
||||
}
|
||||
|
||||
// Is trigger?: Special handling
|
||||
@ -380,7 +337,7 @@ public class ChannelState implements MqttMessageSubscriber {
|
||||
}
|
||||
|
||||
// Outgoing transformations
|
||||
for (ChannelStateTransformation t : transformationsOut) {
|
||||
if (outgoingTransformation.isPresent()) {
|
||||
Command cValue = mqttCommandValue;
|
||||
// Only pass numeric value for QuantityType.
|
||||
if (mqttCommandValue instanceof QuantityType<?> qtCommandValue) {
|
||||
@ -388,15 +345,15 @@ public class ChannelState implements MqttMessageSubscriber {
|
||||
|
||||
}
|
||||
String commandString = mqttFormatter.getMQTTpublishValue(cValue, "%s");
|
||||
String transformedValue = t.processValue(commandString);
|
||||
if (transformedValue != null) {
|
||||
mqttFormatter = new TextValue();
|
||||
mqttCommandValue = new StringType(transformedValue);
|
||||
} else {
|
||||
logger.debug("Transformation '{}' returned null on '{}', discarding message", mqttCommandValue,
|
||||
t.serviceName);
|
||||
Optional<String> transformedValue = outgoingTransformation.apply(commandString);
|
||||
if (transformedValue.isEmpty()) {
|
||||
logger.debug("Transformation '{}' returned null on '{}', discarding message", outgoingTransformation,
|
||||
commandString);
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
|
||||
mqttFormatter = new TextValue();
|
||||
mqttCommandValue = new StringType(transformedValue.get());
|
||||
}
|
||||
|
||||
String commandString;
|
||||
|
@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.mqtt.generic;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.transform.TransformationException;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A transformation for a {@link ChannelState}. It is applied for each received value on an MQTT topic.
|
||||
*
|
||||
* @author David Graeff - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChannelStateTransformation {
|
||||
private final Logger logger = LoggerFactory.getLogger(ChannelStateTransformation.class);
|
||||
private final TransformationServiceProvider provider;
|
||||
private WeakReference<@Nullable TransformationService> transformationService = new WeakReference<>(null);
|
||||
final String pattern;
|
||||
final String serviceName;
|
||||
|
||||
/**
|
||||
* Creates a new channel state transformer.
|
||||
*
|
||||
* @param pattern A transformation pattern, starting with the transformation service
|
||||
* name,followed by a colon and the transformation itself. An Example:
|
||||
* JSONPATH:$.device.status.temperature for a json {device: {status: {
|
||||
* temperature: 23.2 }}}.
|
||||
* @param provider The transformation service provider
|
||||
*/
|
||||
public ChannelStateTransformation(String pattern, TransformationServiceProvider provider) {
|
||||
this.provider = provider;
|
||||
int index = pattern.indexOf(':');
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException(
|
||||
"The transformation pattern must consist of the type and the pattern separated by a colon");
|
||||
}
|
||||
String type = pattern.substring(0, index).toUpperCase();
|
||||
this.pattern = pattern.substring(index + 1);
|
||||
this.serviceName = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new channel state transformer.
|
||||
*
|
||||
* @param serviceName A transformation service name.
|
||||
* @param pattern A transformation. An Example:
|
||||
* $.device.status.temperature for a json {device: {status: {
|
||||
* temperature: 23.2 }}} (for type <code>JSONPATH</code>).
|
||||
* @param provider The transformation service provider
|
||||
*/
|
||||
public ChannelStateTransformation(String serviceName, String pattern, TransformationServiceProvider provider) {
|
||||
this.serviceName = serviceName;
|
||||
this.pattern = pattern;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called by the {@link ChannelState} for each incoming MQTT value.
|
||||
*
|
||||
* @param value The incoming value
|
||||
* @return The transformed value
|
||||
*/
|
||||
protected @Nullable String processValue(String value) {
|
||||
TransformationService transformationService = this.transformationService.get();
|
||||
if (transformationService == null) {
|
||||
transformationService = provider.getTransformationService(serviceName);
|
||||
if (transformationService == null) {
|
||||
logger.warn("Transformation service {} for pattern {} not found!", serviceName, pattern);
|
||||
return value;
|
||||
}
|
||||
this.transformationService = new WeakReference<>(transformationService);
|
||||
}
|
||||
String returnValue = null;
|
||||
try {
|
||||
returnValue = transformationService.transform(pattern, value);
|
||||
} catch (TransformationException e) {
|
||||
logger.warn("Executing the {}-transformation failed: {}. Pattern: '{}'. Value: '{}'", serviceName,
|
||||
e.getMessage(), pattern, value);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.mqtt.generic;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
|
||||
/**
|
||||
* Provide a transformation service which can be used during MQTT topic transformation.
|
||||
*
|
||||
* @author Simon Kaufmann - initial contribution and API
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface TransformationServiceProvider {
|
||||
/**
|
||||
* Provide a {@link TransformationService} matching the given type.
|
||||
*
|
||||
* @param type the type of the requested {@link TransformationService}.
|
||||
* @return a {@link TransformationService} matching the given type.
|
||||
*/
|
||||
@Nullable
|
||||
TransformationService getTransformationService(String type);
|
||||
}
|
@ -19,15 +19,12 @@ import java.util.stream.Stream;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler;
|
||||
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.openhab.core.transform.TransformationHelper;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
@ -42,7 +39,7 @@ import org.osgi.service.component.annotations.Reference;
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class)
|
||||
@NonNullByDefault
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
private @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.of(MqttBindingConstants.GENERIC_MQTT_THING).collect(Collectors.toSet());
|
||||
@ -78,13 +75,8 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(MqttBindingConstants.GENERIC_MQTT_THING)) {
|
||||
return new GenericMQTTThingHandler(thing, stateDescriptionProvider, this, 1500);
|
||||
return new GenericMQTTThingHandler(thing, stateDescriptionProvider, 1500);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TransformationService getTransformationService(String type) {
|
||||
return TransformationHelper.getTransformationService(bundleContext, type);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import org.openhab.binding.mqtt.generic.ChannelConfig;
|
||||
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants;
|
||||
import org.openhab.binding.mqtt.generic.utils.FutureCollector;
|
||||
import org.openhab.binding.mqtt.generic.values.Value;
|
||||
@ -59,21 +58,18 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
|
||||
private final Logger logger = LoggerFactory.getLogger(GenericMQTTThingHandler.class);
|
||||
final Map<ChannelUID, ChannelState> channelStateByChannelUID = new HashMap<>();
|
||||
protected final MqttChannelStateDescriptionProvider stateDescProvider;
|
||||
protected final TransformationServiceProvider transformationServiceProvider;
|
||||
|
||||
/**
|
||||
* Creates a new Thing handler for generic MQTT channels.
|
||||
*
|
||||
* @param thing The thing of this handler
|
||||
* @param stateDescProvider A channel state provider
|
||||
* @param transformationServiceProvider The transformation service provider
|
||||
* @param subscribeTimeout The subscribe timeout
|
||||
*/
|
||||
public GenericMQTTThingHandler(Thing thing, MqttChannelStateDescriptionProvider stateDescProvider,
|
||||
TransformationServiceProvider transformationServiceProvider, int subscribeTimeout) {
|
||||
int subscribeTimeout) {
|
||||
super(thing, subscribeTimeout);
|
||||
this.stateDescProvider = stateDescProvider;
|
||||
this.transformationServiceProvider = transformationServiceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,14 +125,7 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
|
||||
* @return
|
||||
*/
|
||||
protected ChannelState createChannelState(ChannelConfig channelConfig, ChannelUID channelUID, Value valueState) {
|
||||
ChannelState state = new ChannelState(channelConfig, channelUID, valueState, this);
|
||||
|
||||
// Incoming value transformations
|
||||
state.addTransformation(channelConfig.transformationPattern, transformationServiceProvider);
|
||||
// Outgoing value transformations
|
||||
state.addTransformationOut(channelConfig.transformationPatternOut, transformationServiceProvider);
|
||||
|
||||
return state;
|
||||
return new ChannelState(channelConfig, channelUID, valueState, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -230,7 +219,7 @@ public class GenericMQTTThingHandler extends AbstractMQTTThingHandler implements
|
||||
|
||||
if (availabilityTopic != null) {
|
||||
addAvailabilityTopic(availabilityTopic, config.payloadAvailable, config.payloadNotAvailable,
|
||||
config.transformationPattern, transformationServiceProvider);
|
||||
config.transformationPattern);
|
||||
} else {
|
||||
clearAllAvailabilityTopics();
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.mqtt.generic.internal.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
@ -42,5 +44,5 @@ public class GenericThingConfiguration {
|
||||
/**
|
||||
* transformation pattern for the availability payload
|
||||
*/
|
||||
public @Nullable String transformationPattern;
|
||||
public List<String> transformationPattern = List.of();
|
||||
}
|
||||
|
@ -32,18 +32,19 @@
|
||||
<label>MQTT Command Topic</label>
|
||||
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -21,18 +21,19 @@
|
||||
<label>MQTT Command Topic</label>
|
||||
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -21,18 +21,19 @@
|
||||
<label>MQTT Command Topic</label>
|
||||
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -27,18 +27,19 @@
|
||||
<description>An MQTT topic that this thing will send a STOP command to. If not set, it will send STOP commands to the
|
||||
main commandTopic.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -21,18 +21,19 @@
|
||||
<label>MQTT Command Topic</label>
|
||||
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -21,18 +21,19 @@
|
||||
<label>MQTT Command Topic</label>
|
||||
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
|
||||
a json {device: {status: { temperature: 23.2 }}}.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations">
|
||||
<parameter name="transformationPatternOut" type="text" groupName="transformations" multiple="true">
|
||||
<label>Outgoing Value Transformation</label>
|
||||
<description><![CDATA[
|
||||
Applies a transformation before publishing a MQTT topic value.
|
||||
|
@ -15,14 +15,15 @@
|
||||
<label>MQTT Trigger Topic</label>
|
||||
<description>An MQTT topic that this thing will subscribe to, to receive the trigger</description>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations">
|
||||
<parameter name="transformationPattern" type="text" groupName="transformations" multiple="true">
|
||||
<label>Incoming Value Transformations</label>
|
||||
<description><![CDATA[
|
||||
Applies transformations to an incoming MQTT topic value.
|
||||
This can be used to map the events sent by the device to common values for all devices using,
|
||||
e.g. the MAP transformation.
|
||||
|
||||
You can chain transformations by separating them with the intersection character ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]></description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
@ -12,7 +12,7 @@ thing-type.config.mqtt.topic.payloadAvailable.description = Payload of the 'Avai
|
||||
thing-type.config.mqtt.topic.payloadNotAvailable.label = Device Unavailable Payload
|
||||
thing-type.config.mqtt.topic.payloadNotAvailable.description = Payload of the 'Availability Topic', when the device is *not* available. Default: 'OFF'
|
||||
thing-type.config.mqtt.topic.transformationPattern.label = Availability Payload Transformations
|
||||
thing-type.config.mqtt.topic.transformationPattern.description = 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 ∩.
|
||||
thing-type.config.mqtt.topic.transformationPattern.description = 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 listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
|
||||
# channel types
|
||||
|
||||
@ -64,7 +64,7 @@ thing-type.config.mqtt.color_channel.retained.description = The value will be pu
|
||||
thing-type.config.mqtt.color_channel.stateTopic.label = MQTT State Topic
|
||||
thing-type.config.mqtt.color_channel.stateTopic.description = An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.
|
||||
thing-type.config.mqtt.color_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.color_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.color_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.color_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.color_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.dimmer_channel.commandTopic.label = MQTT Command Topic
|
||||
@ -95,7 +95,7 @@ thing-type.config.mqtt.dimmer_channel.stateTopic.description = An MQTT topic tha
|
||||
thing-type.config.mqtt.dimmer_channel.step.label = Delta Value
|
||||
thing-type.config.mqtt.dimmer_channel.step.description = A number/dimmer channel can receive INCREASE/DECREASE commands and computes the target number by adding or subtracting this delta value.
|
||||
thing-type.config.mqtt.dimmer_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.dimmer_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.dimmer_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.dimmer_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.dimmer_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.number_channel.commandTopic.label = MQTT Command Topic
|
||||
@ -122,7 +122,7 @@ thing-type.config.mqtt.number_channel.stateTopic.description = An MQTT topic tha
|
||||
thing-type.config.mqtt.number_channel.step.label = Delta Value
|
||||
thing-type.config.mqtt.number_channel.step.description = A number/dimmer channel can receive INCREASE/DECREASE commands and computes the target number by adding or subtracting this delta value.
|
||||
thing-type.config.mqtt.number_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.number_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.number_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.number_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.number_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.number_channel.unit.label = Unit Of Measurement
|
||||
@ -161,7 +161,7 @@ thing-type.config.mqtt.rollershutter_channel.stopCommandTopic.description = An M
|
||||
thing-type.config.mqtt.rollershutter_channel.transformExtentsToString.label = Transform Commands at Extents to String
|
||||
thing-type.config.mqtt.rollershutter_channel.transformExtentsToString.description = If a command is 0 or 100, send that as UP or DOWN commands instead. Useful if your device doesn't support going to a specific position - only opening or closing, but you have front ends (say HomeKit) or rules that will only send percentage commands instead of UP/DOWN.
|
||||
thing-type.config.mqtt.rollershutter_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.rollershutter_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.rollershutter_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.rollershutter_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.rollershutter_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.string_channel.allowedStates.label = Allowed States
|
||||
@ -186,7 +186,7 @@ thing-type.config.mqtt.string_channel.retained.description = The value will be p
|
||||
thing-type.config.mqtt.string_channel.stateTopic.label = MQTT State Topic
|
||||
thing-type.config.mqtt.string_channel.stateTopic.description = An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.
|
||||
thing-type.config.mqtt.string_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.string_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.string_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.string_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.string_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.switch_channel.commandTopic.label = MQTT Command Topic
|
||||
@ -211,7 +211,7 @@ thing-type.config.mqtt.switch_channel.retained.description = The value will be p
|
||||
thing-type.config.mqtt.switch_channel.stateTopic.label = MQTT State Topic
|
||||
thing-type.config.mqtt.switch_channel.stateTopic.description = An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.
|
||||
thing-type.config.mqtt.switch_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.switch_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.switch_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.switch_channel.transformationPatternOut.label = Outgoing Value Transformation
|
||||
thing-type.config.mqtt.switch_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful.
|
||||
thing-type.config.mqtt.trigger_channel.group.transformations.label = Transform Values
|
||||
@ -219,4 +219,4 @@ thing-type.config.mqtt.trigger_channel.group.transformations.description = These
|
||||
thing-type.config.mqtt.trigger_channel.stateTopic.label = MQTT Trigger Topic
|
||||
thing-type.config.mqtt.trigger_channel.stateTopic.description = An MQTT topic that this thing will subscribe to, to receive the trigger
|
||||
thing-type.config.mqtt.trigger_channel.transformationPattern.label = Incoming Value Transformations
|
||||
thing-type.config.mqtt.trigger_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. This can be used to map the events sent by the device to common values for all devices using, e.g. the MAP transformation. You can chain transformations by separating them with the intersection character ∩.
|
||||
thing-type.config.mqtt.trigger_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. This can be used to map the events sent by the device to common values for all devices using, e.g. the MAP transformation. You can chain transformations by listing each transformation on a separate line, or by separating them with the intersection character ∩.
|
||||
|
@ -29,7 +29,7 @@
|
||||
<description>Payload of the 'Availability Topic', when the device is *not* available. Default: 'OFF'</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="transformationPattern" type="text">
|
||||
<parameter name="transformationPattern" type="text" multiple="true">
|
||||
<label>Availability Payload Transformations</label>
|
||||
<description>
|
||||
<![CDATA[
|
||||
@ -37,7 +37,8 @@
|
||||
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 ∩.
|
||||
You can chain transformations by listing each transformation on a separate line, or
|
||||
by separating them with the intersection character ∩.
|
||||
]]>
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
|
@ -24,6 +24,7 @@ import java.math.BigDecimal;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
@ -33,9 +34,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
@ -56,8 +59,13 @@ import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.transform.TransformationException;
|
||||
import org.openhab.core.transform.TransformationHelper;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.util.ColorUtil;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
/**
|
||||
* Tests the {@link ChannelState} class.
|
||||
@ -407,4 +415,97 @@ public class ChannelStateTests {
|
||||
assertThat(value.getChannelState(), is(instanceOf(RawType.class)));
|
||||
assertThat(((RawType) value.getChannelState()).getMimeType(), is("image/jpeg"));
|
||||
}
|
||||
|
||||
@Nested
|
||||
public class TransformationTests {
|
||||
// Copied from org.openhab.core.thing.binding.generic.ChannelTransformationTest
|
||||
private static final String T1_NAME = "TRANSFORM1";
|
||||
private static final String T1_PATTERN = "T1Pattern";
|
||||
private static final String T1_INPUT = "T1Input";
|
||||
private static final String T1_RESULT = "T1Result";
|
||||
|
||||
private static final String NULL_INPUT = "nullInput";
|
||||
private static final @Nullable String NULL_RESULT = null;
|
||||
|
||||
private @Mock @NonNullByDefault({}) TransformationService transformationService1Mock;
|
||||
|
||||
private @Mock @NonNullByDefault({}) BundleContext bundleContextMock;
|
||||
private @Mock @NonNullByDefault({}) ServiceReference<TransformationService> serviceRef1Mock;
|
||||
|
||||
private @NonNullByDefault({}) TransformationHelper transformationHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void init() throws TransformationException {
|
||||
Mockito.when(transformationService1Mock.transform(eq(T1_PATTERN), eq(T1_INPUT)))
|
||||
.thenAnswer(answer -> T1_RESULT);
|
||||
Mockito.when(transformationService1Mock.transform(eq(T1_PATTERN), eq(NULL_INPUT)))
|
||||
.thenAnswer(answer -> NULL_RESULT);
|
||||
|
||||
Mockito.when(serviceRef1Mock.getProperty(any())).thenReturn("TRANSFORM1");
|
||||
|
||||
Mockito.when(bundleContextMock.getService(serviceRef1Mock)).thenReturn(transformationService1Mock);
|
||||
|
||||
transformationHelper = new TransformationHelper(bundleContextMock);
|
||||
transformationHelper.setTransformationService(serviceRef1Mock);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
transformationHelper.deactivate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformationPatternTest() throws Exception {
|
||||
ChannelConfig config = ChannelConfigBuilder.create("state", "command")
|
||||
.withTransformationPattern(List.of(T1_NAME + ":" + T1_PATTERN)).build();
|
||||
ChannelState c = spy(new ChannelState(config, channelUIDMock, textValue, channelStateUpdateListenerMock));
|
||||
|
||||
CompletableFuture<@Nullable Void> future = c.start(connectionMock, scheduler, 100);
|
||||
c.processMessage("state", T1_INPUT.getBytes());
|
||||
future.get(300, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertThat(textValue.getChannelState().toString(), is(T1_RESULT));
|
||||
verify(channelStateUpdateListenerMock).updateChannelState(eq(channelUIDMock), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformationPatternReturningNullTest() throws Exception {
|
||||
ChannelConfig config = ChannelConfigBuilder.create("state", "command")
|
||||
.withTransformationPattern(List.of(T1_NAME + ":" + T1_PATTERN)).build();
|
||||
ChannelState c = spy(new ChannelState(config, channelUIDMock, textValue, channelStateUpdateListenerMock));
|
||||
|
||||
// First, test with an input that doesn't get transformed to null
|
||||
CompletableFuture<@Nullable Void> future = c.start(connectionMock, scheduler, 100);
|
||||
c.processMessage("state", T1_INPUT.getBytes());
|
||||
future.get(300, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertThat(textValue.getChannelState().toString(), is(T1_RESULT));
|
||||
verify(channelStateUpdateListenerMock).updateChannelState(eq(channelUIDMock), any());
|
||||
|
||||
clearInvocations(channelStateUpdateListenerMock);
|
||||
|
||||
// now test with an input that gets transformed to null
|
||||
future = c.start(connectionMock, scheduler, 100);
|
||||
c.processMessage("state", NULL_INPUT.getBytes());
|
||||
future.get(300, TimeUnit.MILLISECONDS);
|
||||
|
||||
// textValue should not have been updated
|
||||
assertThat(textValue.getChannelState().toString(), is(T1_RESULT));
|
||||
verify(channelStateUpdateListenerMock, never()).updateChannelState(eq(channelUIDMock), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformationPatternOutTest() throws Exception {
|
||||
ChannelConfig config = ChannelConfigBuilder.create("state", "command")
|
||||
.withTransformationPatternOut(List.of(T1_NAME + ":" + T1_PATTERN)).build();
|
||||
ChannelState c = spy(new ChannelState(config, channelUIDMock, textValue, channelStateUpdateListenerMock));
|
||||
|
||||
c.start(connectionMock, scheduler, 0).get(50, TimeUnit.MILLISECONDS);
|
||||
verify(connectionMock).subscribe(eq("state"), eq(c));
|
||||
|
||||
c.publishValue(new StringType(T1_INPUT)).get();
|
||||
verify(connectionMock).publish(eq("command"), argThat(p -> Arrays.equals(p, T1_RESULT.getBytes())),
|
||||
anyInt(), eq(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.mqtt.generic;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.openhab.binding.mqtt.generic.internal.handler.ThingChannelConstants.*;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler;
|
||||
import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
|
||||
/**
|
||||
* Tests cases for {@link ThingHandler} to test the json transformation.
|
||||
*
|
||||
* @author David Graeff - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@NonNullByDefault
|
||||
public class ChannelStateTransformationTests {
|
||||
|
||||
private @Mock @NonNullByDefault({}) TransformationService jsonPathServiceMock;
|
||||
private @Mock @NonNullByDefault({}) TransformationServiceProvider transformationServiceProviderMock;
|
||||
private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
|
||||
private @Mock @NonNullByDefault({}) Thing thingMock;
|
||||
private @Mock @NonNullByDefault({}) AbstractBrokerHandler bridgeHandlerMock;
|
||||
private @Mock @NonNullByDefault({}) MqttBrokerConnection connectionMock;
|
||||
|
||||
private @NonNullByDefault({}) GenericMQTTThingHandler thingHandler;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
ThingStatusInfo thingStatus = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
|
||||
|
||||
// Mock the thing: We need the thingUID and the bridgeUID
|
||||
when(thingMock.getUID()).thenReturn(TEST_GENERIC_THING);
|
||||
when(thingMock.getChannels()).thenReturn(THING_CHANNEL_LIST_WITH_JSON);
|
||||
when(thingMock.getStatusInfo()).thenReturn(thingStatus);
|
||||
when(thingMock.getConfiguration()).thenReturn(new Configuration());
|
||||
|
||||
// Return the mocked connection object if the bridge handler is asked for it
|
||||
when(bridgeHandlerMock.getConnectionAsync()).thenReturn(CompletableFuture.completedFuture(connectionMock));
|
||||
|
||||
CompletableFuture<@Nullable Void> voidFutureComplete = new CompletableFuture<>();
|
||||
voidFutureComplete.complete(null);
|
||||
doReturn(voidFutureComplete).when(connectionMock).unsubscribeAll();
|
||||
doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).subscribe(any(), any());
|
||||
doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).unsubscribe(any(), any());
|
||||
|
||||
thingHandler = spy(new GenericMQTTThingHandler(thingMock, mock(MqttChannelStateDescriptionProvider.class),
|
||||
transformationServiceProviderMock, 1500));
|
||||
when(transformationServiceProviderMock.getTransformationService(anyString())).thenReturn(jsonPathServiceMock);
|
||||
|
||||
thingHandler.setCallback(callbackMock);
|
||||
// Return the bridge handler if the thing handler asks for it
|
||||
doReturn(bridgeHandlerMock).when(thingHandler).getBridgeHandler();
|
||||
|
||||
// We are by default online
|
||||
doReturn(thingStatus).when(thingHandler).getBridgeStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void initialize() throws Exception {
|
||||
when(thingMock.getChannels()).thenReturn(THING_CHANNEL_LIST_WITH_JSON);
|
||||
|
||||
thingHandler.initialize();
|
||||
ChannelState channelConfig = thingHandler.getChannelState(TEXT_CHANNEL_UID);
|
||||
assertThat(channelConfig.transformationsIn.get(0).pattern, is(JSON_PATH_PATTERN));
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void processMessageWithJSONPath() throws Exception {
|
||||
when(jsonPathServiceMock.transform(JSON_PATH_PATTERN, JSON_PATH_JSON)).thenReturn("23.2");
|
||||
|
||||
thingHandler.initialize();
|
||||
ChannelState channelConfig = thingHandler.getChannelState(TEXT_CHANNEL_UID);
|
||||
channelConfig.setChannelStateUpdateListener(thingHandler);
|
||||
|
||||
ChannelStateTransformation transformation = channelConfig.transformationsIn.get(0);
|
||||
|
||||
byte[] payload = JSON_PATH_JSON.getBytes();
|
||||
assertThat(transformation.pattern, is(JSON_PATH_PATTERN));
|
||||
// Test process message
|
||||
channelConfig.processMessage(channelConfig.getStateTopic(), payload);
|
||||
|
||||
verify(callbackMock).stateUpdated(eq(TEXT_CHANNEL_UID), argThat(arg -> "23.2".equals(arg.toString())));
|
||||
assertThat(channelConfig.getCache().getChannelState().toString(), is("23.2"));
|
||||
}
|
||||
}
|
@ -35,7 +35,6 @@ import org.openhab.binding.mqtt.generic.ChannelConfigBuilder;
|
||||
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.ThingHandlerHelper;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
||||
import org.openhab.binding.mqtt.generic.values.ValueFactory;
|
||||
@ -90,8 +89,8 @@ public class GenericThingHandlerTests {
|
||||
doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).publish(any(), any(), anyInt(),
|
||||
anyBoolean());
|
||||
|
||||
thingHandler = spy(new GenericMQTTThingHandler(thingMock, mock(MqttChannelStateDescriptionProvider.class),
|
||||
mock(TransformationServiceProvider.class), 1500));
|
||||
thingHandler = spy(
|
||||
new GenericMQTTThingHandler(thingMock, mock(MqttChannelStateDescriptionProvider.class), 1500));
|
||||
thingHandler.setCallback(callbackMock);
|
||||
|
||||
// Return the bridge handler if the thing handler asks for it
|
||||
|
@ -49,11 +49,7 @@ public class ThingChannelConstants {
|
||||
|
||||
public static final ChannelUID TEXT_CHANNEL_UID = new ChannelUID(TEST_GENERIC_THING, "mytext");
|
||||
|
||||
public static final String JSON_PATH_JSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}";
|
||||
public static final String JSON_PATH_PATTERN = "$.device.status.temperature";
|
||||
|
||||
public static final List<Channel> THING_CHANNEL_LIST = new ArrayList<>();
|
||||
public static final List<Channel> THING_CHANNEL_LIST_WITH_JSON = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create a channel with exact the parameters we need for the tests
|
||||
@ -74,12 +70,6 @@ public class ThingChannelConstants {
|
||||
THING_CHANNEL_LIST.add(cb("onoff", "Switch", onoffConfiguration(), ON_OFF_CHANNEL));
|
||||
THING_CHANNEL_LIST.add(cb("num", "Number", numberConfiguration(), NUMBER_CHANNEL));
|
||||
THING_CHANNEL_LIST.add(cb("percent", "Number:Dimensionless", percentageConfiguration(), PERCENTAGE_CHANNEL));
|
||||
|
||||
THING_CHANNEL_LIST_WITH_JSON.add(cb("mytext", "String", textConfigurationWithJson(), TEXT_WITH_JSON_CHANNEL));
|
||||
THING_CHANNEL_LIST_WITH_JSON.add(cb("onoff", "Switch", onoffConfiguration(), ON_OFF_CHANNEL));
|
||||
THING_CHANNEL_LIST_WITH_JSON.add(cb("num", "Number", numberConfiguration(), NUMBER_CHANNEL));
|
||||
THING_CHANNEL_LIST_WITH_JSON
|
||||
.add(cb("percent", "Number:Dimensionless", percentageConfiguration(), PERCENTAGE_CHANNEL));
|
||||
}
|
||||
|
||||
static Configuration textConfiguration() {
|
||||
@ -89,14 +79,6 @@ public class ThingChannelConstants {
|
||||
return new Configuration(data);
|
||||
}
|
||||
|
||||
static Configuration textConfigurationWithJson() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("stateTopic", "test/state");
|
||||
data.put("commandTopic", "test/command");
|
||||
data.put("transformationPattern", "JSONPATH:" + JSON_PATH_PATTERN);
|
||||
return new Configuration(data);
|
||||
}
|
||||
|
||||
private static Configuration numberConfiguration() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("stateTopic", "test/state");
|
||||
|
@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
@ -28,8 +27,6 @@ import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.transform.TransformationHelper;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
@ -42,7 +39,7 @@ import org.osgi.service.component.annotations.Reference;
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class)
|
||||
@NonNullByDefault
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final MqttChannelTypeProvider typeProvider;
|
||||
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
private final ChannelTypeRegistry channelTypeRegistry;
|
||||
@ -75,13 +72,8 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements
|
||||
|
||||
if (supportsThingType(thingTypeUID)) {
|
||||
return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
|
||||
this, 10000, 2000);
|
||||
10000, 2000);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TransformationService getTransformationService(String type) {
|
||||
return TransformationHelper.getTransformationService(bundleContext, type);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.mqtt.homeassistant.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -21,9 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.ChannelConfigBuilder;
|
||||
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.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.values.Value;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
@ -226,10 +225,19 @@ public class ComponentChannel {
|
||||
Channel channel;
|
||||
|
||||
channelUID = component.buildChannelUID(channelID);
|
||||
channelState = new HomeAssistantChannelState(
|
||||
ChannelConfigBuilder.create().withRetain(retain).withQos(qos).withStateTopic(stateTopic)
|
||||
.withCommandTopic(commandTopic).makeTrigger(trigger).withFormatter(format).build(),
|
||||
channelUID, valueState, channelStateUpdateListener, commandFilter);
|
||||
ChannelConfigBuilder channelConfigBuilder = ChannelConfigBuilder.create().withRetain(retain).withQos(qos)
|
||||
.withStateTopic(stateTopic).withCommandTopic(commandTopic).makeTrigger(trigger)
|
||||
.withFormatter(format);
|
||||
|
||||
if (templateIn != null) {
|
||||
channelConfigBuilder.withTransformationPattern(List.of(JINJA + ":" + templateIn));
|
||||
}
|
||||
if (templateOut != null) {
|
||||
channelConfigBuilder.withTransformationPatternOut(List.of(JINJA + ":" + templateOut));
|
||||
}
|
||||
|
||||
channelState = new HomeAssistantChannelState(channelConfigBuilder.build(), channelUID, valueState,
|
||||
channelStateUpdateListener, commandFilter);
|
||||
|
||||
// disabled by default components should always show up as advanced
|
||||
if (!component.isEnabledByDefault()) {
|
||||
@ -262,18 +270,6 @@ public class ComponentChannel {
|
||||
ComponentChannel result = new ComponentChannel(channelState, channel, stateDescription, commandDescription,
|
||||
channelStateUpdateListener);
|
||||
|
||||
TransformationServiceProvider transformationProvider = component.getTransformationServiceProvider();
|
||||
|
||||
final String templateIn = this.templateIn;
|
||||
if (templateIn != null && transformationProvider != null) {
|
||||
channelState
|
||||
.addTransformation(new ChannelStateTransformation(JINJA, templateIn, transformationProvider));
|
||||
}
|
||||
final String templateOut = this.templateOut;
|
||||
if (templateOut != null && transformationProvider != null) {
|
||||
channelState.addTransformationOut(
|
||||
new ChannelStateTransformation(JINJA, templateOut, transformationProvider));
|
||||
}
|
||||
if (addToComponent) {
|
||||
component.getChannelMap().put(channelID, result);
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.utils.FutureCollector;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory;
|
||||
@ -52,7 +51,6 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final ChannelStateUpdateListener updateListener;
|
||||
private final AvailabilityTracker tracker;
|
||||
private final TransformationServiceProvider transformationServiceProvider;
|
||||
private final boolean newStyleChannels;
|
||||
|
||||
protected final CompletableFuture<@Nullable Void> discoverFinishedFuture = new CompletableFuture<>();
|
||||
@ -80,13 +78,12 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
*/
|
||||
public DiscoverComponents(ThingUID thingUID, ScheduledExecutorService scheduler,
|
||||
ChannelStateUpdateListener channelStateUpdateListener, AvailabilityTracker tracker, Gson gson,
|
||||
TransformationServiceProvider transformationServiceProvider, boolean newStyleChannels) {
|
||||
boolean newStyleChannels) {
|
||||
this.thingUID = thingUID;
|
||||
this.scheduler = scheduler;
|
||||
this.updateListener = channelStateUpdateListener;
|
||||
this.gson = gson;
|
||||
this.tracker = tracker;
|
||||
this.transformationServiceProvider = transformationServiceProvider;
|
||||
this.newStyleChannels = newStyleChannels;
|
||||
}
|
||||
|
||||
@ -103,7 +100,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
if (config.length() > 0) {
|
||||
try {
|
||||
component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
|
||||
gson, transformationServiceProvider, newStyleChannels);
|
||||
gson, newStyleChannels);
|
||||
component.setConfigSeen();
|
||||
|
||||
logger.trace("Found HomeAssistant component {}", haID);
|
||||
|
@ -26,7 +26,6 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.values.Value;
|
||||
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
|
||||
@ -132,24 +131,27 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
||||
componentConfiguration.getTracker().setAvailabilityMode(availabilityTrackerMode);
|
||||
for (Availability availability : availabilities) {
|
||||
String availabilityTemplate = availability.getValueTemplate();
|
||||
List<String> availabilityTemplates = List.of();
|
||||
if (availabilityTemplate != null) {
|
||||
availabilityTemplate = JINJA_PREFIX + availabilityTemplate;
|
||||
availabilityTemplates = List.of(availabilityTemplate);
|
||||
}
|
||||
componentConfiguration.getTracker().addAvailabilityTopic(availability.getTopic(),
|
||||
availability.getPayloadAvailable(), availability.getPayloadNotAvailable(), availabilityTemplate,
|
||||
componentConfiguration.getTransformationServiceProvider());
|
||||
availability.getPayloadAvailable(), availability.getPayloadNotAvailable(),
|
||||
availabilityTemplates);
|
||||
}
|
||||
} else {
|
||||
String availabilityTopic = this.channelConfiguration.getAvailabilityTopic();
|
||||
if (availabilityTopic != null) {
|
||||
String availabilityTemplate = this.channelConfiguration.getAvailabilityTemplate();
|
||||
List<String> availabilityTemplates = List.of();
|
||||
if (availabilityTemplate != null) {
|
||||
availabilityTemplate = JINJA_PREFIX + availabilityTemplate;
|
||||
availabilityTemplates = List.of(availabilityTemplate);
|
||||
}
|
||||
componentConfiguration.getTracker().addAvailabilityTopic(availabilityTopic,
|
||||
this.channelConfiguration.getPayloadAvailable(),
|
||||
this.channelConfiguration.getPayloadNotAvailable(), availabilityTemplate,
|
||||
componentConfiguration.getTransformationServiceProvider());
|
||||
this.channelConfiguration.getPayloadNotAvailable(), availabilityTemplates);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -324,11 +326,6 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
||||
return channelConfigurationJson;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TransformationServiceProvider getTransformationServiceProvider() {
|
||||
return componentConfiguration.getTransformationServiceProvider();
|
||||
}
|
||||
|
||||
public boolean isEnabledByDefault() {
|
||||
return channelConfiguration.isEnabledByDefault();
|
||||
}
|
||||
|
@ -15,10 +15,8 @@ package org.openhab.binding.mqtt.homeassistant.internal.component;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||
@ -48,11 +46,9 @@ public class ComponentFactory {
|
||||
*/
|
||||
public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID, String channelConfigurationJSON,
|
||||
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker, ScheduledExecutorService scheduler,
|
||||
Gson gson, TransformationServiceProvider transformationServiceProvider, boolean newStyleChannels)
|
||||
throws ConfigurationException {
|
||||
Gson gson, boolean newStyleChannels) throws ConfigurationException {
|
||||
ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
|
||||
channelConfigurationJSON, gson, updateListener, tracker, scheduler)
|
||||
.transformationProvider(transformationServiceProvider);
|
||||
channelConfigurationJSON, gson, updateListener, tracker, scheduler);
|
||||
switch (haID.component) {
|
||||
case "alarm_control_panel":
|
||||
return new AlarmControlPanel(componentConfiguration, newStyleChannels);
|
||||
@ -101,7 +97,6 @@ public class ComponentFactory {
|
||||
private final AvailabilityTracker tracker;
|
||||
private final Gson gson;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private @Nullable TransformationServiceProvider transformationServiceProvider;
|
||||
|
||||
/**
|
||||
* Provide a thingUID and HomeAssistant topic ID to determine the channel group UID and type.
|
||||
@ -123,12 +118,6 @@ public class ComponentFactory {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
public ComponentConfiguration transformationProvider(
|
||||
TransformationServiceProvider transformationServiceProvider) {
|
||||
this.transformationServiceProvider = transformationServiceProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ThingUID getThingUID() {
|
||||
return thingUID;
|
||||
}
|
||||
@ -145,11 +134,6 @@ public class ComponentFactory {
|
||||
return updateListener;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TransformationServiceProvider getTransformationServiceProvider() {
|
||||
return transformationServiceProvider;
|
||||
}
|
||||
|
||||
public Gson getGson() {
|
||||
return gson;
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler;
|
||||
import org.openhab.binding.mqtt.generic.ChannelState;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing;
|
||||
import org.openhab.binding.mqtt.generic.utils.FutureCollector;
|
||||
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
|
||||
@ -101,8 +100,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
protected HandlerConfiguration config = new HandlerConfiguration();
|
||||
private Set<HaID> discoveryHomeAssistantIDs = new HashSet<>();
|
||||
|
||||
protected final TransformationServiceProvider transformationServiceProvider;
|
||||
|
||||
private boolean started;
|
||||
private boolean newStyleChannels;
|
||||
private @Nullable Update updateComponent;
|
||||
@ -118,21 +115,18 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
*/
|
||||
public HomeAssistantThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
|
||||
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
|
||||
TransformationServiceProvider transformationServiceProvider, int subscribeTimeout,
|
||||
int attributeReceiveTimeout) {
|
||||
int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
super(thing, subscribeTimeout);
|
||||
this.gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.channelTypeRegistry = channelTypeRegistry;
|
||||
this.transformationServiceProvider = transformationServiceProvider;
|
||||
this.attributeReceiveTimeout = attributeReceiveTimeout;
|
||||
this.delayedProcessing = new DelayedBatchProcessing<>(attributeReceiveTimeout, this, scheduler);
|
||||
|
||||
newStyleChannels = "true".equals(thing.getProperties().get("newStyleChannels"));
|
||||
|
||||
this.discoverComponents = new DiscoverComponents(thing.getUID(), scheduler, this, this, gson,
|
||||
this.transformationServiceProvider, newStyleChannels);
|
||||
this.discoverComponents = new DiscoverComponents(thing.getUID(), scheduler, this, this, gson, newStyleChannels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -162,7 +156,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
} else {
|
||||
try {
|
||||
component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
|
||||
scheduler, gson, transformationServiceProvider, newStyleChannels);
|
||||
scheduler, gson, newStyleChannels);
|
||||
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
||||
typeID = calculateThingTypeUID(component);
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.handler.BrokerHandler;
|
||||
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
@ -57,8 +57,12 @@ import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.thing.type.ThingType;
|
||||
import org.openhab.core.thing.type.ThingTypeBuilder;
|
||||
import org.openhab.core.thing.type.ThingTypeRegistry;
|
||||
import org.openhab.core.transform.TransformationHelper;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.openhab.transform.jinja.internal.JinjaTransformationService;
|
||||
import org.openhab.transform.jinja.internal.profiles.JinjaTransformationProfile;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
/**
|
||||
* Abstract class for HomeAssistant unit tests.
|
||||
@ -86,7 +90,6 @@ public abstract class AbstractHomeAssistantTests extends JavaTest {
|
||||
|
||||
protected @Mock @NonNullByDefault({}) MqttBrokerConnection bridgeConnection;
|
||||
protected @Mock @NonNullByDefault({}) ThingTypeRegistry thingTypeRegistry;
|
||||
protected @Mock @NonNullByDefault({}) TransformationServiceProvider transformationServiceProvider;
|
||||
|
||||
protected @NonNullByDefault({}) MqttChannelTypeProvider channelTypeProvider;
|
||||
protected @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
@ -97,16 +100,27 @@ public abstract class AbstractHomeAssistantTests extends JavaTest {
|
||||
protected final Thing haThing = ThingBuilder.create(HA_TYPE_UID, HA_UID).withBridge(BRIDGE_UID).build();
|
||||
protected final ConcurrentMap<String, Set<MqttMessageSubscriber>> subscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
private @Mock @NonNullByDefault({}) TransformationService transformationService1Mock;
|
||||
|
||||
private @Mock @NonNullByDefault({}) BundleContext bundleContextMock;
|
||||
private @Mock @NonNullByDefault({}) ServiceReference<TransformationService> serviceRefMock;
|
||||
|
||||
private @NonNullByDefault({}) TransformationHelper transformationHelper;
|
||||
|
||||
private final JinjaTransformationService jinjaTransformationService = new JinjaTransformationService();
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEachAbstractHomeAssistantTests() {
|
||||
Mockito.when(serviceRefMock.getProperty(any())).thenReturn(JinjaTransformationProfile.PROFILE_TYPE_UID.getId());
|
||||
|
||||
Mockito.when(bundleContextMock.getService(serviceRefMock)).thenReturn(jinjaTransformationService);
|
||||
|
||||
transformationHelper = new TransformationHelper(bundleContextMock);
|
||||
transformationHelper.setTransformationService(serviceRefMock);
|
||||
|
||||
when(thingTypeRegistry.getThingType(BRIDGE_TYPE_UID))
|
||||
.thenReturn(ThingTypeBuilder.instance(BRIDGE_TYPE_UID, BRIDGE_TYPE_LABEL).build());
|
||||
when(thingTypeRegistry.getThingType(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)).thenReturn(HA_THING_TYPE);
|
||||
when(transformationServiceProvider
|
||||
.getTransformationService(JinjaTransformationProfile.PROFILE_TYPE_UID.getId()))
|
||||
.thenReturn(jinjaTransformationService);
|
||||
|
||||
channelTypeProvider = spy(new MqttChannelTypeProvider(thingTypeRegistry, new VolatileStorageService()));
|
||||
stateDescriptionProvider = spy(new MqttChannelStateDescriptionProvider());
|
||||
|
@ -34,7 +34,6 @@ import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.generic.values.Value;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.AbstractHomeAssistantTests;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
|
||||
@ -79,7 +78,7 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
|
||||
when(callbackMock.getBridge(eq(BRIDGE_UID))).thenReturn(bridgeThing);
|
||||
|
||||
thingHandler = new LatchThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, transformationServiceProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
thingHandler = spy(thingHandler);
|
||||
@ -288,10 +287,9 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
|
||||
|
||||
public LatchThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
|
||||
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
|
||||
TransformationServiceProvider transformationServiceProvider, int subscribeTimeout,
|
||||
int attributeReceiveTimeout) {
|
||||
super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry,
|
||||
transformationServiceProvider, subscribeTimeout, attributeReceiveTimeout);
|
||||
int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry, subscribeTimeout,
|
||||
attributeReceiveTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,7 +75,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
|
||||
when(callbackMock.getBridge(eq(BRIDGE_UID))).thenReturn(bridgeThing);
|
||||
|
||||
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, transformationServiceProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
nonSpyThingHandler = thingHandler;
|
||||
|
@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
@ -26,8 +25,6 @@ import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.transform.TransformationHelper;
|
||||
import org.openhab.core.transform.TransformationService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
@ -40,7 +37,7 @@ import org.osgi.service.component.annotations.Reference;
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class)
|
||||
@NonNullByDefault
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider {
|
||||
public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final MqttChannelTypeProvider typeProvider;
|
||||
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
private final ChannelTypeRegistry channelTypeRegistry;
|
||||
@ -78,9 +75,4 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TransformationService getTransformationService(String type) {
|
||||
return TransformationHelper.getTransformationService(bundleContext, type);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
@ -60,7 +59,6 @@ public class DiscoverComponentsTest extends JavaOSGiTest {
|
||||
|
||||
private @Mock @NonNullByDefault({}) MqttBrokerConnection connection;
|
||||
private @Mock @NonNullByDefault({}) ComponentDiscovered discovered;
|
||||
private @Mock @NonNullByDefault({}) TransformationServiceProvider transformationServiceProvider;
|
||||
private @Mock @NonNullByDefault({}) ChannelStateUpdateListener channelStateUpdateListener;
|
||||
private @Mock @NonNullByDefault({}) AvailabilityTracker availabilityTracker;
|
||||
|
||||
@ -73,7 +71,6 @@ public class DiscoverComponentsTest extends JavaOSGiTest {
|
||||
doReturn(CompletableFuture.completedFuture(true)).when(connection).unsubscribe(any(), any());
|
||||
doReturn(CompletableFuture.completedFuture(true)).when(connection).publish(any(), any(), anyInt(),
|
||||
anyBoolean());
|
||||
doReturn(null).when(transformationServiceProvider).getTransformationService(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -84,7 +81,7 @@ public class DiscoverComponentsTest extends JavaOSGiTest {
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
|
||||
|
||||
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider, true));
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, true));
|
||||
|
||||
HandlerConfiguration config = new HandlerConfiguration("homeassistant", List.of("switch/object"));
|
||||
|
||||
|
@ -42,7 +42,6 @@ import org.mockito.quality.Strictness;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
@ -76,7 +75,6 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
||||
|
||||
private @Mock @NonNullByDefault({}) ChannelStateUpdateListener channelStateUpdateListener;
|
||||
private @Mock @NonNullByDefault({}) AvailabilityTracker availabilityTracker;
|
||||
private @Mock @NonNullByDefault({}) TransformationServiceProvider transformationServiceProvider;
|
||||
|
||||
/**
|
||||
* Create an observer that fails the test as soon as the broker client connection changes its connection state
|
||||
@ -110,8 +108,6 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(2, TimeUnit.SECONDS);
|
||||
|
||||
failure = null;
|
||||
|
||||
doReturn(null).when(transformationServiceProvider).getTransformationService(any());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,7 +146,7 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
||||
|
||||
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
|
||||
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider, true));
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, true));
|
||||
|
||||
// The DiscoverComponents object calls ComponentDiscovered callbacks.
|
||||
// In the following implementation we add the found component to the `haComponents` map
|
||||
|
Loading…
Reference in New Issue
Block a user