]> git.basschouten.com Git - openhab-addons.git/blob
eaca9697b6bc0a7f1073bd890aeb7e5c3079c350
[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.persistence.mongodb.internal;
14
15 import java.time.ZoneId;
16 import java.time.ZonedDateTime;
17 import java.util.Date;
18 import java.util.Map;
19 import java.util.function.BiFunction;
20 import java.util.function.Function;
21
22 import javax.measure.Unit;
23
24 import org.bson.Document;
25 import org.bson.types.Binary;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.core.items.GenericItem;
29 import org.openhab.core.items.GroupItem;
30 import org.openhab.core.items.Item;
31 import org.openhab.core.library.items.CallItem;
32 import org.openhab.core.library.items.ColorItem;
33 import org.openhab.core.library.items.ContactItem;
34 import org.openhab.core.library.items.DateTimeItem;
35 import org.openhab.core.library.items.DimmerItem;
36 import org.openhab.core.library.items.ImageItem;
37 import org.openhab.core.library.items.LocationItem;
38 import org.openhab.core.library.items.NumberItem;
39 import org.openhab.core.library.items.PlayerItem;
40 import org.openhab.core.library.items.RollershutterItem;
41 import org.openhab.core.library.items.StringItem;
42 import org.openhab.core.library.items.SwitchItem;
43 import org.openhab.core.library.types.DateTimeType;
44 import org.openhab.core.library.types.DecimalType;
45 import org.openhab.core.library.types.HSBType;
46 import org.openhab.core.library.types.OnOffType;
47 import org.openhab.core.library.types.OpenClosedType;
48 import org.openhab.core.library.types.PercentType;
49 import org.openhab.core.library.types.PlayPauseType;
50 import org.openhab.core.library.types.PointType;
51 import org.openhab.core.library.types.QuantityType;
52 import org.openhab.core.library.types.RawType;
53 import org.openhab.core.library.types.StringListType;
54 import org.openhab.core.library.types.StringType;
55 import org.openhab.core.persistence.FilterCriteria.Operator;
56 import org.openhab.core.types.State;
57 import org.openhab.core.types.UnDefType;
58 import org.openhab.core.types.util.UnitUtils;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 /**
63  * This class handles the conversion of types between openHAB and MongoDB.
64  * It provides methods to convert openHAB states to MongoDB compatible types and vice versa.
65  * It also provides a method to convert openHAB filter operators to MongoDB query operators.
66  *
67  * @author RenĂ© Ulbricht - Initial contribution
68  */
69 @NonNullByDefault
70 public class MongoDBTypeConversions {
71
72     /**
73      * Converts a MongoDB document to an openHAB state.
74      *
75      * @param item The openHAB item that the state belongs to.
76      * @param doc The MongoDB document to convert.
77      * @return The openHAB state.
78      * @throws IllegalArgumentException If the item type is not supported.
79      */
80     public static State getStateFromDocument(Item item, Document doc) {
81         Item realItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item;
82         BiFunction<Item, Document, State> converter = ITEM_STATE_CONVERTERS.get(realItem.getClass());
83         if (converter != null) {
84             return converter.apply(realItem, doc);
85         } else {
86             throw new IllegalArgumentException("Unsupported item type: " + realItem.getClass().getName());
87         }
88     }
89
90     /**
91      * Converts an openHAB filter operator to a MongoDB query operator.
92      *
93      * @param operator The openHAB filter operator to convert.
94      * @return The MongoDB query operator, or null if the operator is not supported.
95      */
96     public static @Nullable String convertOperator(Operator operator) {
97         return switch (operator) {
98             case EQ -> "$eq";
99             case GT -> "$gt";
100             case GTE -> "$gte";
101             case LT -> "$lt";
102             case LTE -> "$lte";
103             case NEQ -> "$neq";
104             default -> null;
105         };
106     }
107
108     /**
109      * Converts an openHAB state to a MongoDB compatible type.
110      *
111      * @param state The openHAB state to convert.
112      * @return The MongoDB compatible type.
113      */
114     public static Object convertValue(State state) {
115         return STATE_CONVERTERS.getOrDefault(state.getClass(), State::toString).apply(state);
116     }
117
118     private static final Logger logger = LoggerFactory.getLogger(MongoDBTypeConversions.class);
119
120     /**
121      * A map of converters that convert openHAB states to MongoDB compatible types.
122      * Each converter is a function that takes an openHAB state and returns an object that can be stored in MongoDB.
123      */
124     private static final Map<Class<? extends State>, Function<State, Object>> STATE_CONVERTERS = Map.of( //
125             HSBType.class, State::toString, //
126             QuantityType.class, state -> ((QuantityType<?>) state).toBigDecimal().doubleValue(), //
127             PercentType.class, state -> ((PercentType) state).intValue(), //
128             DateTimeType.class, state -> ((DateTimeType) state).getZonedDateTime().toString(), //
129             StringListType.class, State::toString, //
130             DecimalType.class, state -> ((DecimalType) state).toBigDecimal().doubleValue(), //
131             RawType.class, MongoDBTypeConversions::handleRawType//
132     );
133
134     private static Object handleRawType(State state) {
135         RawType rawType = (RawType) state;
136         Document doc = new Document();
137         doc.put(MongoDBFields.FIELD_VALUE_TYPE, rawType.getMimeType());
138         doc.put(MongoDBFields.FIELD_VALUE_DATA, rawType.getBytes());
139         return doc;
140     }
141
142     /**
143      * A map of converters that convert MongoDB documents to openHAB states.
144      * Each converter is a function that takes an openHAB item and a MongoDB document and returns an openHAB state.
145      */
146
147     private static final Map<Class<? extends Item>, BiFunction<Item, Document, State>> ITEM_STATE_CONVERTERS = //
148             Map.ofEntries( //
149                     Map.entry(NumberItem.class, MongoDBTypeConversions::handleNumberItem),
150                     Map.entry(ColorItem.class, MongoDBTypeConversions::handleColorItem),
151                     Map.entry(DimmerItem.class, MongoDBTypeConversions::handleDimmerItem),
152                     Map.entry(SwitchItem.class,
153                             (Item item, Document doc) -> OnOffType.valueOf(doc.getString(MongoDBFields.FIELD_VALUE))),
154                     Map.entry(ContactItem.class,
155                             (Item item, Document doc) -> OpenClosedType
156                                     .valueOf(doc.getString(MongoDBFields.FIELD_VALUE))),
157                     Map.entry(RollershutterItem.class, MongoDBTypeConversions::handleRollershutterItem),
158                     Map.entry(DateTimeItem.class, MongoDBTypeConversions::handleDateTimeItem),
159                     Map.entry(LocationItem.class,
160                             (Item item, Document doc) -> new PointType(doc.getString(MongoDBFields.FIELD_VALUE))),
161                     Map.entry(PlayerItem.class,
162                             (Item item, Document doc) -> PlayPauseType
163                                     .valueOf(doc.getString(MongoDBFields.FIELD_VALUE))),
164                     Map.entry(CallItem.class,
165                             (Item item, Document doc) -> new StringListType(doc.getString(MongoDBFields.FIELD_VALUE))),
166                     Map.entry(ImageItem.class, MongoDBTypeConversions::handleImageItem), //
167                     Map.entry(StringItem.class,
168                             (Item item, Document doc) -> new StringType(doc.getString(MongoDBFields.FIELD_VALUE))),
169                     Map.entry(GenericItem.class,
170                             (Item item, Document doc) -> new StringType(doc.getString(MongoDBFields.FIELD_VALUE)))//
171             );
172
173     private static State handleNumberItem(Item item, Document doc) {
174         NumberItem numberItem = (NumberItem) item;
175         Unit<?> unit = numberItem.getUnit();
176         Object value = doc.get(MongoDBFields.FIELD_VALUE);
177         if (value == null) {
178             return UnDefType.UNDEF;
179         }
180         if (doc.containsKey(MongoDBFields.FIELD_UNIT)) {
181             String unitString = doc.getString(MongoDBFields.FIELD_UNIT);
182             Unit<?> docUnit = UnitUtils.parseUnit(unitString);
183             if (docUnit != null) {
184                 unit = docUnit;
185             }
186         }
187         if (value instanceof String) {
188             return new QuantityType<>(value.toString());
189         }
190         if (unit != null) {
191             return new QuantityType<>(((Number) value).doubleValue(), unit);
192         } else {
193             return new DecimalType(((Number) value).doubleValue());
194         }
195     }
196
197     private static State handleColorItem(Item item, Document doc) {
198         Object value = doc.get(MongoDBFields.FIELD_VALUE);
199         if (value instanceof String) {
200             return new HSBType(value.toString());
201         } else {
202             logger.warn("HSBType ({}) value is not a valid string: {}", doc.getString(MongoDBFields.FIELD_REALNAME),
203                     value);
204             return new HSBType("0,0,0");
205         }
206     }
207
208     private static State handleDimmerItem(Item item, Document doc) {
209         Object value = doc.get(MongoDBFields.FIELD_VALUE);
210         if (value == null) {
211             return UnDefType.UNDEF;
212         }
213         if (value instanceof Integer) {
214             return new PercentType((Integer) value);
215         } else {
216             return new PercentType(((Number) value).intValue());
217         }
218     }
219
220     private static State handleRollershutterItem(Item item, Document doc) {
221         Object value = doc.get(MongoDBFields.FIELD_VALUE);
222         if (value == null) {
223             return UnDefType.UNDEF;
224         }
225         if (value instanceof Integer) {
226             return new PercentType((Integer) value);
227         } else {
228             return new PercentType(((Number) value).intValue());
229         }
230     }
231
232     private static State handleDateTimeItem(Item item, Document doc) {
233         Object value = doc.get(MongoDBFields.FIELD_VALUE);
234         if (value == null) {
235             return UnDefType.UNDEF;
236         }
237         if (value instanceof String) {
238             return new DateTimeType(ZonedDateTime.parse(doc.getString(MongoDBFields.FIELD_VALUE)));
239         } else {
240             return new DateTimeType(ZonedDateTime.ofInstant(((Date) value).toInstant(), ZoneId.systemDefault()));
241         }
242     }
243
244     private static State handleImageItem(Item item, Document doc) {
245         Object value = doc.get(MongoDBFields.FIELD_VALUE);
246         if (value instanceof Document) {
247             Document fieldValue = (Document) value;
248             String type = fieldValue.getString(MongoDBFields.FIELD_VALUE_TYPE);
249             Binary data = fieldValue.get(MongoDBFields.FIELD_VALUE_DATA, Binary.class);
250             return new RawType(data.getData(), type);
251         } else {
252             logger.warn("ImageItem ({}) value is not a Document: {}", doc.getString(MongoDBFields.FIELD_REALNAME),
253                     value);
254             return new RawType(new byte[0], "application/octet-stream");
255         }
256     }
257 }