]> git.basschouten.com Git - openhab-addons.git/commitdiff
[mqtt.homeassistant] Use Jinjava directly (#17378)
authorCody Cutrer <cody@cutrer.us>
Mon, 9 Sep 2024 12:54:08 +0000 (06:54 -0600)
committerGitHub <noreply@github.com>
Mon, 9 Sep 2024 12:54:08 +0000 (14:54 +0200)
* [mqtt.homeassistant] Use Jinjava directly

Signed-off-by: Cody Cutrer <cody@cutrer.us>
20 files changed:
bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java
bundles/org.openhab.binding.mqtt.homeassistant/README.md
bundles/org.openhab.binding.mqtt.homeassistant/noEmbedDependencies.profile [new file with mode: 0644]
bundles/org.openhab.binding.mqtt.homeassistant/pom.xml
bundles/org.openhab.binding.mqtt.homeassistant/src/main/feature/feature.xml
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentChannel.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HomeAssistantChannelState.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HomeAssistantChannelTransformation.java [new file with mode: 0644]
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponent.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java
bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/AbstractHomeAssistantTests.java
bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponentTests.java
bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandlerTests.java
features/openhab-addons/src/main/resources/footer.xml
itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun
itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/DiscoverComponentsTest.java
itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/HomeAssistantMQTTImplementationTest.java

index e1d5da2ef590ebe4302b6c5ecc8c561dd1ddfd85..2ba8ad2aa1b19afddde068530cfbac689da087f5 100644 (file)
@@ -78,13 +78,35 @@ public class ChannelState implements MqttMessageSubscriber {
      */
     public ChannelState(ChannelConfig config, ChannelUID channelUID, Value cachedValue,
             @Nullable ChannelStateUpdateListener channelStateUpdateListener) {
+        this(config, channelUID, cachedValue, channelStateUpdateListener,
+                new ChannelTransformation(config.transformationPattern),
+                new ChannelTransformation(config.transformationPatternOut));
+    }
+
+    /**
+     * Creates a new channel state.
+     *
+     * @param config The channel configuration
+     * @param channelUID The channelUID is used for the {@link ChannelStateUpdateListener} to notify about value changes
+     * @param cachedValue MQTT only notifies us once about a value, during the subscribe. The channel state therefore
+     *            needs a cache for the current value.
+     * @param channelStateUpdateListener A channel state update listener
+     * @param incomingTransformation A transformation to apply to incoming values
+     * @param outgoingTransformation A transformation to apply to outgoing values
+     */
+    public ChannelState(ChannelConfig config, ChannelUID channelUID, Value cachedValue,
+            @Nullable ChannelStateUpdateListener channelStateUpdateListener,
+            @Nullable ChannelTransformation incomingTransformation,
+            @Nullable ChannelTransformation outgoingTransformation) {
         this.config = config;
         this.channelStateUpdateListener = channelStateUpdateListener;
         this.channelUID = channelUID;
         this.cachedValue = cachedValue;
         this.readOnly = config.commandTopic.isBlank();
-        this.incomingTransformation = new ChannelTransformation(config.transformationPattern);
-        this.outgoingTransformation = new ChannelTransformation(config.transformationPatternOut);
+        this.incomingTransformation = incomingTransformation == null ? new ChannelTransformation((String) null)
+                : incomingTransformation;
+        this.outgoingTransformation = outgoingTransformation == null ? new ChannelTransformation((String) null)
+                : outgoingTransformation;
     }
 
     public boolean isReadOnly() {
index 93205168af9aaaed488b8c7ffb2198d0f7f906f2..f65261c18bf626d2a62953bec3a798552761ef27 100644 (file)
@@ -6,15 +6,6 @@ Devices that use [Home Assistant MQTT Discovery](https://www.home-assistant.io/i
 Components that share a common `device.identifiers` will automatically be grouped together as a single Thing.
 Each component will be represented as a Channel Group, with the attributes of that component being individual channels.
 
-## Requirements
-
-The Home Assistant MQTT binding requires two transformations to be installed:
-
-- JINJA-Transformations
-- JSONPath-Transformations
-
-These can be installed under `Settings` &rarr; `Addon` &rarr; `Transformations`
-
 ## Discovery
 
 Any device that publishes the component configuration under the `homeassistant` prefix in MQTT will have their components automatically discovered and added to the Inbox.
diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/noEmbedDependencies.profile b/bundles/org.openhab.binding.mqtt.homeassistant/noEmbedDependencies.profile
new file mode 100644 (file)
index 0000000..e69de29
index 79773a7dc24a0fa88f9f4ba551c807468c33d9bf..854983ad21f14630877811217fdc551c2c83cfc0 100644 (file)
     </dependency>
 
     <dependency>
-      <groupId>org.openhab.addons.bundles</groupId>
-      <artifactId>org.openhab.transform.jinja</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>com.hubspot.jinjava.jinjava</artifactId>
+      <version>2.7.2_0</version>
+      <scope>compile</scope>
     </dependency>
     <dependency>
-      <groupId>com.hubspot.jinjava</groupId>
-      <artifactId>jinjava</artifactId>
-      <version>2.7.2</version>
-      <scope>test</scope>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>com.google.re2j.re2j</artifactId>
+      <version>1.2</version>
+      <scope>compile</scope>
     </dependency>
     <dependency>
-      <groupId>com.google.re2j</groupId>
-      <artifactId>re2j</artifactId>
-      <version>1.2</version>
+      <groupId>ch.obermuhlner</groupId>
+      <artifactId>big-math</artifactId>
+      <version>2.3.2</version>
+      <scope>compile</scope>
     </dependency>
   </dependencies>
 </project>
index fd50c3169fcff24d89ed060a8aeeb970b3097db9..853e170e1cfd7dc142971a69ceeaa9aa1361d1ea 100644 (file)
@@ -5,6 +5,10 @@
        <feature name="openhab-binding-mqtt-homeassistant" description="MQTT Binding Homeassistant" version="${project.version}">
                <feature>openhab-runtime-base</feature>
                <feature>openhab-transport-mqtt</feature>
+               <feature dependency="true">openhab.tp-commons-net</feature>
+               <bundle dependency="true">mvn:org.openhab.osgiify/com.hubspot.jinjava.jinjava/2.7.2_0</bundle>
+               <bundle dependency="true">mvn:org.openhab.osgiify/com.google.re2j.re2j/1.2</bundle>
+               <bundle dependency="true">mvn:ch.obermuhlner/big-math/2.3.2</bundle>
                <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
                <bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version}</bundle>
                <bundle start-level="82">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homeassistant/${project.version}</bundle>
index b228b1b0e3b9b071799d7d49ea914a0b7661d93a..6c5ebdd8886534295db9bcd4740d86fa49da90e7 100644 (file)
@@ -31,6 +31,8 @@ import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 
+import com.hubspot.jinjava.Jinjava;
+
 /**
  * The {@link MqttThingHandlerFactory} is responsible for creating things and thing
  * handlers.
@@ -43,6 +45,7 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
     private final MqttChannelTypeProvider typeProvider;
     private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
     private final ChannelTypeRegistry channelTypeRegistry;
+    private final Jinjava jinjava = new Jinjava();
 
     private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
             .of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet());
@@ -72,7 +75,7 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
 
         if (supportsThingType(thingTypeUID)) {
             return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
-                    10000, 2000);
+                    jinjava, 10000, 2000);
         }
         return null;
     }
index c8bb949f42bfedb5826c35d3e6a7b9facfa7a8bd..df7b926870b8bebe04ea9532ebd9bdd221b9e642 100644 (file)
@@ -12,7 +12,6 @@
  */
 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;
@@ -30,6 +29,7 @@ import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.generic.ChannelTransformation;
 import org.openhab.core.thing.type.AutoUpdatePolicy;
 import org.openhab.core.thing.type.ChannelDefinition;
 import org.openhab.core.thing.type.ChannelDefinitionBuilder;
@@ -223,21 +223,26 @@ public class ComponentChannel {
             ChannelUID channelUID;
             ChannelState channelState;
             Channel channel;
+            ChannelTransformation incomingTransformation = null, outgoingTransformation = null;
 
             channelUID = component.buildChannelUID(channelID);
             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));
+            String localTemplateIn = templateIn;
+            if (localTemplateIn != null) {
+                incomingTransformation = new HomeAssistantChannelTransformation(component.getJinjava(), component,
+                        localTemplateIn);
             }
-            if (templateOut != null) {
-                channelConfigBuilder.withTransformationPatternOut(List.of(JINJA + ":" + templateOut));
+            String localTemplateOut = templateOut;
+            if (localTemplateOut != null) {
+                outgoingTransformation = new HomeAssistantChannelTransformation(component.getJinjava(), component,
+                        localTemplateOut);
             }
 
             channelState = new HomeAssistantChannelState(channelConfigBuilder.build(), channelUID, valueState,
-                    channelStateUpdateListener, commandFilter);
+                    channelStateUpdateListener, commandFilter, incomingTransformation, outgoingTransformation);
 
             // disabled by default components should always show up as advanced
             if (!component.isEnabledByDefault()) {
index 7f23a13aed2755746aebf02bb74aa727525d177d..810d99d988d78f2846ff31a21c010edd1297dcff 100644 (file)
@@ -37,6 +37,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * Responsible for subscribing to the HomeAssistant MQTT components wildcard topic, either
@@ -55,6 +56,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
 
     protected final CompletableFuture<@Nullable Void> discoverFinishedFuture = new CompletableFuture<>();
     private final Gson gson;
+    private final Jinjava jinjava;
 
     private @Nullable ScheduledFuture<?> stopDiscoveryFuture;
     private WeakReference<@Nullable MqttBrokerConnection> connectionRef = new WeakReference<>(null);
@@ -78,11 +80,12 @@ public class DiscoverComponents implements MqttMessageSubscriber {
      */
     public DiscoverComponents(ThingUID thingUID, ScheduledExecutorService scheduler,
             ChannelStateUpdateListener channelStateUpdateListener, AvailabilityTracker tracker, Gson gson,
-            boolean newStyleChannels) {
+            Jinjava jinjava, boolean newStyleChannels) {
         this.thingUID = thingUID;
         this.scheduler = scheduler;
         this.updateListener = channelStateUpdateListener;
         this.gson = gson;
+        this.jinjava = jinjava;
         this.tracker = tracker;
         this.newStyleChannels = newStyleChannels;
     }
@@ -100,7 +103,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
         if (config.length() > 0) {
             try {
                 component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
-                        gson, newStyleChannels);
+                        gson, jinjava, newStyleChannels);
                 component.setConfigSeen();
 
                 logger.trace("Found HomeAssistant component {}", haID);
index 16fb5303dacc2272ade4af4b9c8656ed0cdb2686..84afd134883cd57ae19cc4872dd38348a7eed842 100644 (file)
@@ -22,6 +22,7 @@ import org.openhab.binding.mqtt.generic.ChannelState;
 import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
 import org.openhab.binding.mqtt.generic.values.Value;
 import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.binding.generic.ChannelTransformation;
 import org.openhab.core.types.Command;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,9 +49,11 @@ public class HomeAssistantChannelState extends ChannelState {
      *            <code>false</code> ignored. Can be <code>null</code> to publish all commands.
      */
     public HomeAssistantChannelState(ChannelConfig config, ChannelUID channelUID, Value cachedValue,
-            @Nullable ChannelStateUpdateListener channelStateUpdateListener,
-            @Nullable Predicate<Command> commandFilter) {
-        super(config, channelUID, cachedValue, channelStateUpdateListener);
+            @Nullable ChannelStateUpdateListener channelStateUpdateListener, @Nullable Predicate<Command> commandFilter,
+            @Nullable ChannelTransformation incomingTransformation,
+            @Nullable ChannelTransformation outgoingTransformation) {
+        super(config, channelUID, cachedValue, channelStateUpdateListener, incomingTransformation,
+                outgoingTransformation);
         this.commandFilter = commandFilter;
     }
 
diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HomeAssistantChannelTransformation.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HomeAssistantChannelTransformation.java
new file mode 100644 (file)
index 0000000..3a51c77
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * 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.homeassistant.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
+import org.openhab.core.thing.binding.generic.ChannelTransformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.hubspot.jinjava.Jinjava;
+import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
+
+/**
+ * Provides a channel transformation for a Home Assistant channel with a
+ * Jinja2 template, providing the additional context and extensions required by Home Assistant
+ * Based in part on the JinjaTransformationService
+ *
+ * @author Cody Cutrer - Initial contribution
+ */
+@NonNullByDefault
+public class HomeAssistantChannelTransformation extends ChannelTransformation {
+    private final Logger logger = LoggerFactory.getLogger(HomeAssistantChannelTransformation.class);
+
+    private final Jinjava jinjava;
+    private final AbstractComponent component;
+    private final String template;
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    public HomeAssistantChannelTransformation(Jinjava jinjava, AbstractComponent component, String template) {
+        super((String) null);
+        this.jinjava = jinjava;
+        this.component = component;
+        this.template = template;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return template.isEmpty();
+    }
+
+    @Override
+    public Optional<String> apply(String value) {
+        String transformationResult;
+        Map<String, @Nullable Object> bindings = new HashMap<>();
+
+        logger.debug("about to transform '{}' by the function '{}'", value, template);
+
+        bindings.put("value", value);
+
+        try {
+            JsonNode tree = objectMapper.readTree(value);
+            bindings.put("value_json", toObject(tree));
+        } catch (IOException e) {
+            // ok, then value_json is null...
+        }
+
+        try {
+            transformationResult = jinjava.render(template, bindings);
+        } catch (FatalTemplateErrorsException e) {
+            logger.warn("Applying template {} for component {} failed: {}", template,
+                    component.getHaID().toShortTopic(), e.getMessage());
+            return Optional.empty();
+        }
+
+        logger.debug("transformation resulted in '{}'", transformationResult);
+
+        return Optional.of(transformationResult);
+    }
+
+    private static @Nullable Object toObject(JsonNode node) {
+        switch (node.getNodeType()) {
+            case ARRAY: {
+                List<@Nullable Object> result = new ArrayList<>();
+                for (JsonNode el : node) {
+                    result.add(toObject(el));
+                }
+                return result;
+            }
+            case NUMBER:
+                return node.decimalValue();
+            case OBJECT: {
+                Map<String, @Nullable Object> result = new HashMap<>();
+                Iterator<Entry<String, JsonNode>> it = node.fields();
+                while (it.hasNext()) {
+                    Entry<String, JsonNode> field = it.next();
+                    result.put(field.getKey(), toObject(field.getValue()));
+                }
+                return result;
+            }
+            case STRING:
+                return node.asText();
+            case BOOLEAN:
+                return node.asBoolean();
+            case NULL:
+            default:
+                return null;
+        }
+    }
+}
index ccff4d3db0ca7a1cb73549bc846431f1ef57feb3..131cc81d65cf833019f698f8d30afeae31a678f5 100644 (file)
@@ -49,6 +49,7 @@ import org.openhab.core.types.CommandDescription;
 import org.openhab.core.types.StateDescription;
 
 import com.google.gson.Gson;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * A HomeAssistant component is comparable to a channel group.
@@ -334,6 +335,10 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
         return componentConfiguration.getGson();
     }
 
+    public Jinjava getJinjava() {
+        return componentConfiguration.getJinjava();
+    }
+
     public C getChannelConfiguration() {
         return channelConfiguration;
     }
index 1acc4ac18bd4ce9820c91732a89fbd4e6cbda96e..36e9bbd66bdf165217f0896db26120b97a45894d 100644 (file)
@@ -24,6 +24,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComp
 import org.openhab.core.thing.ThingUID;
 
 import com.google.gson.Gson;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * A factory to create HomeAssistant MQTT components. Those components are specified at:
@@ -46,9 +47,9 @@ public class ComponentFactory {
      */
     public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID, String channelConfigurationJSON,
             ChannelStateUpdateListener updateListener, AvailabilityTracker tracker, ScheduledExecutorService scheduler,
-            Gson gson, boolean newStyleChannels) throws ConfigurationException {
+            Gson gson, Jinjava jinjava, boolean newStyleChannels) throws ConfigurationException {
         ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
-                channelConfigurationJSON, gson, updateListener, tracker, scheduler);
+                channelConfigurationJSON, gson, jinjava, updateListener, tracker, scheduler);
         switch (haID.component) {
             case "alarm_control_panel":
                 return new AlarmControlPanel(componentConfiguration, newStyleChannels);
@@ -96,6 +97,7 @@ public class ComponentFactory {
         private final ChannelStateUpdateListener updateListener;
         private final AvailabilityTracker tracker;
         private final Gson gson;
+        private final Jinjava jinjava;
         private final ScheduledExecutorService scheduler;
 
         /**
@@ -106,13 +108,14 @@ public class ComponentFactory {
          * @param configJSON The configuration string
          * @param gson A Gson instance
          */
-        protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson,
+        protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson, Jinjava jinjava,
                 ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
                 ScheduledExecutorService scheduler) {
             this.thingUID = thingUID;
             this.haID = haID;
             this.configJSON = configJSON;
             this.gson = gson;
+            this.jinjava = jinjava;
             this.updateListener = updateListener;
             this.tracker = tracker;
             this.scheduler = scheduler;
@@ -138,6 +141,10 @@ public class ComponentFactory {
             return gson;
         }
 
+        public Jinjava getJinjava() {
+            return jinjava;
+        }
+
         public AvailabilityTracker getTracker() {
             return tracker;
         }
index fb9e68e862fa691dd460f949de3aaf11e23c44c7..8c19ce7efe3c158a2d10e9a7303b5c0cd65926db 100644 (file)
@@ -59,6 +59,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * Handles HomeAssistant MQTT object things. Such an HA Object can have multiple HA Components with different instances
@@ -90,6 +91,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
     protected final MqttChannelTypeProvider channelTypeProvider;
     protected final MqttChannelStateDescriptionProvider stateDescriptionProvider;
     protected final ChannelTypeRegistry channelTypeRegistry;
+    protected final Jinjava jinjava;
     public final int attributeReceiveTimeout;
     protected final DelayedBatchProcessing<AbstractComponent<?>> delayedProcessing;
     protected final DiscoverComponents discoverComponents;
@@ -115,18 +117,20 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
      */
     public HomeAssistantThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
             MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
-            int subscribeTimeout, int attributeReceiveTimeout) {
+            Jinjava jinjava, 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.jinjava = jinjava;
         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, newStyleChannels);
+        this.discoverComponents = new DiscoverComponents(thing.getUID(), scheduler, this, this, gson, jinjava,
+                newStyleChannels);
     }
 
     @Override
@@ -156,7 +160,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
             } else {
                 try {
                     component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
-                            scheduler, gson, newStyleChannels);
+                            scheduler, gson, jinjava, newStyleChannels);
                     if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
                         typeID = calculateThingTypeUID(component);
                     }
index 3e3de4afbffd3369b21f5720e328721261b24c5d..4ef9d6e2668794ef6f1775060967dce26b7bfd3c 100644 (file)
@@ -32,7 +32,6 @@ 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;
@@ -59,8 +58,6 @@ 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;
 
@@ -107,14 +104,8 @@ public abstract class AbstractHomeAssistantTests extends JavaTest {
 
     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);
 
index bc63d783ea1e664da5a67257b6816de23c0932ee..dc7d2b1ef1db9df8e479f58662365fd7cc59f215 100644 (file)
@@ -49,6 +49,8 @@ import org.openhab.core.thing.type.ChannelTypeRegistry;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
 
+import com.hubspot.jinjava.Jinjava;
+
 /**
  * Abstract class for components tests.
  *
@@ -288,8 +290,8 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
         public LatchThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
                 MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
                 int subscribeTimeout, int attributeReceiveTimeout) {
-            super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry, subscribeTimeout,
-                    attributeReceiveTimeout);
+            super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry, new Jinjava(),
+                    subscribeTimeout, attributeReceiveTimeout);
         }
 
         @Override
index acf5990420ccc1b8c4fb46254cc4a8188ae74b7b..ab469881547f927aea6003d1b39f809a87b6987f 100644 (file)
@@ -39,6 +39,8 @@ import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.binding.ThingHandlerCallback;
 import org.openhab.core.types.StateDescription;
 
+import com.hubspot.jinjava.Jinjava;
+
 /**
  * Tests for {@link HomeAssistantThingHandler}
  *
@@ -75,7 +77,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
         when(callbackMock.getBridge(eq(BRIDGE_UID))).thenReturn(bridgeThing);
 
         thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
-                channelTypeRegistry, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
+                channelTypeRegistry, new Jinjava(), SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
         thingHandler.setConnection(bridgeConnection);
         thingHandler.setCallback(callbackMock);
         nonSpyThingHandler = thingHandler;
index df4215793661e9f8fb1f49e1574fa89980ba286a..383959548d486512cba3319c20b0e281a0f984f0 100644 (file)
        <feature name="openhab-binding-mqtt" description="MQTT Binding" version="${project.version}">
                <feature>openhab-runtime-base</feature>
                <feature>openhab-transport-mqtt</feature>
+               <feature dependency="true">openhab.tp-commons-net</feature>
+               <bundle dependency="true">mvn:org.openhab.osgiify/com.hubspot.jinjava.jinjava/2.7.2_0</bundle>
+               <bundle dependency="true">mvn:org.openhab.osgiify/com.google.re2j.re2j/1.2</bundle>
+               <bundle dependency="true">mvn:ch.obermuhlner/big-math/2.3.2</bundle>
                <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
                <bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}</bundle>
                <bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version}</bundle>
index 226f765ca2603e3322d4a5dd42c0fec648326b95..b6d41e1a2e85b2d186eeaa1e78ee28eee17f17b8 100644 (file)
@@ -116,4 +116,18 @@ Import-Package: \
        org.openhab.core.io.transport.mqtt;version='[4.3.0,4.3.1)',\
        org.openhab.core.test;version='[4.3.0,4.3.1)',\
        org.openhab.core.thing;version='[4.3.0,4.3.1)',\
-       org.openhab.core.transform;version='[4.3.0,4.3.1)'
+       org.openhab.core.transform;version='[4.3.0,4.3.1)',\
+       ch.obermuhlner.math.big;version='[2.3.2,2.3.3)',\
+       com.fasterxml.jackson.core.jackson-annotations;version='[2.17.1,2.17.2)',\
+       com.fasterxml.jackson.core.jackson-core;version='[2.17.1,2.17.2)',\
+       com.fasterxml.jackson.core.jackson-databind;version='[2.17.1,2.17.2)',\
+       com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.17.1,2.17.2)',\
+       com.google.guava;version='[33.2.0,33.2.1)',\
+       com.google.guava.failureaccess;version='[1.0.2,1.0.3)',\
+       com.google.re2j.re2j;version='[1.2.0,1.2.1)',\
+       com.hubspot.jinjava.jinjava;version='[2.7.2,2.7.3)',\
+       javassist;version='[3.29.2,3.29.3)',\
+       org.apache.commons.commons-net;version='[3.9.0,3.9.1)',\
+       org.apache.commons.lang3;version='[3.14.0,3.14.1)',\
+       org.osgi.service.cm;version='[1.6.0,1.6.1)',\
+       org.yaml.snakeyaml;version='[2.2.0,2.2.1)'
index 5ab82f8347bf01c0fbbef08f9826c7a9ffe91837..903c94a00d7a62894deaafd891b19ce58836c1b4 100644 (file)
@@ -46,6 +46,7 @@ import org.openhab.core.test.java.JavaOSGiTest;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * Tests the {@link DiscoverComponents} class.
@@ -79,9 +80,10 @@ public class DiscoverComponentsTest extends JavaOSGiTest {
         ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
 
         Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
+        Jinjava jinjava = new Jinjava();
 
         DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
-                scheduler, channelStateUpdateListener, availabilityTracker, gson, true));
+                scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, true));
 
         HandlerConfiguration config = new HandlerConfiguration("homeassistant", List.of("switch/object"));
 
index 04a732c9296f2860aa5f69991da70feb25a9f2bd..2f273661d3be1f8f1012179fd50c6d4a021203df 100644 (file)
@@ -57,6 +57,7 @@ import org.openhab.core.types.UnDefType;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.hubspot.jinjava.Jinjava;
 
 /**
  * A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device
@@ -143,10 +144,11 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
 
         final Map<String, AbstractComponent<?>> haComponents = new HashMap<>();
         Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
+        Jinjava jinjava = new Jinjava();
 
         ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
         DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
-                scheduler, channelStateUpdateListener, availabilityTracker, gson, true));
+                scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, true));
 
         // The DiscoverComponents object calls ComponentDiscovered callbacks.
         // In the following implementation we add the found component to the `haComponents` map