]> git.basschouten.com Git - openhab-addons.git/blob
50731305faa03004fc90e37ec80e9757ca33c752
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.homeassistant.internal;
14
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;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Optional;
23
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;
30
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;
35
36 /**
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
40  *
41  * @author Cody Cutrer - Initial contribution
42  */
43 @NonNullByDefault
44 public class HomeAssistantChannelTransformation extends ChannelTransformation {
45     private final Logger logger = LoggerFactory.getLogger(HomeAssistantChannelTransformation.class);
46
47     private final Jinjava jinjava;
48     private final AbstractComponent component;
49     private final String template;
50     private final ObjectMapper objectMapper = new ObjectMapper();
51
52     public HomeAssistantChannelTransformation(Jinjava jinjava, AbstractComponent component, String template) {
53         super((String) null);
54         this.jinjava = jinjava;
55         this.component = component;
56         this.template = template;
57     }
58
59     @Override
60     public boolean isEmpty() {
61         return template.isEmpty();
62     }
63
64     @Override
65     public Optional<String> apply(String value) {
66         return apply(template, value);
67     }
68
69     public Optional<String> apply(String template, String value) {
70         Map<String, @Nullable Object> bindings = new HashMap<>();
71
72         logger.debug("about to transform '{}' by the function '{}'", value, template);
73
74         bindings.put("value", value);
75
76         try {
77             JsonNode tree = objectMapper.readTree(value);
78             bindings.put("value_json", toObject(tree));
79         } catch (IOException e) {
80             // ok, then value_json is null...
81         }
82
83         return apply(template, bindings);
84     }
85
86     public Optional<String> apply(String template, Map<String, @Nullable Object> bindings) {
87         String transformationResult;
88
89         try {
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();
95         }
96
97         logger.debug("transformation resulted in '{}'", transformationResult);
98
99         return Optional.of(transformationResult);
100     }
101
102     private static @Nullable Object toObject(JsonNode node) {
103         switch (node.getNodeType()) {
104             case ARRAY: {
105                 List<@Nullable Object> result = new ArrayList<>();
106                 for (JsonNode el : node) {
107                     result.add(toObject(el));
108                 }
109                 return result;
110             }
111             case NUMBER:
112                 return node.decimalValue();
113             case OBJECT: {
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()));
119                 }
120                 return result;
121             }
122             case STRING:
123                 return node.asText();
124             case BOOLEAN:
125                 return node.asBoolean();
126             case NULL:
127             default:
128                 return null;
129         }
130     }
131 }