]> git.basschouten.com Git - openhab-addons.git/blob
288eee826c1eeae76699cc5328fe86da77e24faf
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.homematic.internal.converter.type;
14
15 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*;
16
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import org.openhab.binding.homematic.internal.converter.ConverterException;
23 import org.openhab.binding.homematic.internal.converter.ConverterTypeException;
24 import org.openhab.binding.homematic.internal.converter.StateInvertInfo;
25 import org.openhab.binding.homematic.internal.converter.TypeConverter;
26 import org.openhab.binding.homematic.internal.model.HmDatapoint;
27 import org.openhab.binding.homematic.internal.model.HmDatapointInfo;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.State;
30 import org.openhab.core.types.Type;
31 import org.openhab.core.types.UnDefType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * Base class for all Converters with common methods.
37  *
38  * @author Gerhard Riegler - Initial contribution
39  */
40 public abstract class AbstractTypeConverter<T extends State> implements TypeConverter<T> {
41     private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class);
42
43     /**
44      * Defines all devices where the state datapoint must be inverted.
45      */
46     private static final List<StateInvertInfo> STATE_INVERT_INFO_LIST = new ArrayList<>(3);
47
48     static {
49         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_SHUTTER_CONTACT));
50         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_SHUTTER_CONTACT_2));
51         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_INCLINATION_SENSOR));
52         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_WIRED_IO_MODULE, 15, 26));
53         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_MAX_WINDOW_SENSOR));
54         STATE_INVERT_INFO_LIST.add(new StateInvertInfo(DEVICE_TYPE_SHUTTER_CONTACT_INTERFACE));
55     }
56
57     /**
58      * Checks the datapoint if the state value must be inverted.
59      */
60     protected boolean isStateInvertDatapoint(HmDatapoint dp) {
61         if (DATAPOINT_NAME_STATE.equals(dp.getName())) {
62             for (StateInvertInfo stateInvertInfo : STATE_INVERT_INFO_LIST) {
63                 if (stateInvertInfo.isToInvert(dp)) {
64                     return true;
65                 }
66             }
67         }
68         return false;
69     }
70
71     /**
72      * Rounds a double value.
73      */
74     protected BigDecimal round(Double number) {
75         BigDecimal bd = new BigDecimal(number == null ? "0" : number.toString());
76         String stringBd = bd.toPlainString();
77         int scale = stringBd.length() - (stringBd.lastIndexOf('.') + 1);
78         return bd.setScale(scale > 2 ? 6 : 2, RoundingMode.HALF_UP);
79     }
80
81     @SuppressWarnings("unchecked")
82     @Override
83     public Object convertToBinding(Type type, HmDatapoint dp) throws ConverterException {
84         if (isLoggingRequired()) {
85             logAtDefaultLevel(
86                     "Converting type {} with value '{}' using {} to datapoint '{}' (dpType='{}', dpUnit='{}')",
87                     type.getClass().getSimpleName(), type.toString(), this.getClass().getSimpleName(),
88                     new HmDatapointInfo(dp), dp.getType(), dp.getUnit());
89         }
90
91         if (type == UnDefType.NULL) {
92             return null;
93         } else if (type.getClass().isEnum() && !(this instanceof OnOffTypeConverter)
94                 && !(this instanceof OpenClosedTypeConverter)) {
95             return commandToBinding((Command) type, dp);
96         } else if (!toBindingValidation(dp, type.getClass())) {
97             String errorMessage = String.format("Can't convert type %s with value '%s' to %s value with %s for '%s'",
98                     type.getClass().getSimpleName(), type.toString(), dp.getType(), this.getClass().getSimpleName(),
99                     new HmDatapointInfo(dp));
100             throw new ConverterTypeException(errorMessage);
101         }
102
103         return toBinding((T) type, dp);
104     }
105
106     @SuppressWarnings("unchecked")
107     @Override
108     public T convertFromBinding(HmDatapoint dp) throws ConverterException {
109         if (isLoggingRequired()) {
110             logAtDefaultLevel("Converting datapoint '{}' (dpType='{}', dpUnit='{}', dpValue='{}') with {}",
111                     new HmDatapointInfo(dp), dp.getType(), dp.getUnit(), dp.getValue(),
112                     this.getClass().getSimpleName());
113         }
114
115         if (dp.getValue() == null) {
116             return (T) UnDefType.NULL;
117         } else if (!fromBindingValidation(dp)) {
118             String errorMessage = String.format("Can't convert %s value '%s' with %s for '%s'", dp.getType(),
119                     dp.getValue(), this.getClass().getSimpleName(), new HmDatapointInfo(dp));
120             throw new ConverterTypeException(errorMessage);
121         }
122
123         return fromBinding(dp);
124     }
125
126     /**
127      * By default, instances of {@link AbstractTypeConverter} log in level TRACE.
128      * May be overridden to increase logging verbosity of a converter.
129      *
130      * @return desired LogLevel
131      */
132     protected LogLevel getDefaultLogLevelForTypeConverter() {
133         return LogLevel.TRACE;
134     }
135
136     private boolean isLoggingRequired() {
137         if (getDefaultLogLevelForTypeConverter() == LogLevel.TRACE) {
138             return logger.isTraceEnabled();
139         }
140         if (getDefaultLogLevelForTypeConverter() == LogLevel.DEBUG) {
141             return logger.isDebugEnabled();
142         }
143         return true;
144     }
145
146     private void logAtDefaultLevel(String format, Object... arguments) {
147         switch (getDefaultLogLevelForTypeConverter()) {
148             case TRACE:
149                 logger.trace(format, arguments);
150                 break;
151             case DEBUG:
152                 logger.debug(format, arguments);
153                 break;
154             case INFO:
155             default:
156                 logger.info(format, arguments);
157                 break;
158         }
159     }
160
161     /**
162      * Converts an openHAB command to a Homematic value.
163      */
164     protected Object commandToBinding(Command command, HmDatapoint dp) throws ConverterException {
165         throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for "
166                 + this.getClass().getSimpleName());
167     }
168
169     /**
170      * Returns true, if the conversion from openHAB to the binding is possible.
171      */
172     protected abstract boolean toBindingValidation(HmDatapoint dp, Class<? extends Type> typeClass);
173
174     /**
175      * Converts the type to a datapoint value.
176      */
177     protected abstract Object toBinding(T type, HmDatapoint dp) throws ConverterException;
178
179     /**
180      * Returns true, if the conversion from the binding to openHAB is possible.
181      */
182     protected abstract boolean fromBindingValidation(HmDatapoint dp);
183
184     /**
185      * Converts the datapoint value to an openHAB type.
186      */
187     protected abstract T fromBinding(HmDatapoint dp) throws ConverterException;
188
189     protected enum LogLevel {
190         TRACE,
191         INFO,
192         DEBUG
193     }
194 }