2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.homeassistant.internal;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Map.Entry;
22 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
27 import org.openhab.core.thing.binding.generic.ChannelTransformation;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 import com.fasterxml.jackson.databind.JsonNode;
32 import com.fasterxml.jackson.databind.ObjectMapper;
33 import com.hubspot.jinjava.Jinjava;
34 import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
37 * Provides a channel transformation for a Home Assistant channel with a
38 * Jinja2 template, providing the additional context and extensions required by Home Assistant
39 * Based in part on the JinjaTransformationService
41 * @author Cody Cutrer - Initial contribution
44 public class HomeAssistantChannelTransformation extends ChannelTransformation {
45 private final Logger logger = LoggerFactory.getLogger(HomeAssistantChannelTransformation.class);
47 private final Jinjava jinjava;
48 private final AbstractComponent component;
49 private final String template;
50 private final ObjectMapper objectMapper = new ObjectMapper();
52 public HomeAssistantChannelTransformation(Jinjava jinjava, AbstractComponent component, String template) {
54 this.jinjava = jinjava;
55 this.component = component;
56 this.template = template;
60 public boolean isEmpty() {
61 return template.isEmpty();
65 public Optional<String> apply(String value) {
66 return apply(template, value);
69 public Optional<String> apply(String template, String value) {
70 Map<String, @Nullable Object> bindings = new HashMap<>();
72 logger.debug("about to transform '{}' by the function '{}'", value, template);
74 bindings.put("value", value);
77 JsonNode tree = objectMapper.readTree(value);
78 bindings.put("value_json", toObject(tree));
79 } catch (IOException e) {
80 // ok, then value_json is null...
83 return apply(template, bindings);
86 public Optional<String> apply(String template, Map<String, @Nullable Object> bindings) {
87 String transformationResult;
90 transformationResult = jinjava.render(template, bindings);
91 } catch (FatalTemplateErrorsException e) {
92 logger.warn("Applying template {} for component {} failed: {}", template,
93 component.getHaID().toShortTopic(), e.getMessage());
94 return Optional.empty();
97 logger.debug("transformation resulted in '{}'", transformationResult);
99 return Optional.of(transformationResult);
102 private static @Nullable Object toObject(JsonNode node) {
103 switch (node.getNodeType()) {
105 List<@Nullable Object> result = new ArrayList<>();
106 for (JsonNode el : node) {
107 result.add(toObject(el));
112 return node.decimalValue();
114 Map<String, @Nullable Object> result = new HashMap<>();
115 Iterator<Entry<String, JsonNode>> it = node.fields();
116 while (it.hasNext()) {
117 Entry<String, JsonNode> field = it.next();
118 result.put(field.getKey(), toObject(field.getValue()));
123 return node.asText();
125 return node.asBoolean();