]> git.basschouten.com Git - openhab-addons.git/blob
9c0050f60a4689bff385e3b769b69c0591a46715
[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.modbus.internal;
14
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.Optional;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.core.library.types.DecimalType;
22 import org.openhab.core.library.types.OnOffType;
23 import org.openhab.core.library.types.OpenClosedType;
24 import org.openhab.core.thing.binding.generic.ChannelTransformation;
25 import org.openhab.core.types.Command;
26 import org.openhab.core.types.State;
27 import org.openhab.core.types.TypeParser;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * 
33  * Class for performing transformations of a command or state.
34  *
35  * @author Jimmy Tanagra - Initial contribution
36  *
37  */
38 @NonNullByDefault
39 public class ModbusTransformation {
40
41     public static final String TRANSFORM_DEFAULT = "default";
42
43     /**
44      * Ordered list of types that are tried out first when trying to parse transformed command
45      */
46     private static final List<Class<? extends Command>> DEFAULT_TYPES = List.of( //
47             DecimalType.class, //
48             OpenClosedType.class, //
49             OnOffType.class //
50     );
51
52     private final Logger logger = LoggerFactory.getLogger(ModbusTransformation.class);
53     private final @Nullable ChannelTransformation transformation;
54     private final @Nullable String constantOutput;
55
56     /**
57      * Creates a transformation object.
58      * 
59      * The transformations are chained and applied in the order they are given in the list.
60      * Each transformation can also contain the intersection symbol "∩" to separate
61      * multiple transformations in one line.
62      * 
63      * - If the transformationList is null or consists of only blank strings,
64      * the output of the transformation will be an empty string regardless of the input.
65      * 
66      * - If first element is "default", the transformation will be considered as
67      * an identity transformation, which returns the input as the output.
68      * Additional elements in the list are ignored.
69      * 
70      * - If the transformationList contains valid transformation syntax, the output
71      * will be transformed according to the given transformations.
72      * 
73      * - If the first element is some other value, it is treated as a constant and it
74      * will become the output of the transformation, regardless of the input.
75      * Additional elements in the list are ignored.
76      * 
77      * @param transformations a list of transformations to apply.
78      */
79     public ModbusTransformation(@Nullable List<String> transformationList) {
80         if (transformationList == null || transformationList.isEmpty()
81                 || transformationList.stream().allMatch(String::isBlank)) {
82             transformation = null;
83             constantOutput = "";
84             return;
85         }
86
87         int size = transformationList.size();
88         String firstLine = transformationList.get(0).trim();
89
90         if (size == 1 && firstLine.equalsIgnoreCase(TRANSFORM_DEFAULT)) {
91             // no-op (identity) transformation
92             transformation = null;
93             constantOutput = null;
94             return;
95         }
96
97         if (transformationList.stream().allMatch(ChannelTransformation::isValidTransformation)) {
98             transformation = new ChannelTransformation(transformationList);
99             constantOutput = null;
100         } else {
101             transformation = null;
102             constantOutput = firstLine;
103             if (size > 1) {
104                 logger.warn(
105                         "Given transformation configuration {} did not match the correct pattern. Transformation output will be constant '{}'",
106                         transformationList, constantOutput);
107             } else {
108                 logger.debug("The output for transformation {} will be constant '{}'", transformationList,
109                         constantOutput);
110             }
111         }
112     }
113
114     public String transform(String value) {
115         if (transformation != null) {
116             // return input if transformation failed
117             return Objects.requireNonNull(transformation.apply(value).orElse(value));
118         }
119
120         return Objects.requireNonNullElse(constantOutput, value);
121     }
122
123     public boolean isIdentityTransform() {
124         return transformation == null && constantOutput == null;
125     }
126
127     public static Optional<Command> tryConvertToCommand(String transformed) {
128         return Optional.ofNullable(TypeParser.parseCommand(DEFAULT_TYPES, transformed));
129     }
130
131     /**
132      * Transform state to another state using this transformation
133      *
134      * @param types types to used to parse the transformation result
135      * @param state
136      * @return Transformed command, or null if no transformation was possible
137      */
138     public @Nullable State transformState(List<Class<? extends State>> types, State state) {
139         // Note that even identity transformations go through the State -> String -> State steps. This does add some
140         // overhead but takes care of DecimalType -> PercentType conversions, for example.
141         final String stateAsString = state.toString();
142         final String transformed = transform(stateAsString);
143         return TypeParser.parseState(types, transformed);
144     }
145 }