Skip to content

Commit

Permalink
[mongodb]: fix quantity type storage, introduce new types
Browse files Browse the repository at this point in the history
Signed-off-by: Konrad Zawadka <konrad.zawadka@gmail.com>
  • Loading branch information
Konrad committed Sep 18, 2023
1 parent d0b161a commit 21b3c11
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.openhab.persistence.mongodb.internal;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.TimeZone;

import javax.measure.Unit;

import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.ContactItem;
import org.openhab.core.library.items.DateTimeItem;
import org.openhab.core.library.items.DimmerItem;
import org.openhab.core.library.items.ImageItem;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.RollershutterItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

import io.micrometer.core.lang.Nullable;

/**
* Conversion logic between openHAB {@link State} types and MongoDB store types
*
* @author Konrad Zawadka - Initial contribution, based on previous work from Joan Pujol Espinar, Theo Weiss and Dominik
* Vorreiter
*/
public class MongoDBConvertUtils {
@NonNullByDefault
static final Number DIGITAL_VALUE_OFF = 0; // Visible for testing
@NonNullByDefault
static final Number DIGITAL_VALUE_ON = 1; // Visible for testing
@NonNullByDefault
static final String FIELD_ID = "_id";
@NonNullByDefault
static final String FIELD_ITEM = "item";
@NonNullByDefault
static final String FIELD_REALNAME = "realName";
@NonNullByDefault
static final String FIELD_TIMESTAMP = "timestamp";
@NonNullByDefault
static final String FIELD_VALUE = "value";

protected static Object stateToObject(State state) {
Object value;
if (state instanceof HSBType) {
value = state.toString();
} else if (state instanceof PointType) {
value = state.toString();
} else if (state instanceof DecimalType type) {
value = type.toBigDecimal();
} else if (state instanceof QuantityType<?> type) {
value = type.toBigDecimal();
} else if (state instanceof OnOffType) {
value = state == OnOffType.ON ? DIGITAL_VALUE_ON : DIGITAL_VALUE_OFF;
} else if (state instanceof OpenClosedType) {
value = state == OpenClosedType.OPEN ? DIGITAL_VALUE_ON : DIGITAL_VALUE_OFF;
} else if (state instanceof DateTimeType type) {
value = type.getZonedDateTime().toInstant().toEpochMilli();
} else {
value = state.toFullString();
}
return value;
}

protected static DBObject itemToDBObject(Item item, String alias) {
String name = (alias != null) ? alias : item.getName();
Object value = MongoDBConvertUtils.stateToObject(item.getState());

DBObject obj = new BasicDBObject();
obj.put(FIELD_ID, new ObjectId());
obj.put(FIELD_ITEM, name);
obj.put(FIELD_REALNAME, item.getName());
obj.put(FIELD_TIMESTAMP, new Date());
obj.put(FIELD_VALUE, value);
return obj;
}

protected static State objectToState(BasicDBObject obj, Item itemToSetState) {
final State state;
if (itemToSetState instanceof NumberItem numberItem) {
double value = obj.getDouble(FIELD_VALUE);
Unit<?> unit = numberItem.getUnit();
if (unit == null) {
return new DecimalType(value);
} else {
return new QuantityType<>(value, unit);
}
} else if (itemToSetState instanceof DimmerItem) {
return new PercentType(obj.getInt(FIELD_VALUE));
} else if (itemToSetState instanceof SwitchItem) {
return toBoolean(obj.getString(FIELD_VALUE)) ? OnOffType.ON : OnOffType.OFF;
} else if (itemToSetState instanceof ContactItem) {
return toBoolean(obj.getString(FIELD_VALUE)) ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
} else if (itemToSetState instanceof RollershutterItem) {
state = new PercentType(obj.getInt(FIELD_VALUE));
} else if (itemToSetState instanceof DateTimeItem) {
state = getDateTime(obj);
} else if (itemToSetState instanceof ImageItem) {
state = RawType.valueOf(obj.getString(FIELD_VALUE));
} else {
state = new StringType(obj.getString(FIELD_VALUE));
}
return state;
}

private static State getDateTime(BasicDBObject obj) {
String valueStr = obj.getString(FIELD_VALUE);
if (StringUtils.isNumeric(valueStr)) {
Instant i = Instant.ofEpochMilli(new BigDecimal(valueStr).longValue());
ZonedDateTime z = ZonedDateTime.ofInstant(i, TimeZone.getDefault().toZoneId());
return new DateTimeType(z);
} else {
return new DateTimeType(
ZonedDateTime.ofInstant(obj.getDate(FIELD_VALUE).toInstant(), ZoneId.systemDefault()));
}
}

private static boolean toBoolean(@Nullable Object object) {
if (object instanceof Boolean boolean1) {
return boolean1;
} else if (object != null) {
if ("1".equals(object) || "1.0".equals(object)) {
return true;
} else {
return Boolean.parseBoolean(String.valueOf(object));
}
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
*/
package org.openhab.persistence.mongodb.internal;

import static org.openhab.persistence.mongodb.internal.MongoDBConvertUtils.FIELD_ITEM;
import static org.openhab.persistence.mongodb.internal.MongoDBConvertUtils.FIELD_TIMESTAMP;
import static org.openhab.persistence.mongodb.internal.MongoDBConvertUtils.FIELD_VALUE;
import static org.openhab.persistence.mongodb.internal.MongoDBConvertUtils.itemToDBObject;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
Expand All @@ -22,24 +27,11 @@
import java.util.Map;
import java.util.Set;

import org.bson.types.ObjectId;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.items.ContactItem;
import org.openhab.core.library.items.DateTimeItem;
import org.openhab.core.library.items.DimmerItem;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.RollershutterItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Operator;
import org.openhab.core.persistence.FilterCriteria.Ordering;
Expand Down Expand Up @@ -77,12 +69,6 @@
QueryablePersistenceService.class }, configurationPid = "org.openhab.mongodb", configurationPolicy = ConfigurationPolicy.REQUIRE)
public class MongoDBPersistenceService implements QueryablePersistenceService {

private static final String FIELD_ID = "_id";
private static final String FIELD_ITEM = "item";
private static final String FIELD_REALNAME = "realName";
private static final String FIELD_TIMESTAMP = "timestamp";
private static final String FIELD_VALUE = "value";

private final Logger logger = LoggerFactory.getLogger(MongoDBPersistenceService.class);

private String url = "";
Expand Down Expand Up @@ -172,8 +158,7 @@ public void store(Item item, @Nullable String alias) {
return;
}

String realItemName = item.getName();
String collectionName = collectionPerItem ? realItemName : this.collection;
String collectionName = collectionPerItem ? item.getName() : this.collection;

@Nullable
DBCollection collection = connectToCollection(collectionName);
Expand All @@ -183,32 +168,10 @@ public void store(Item item, @Nullable String alias) {
return;
}

String name = (alias != null) ? alias : realItemName;
Object value = this.convertValue(item.getState());

DBObject obj = new BasicDBObject();
obj.put(FIELD_ID, new ObjectId());
obj.put(FIELD_ITEM, name);
obj.put(FIELD_REALNAME, realItemName);
obj.put(FIELD_TIMESTAMP, new Date());
obj.put(FIELD_VALUE, value);
DBObject obj = itemToDBObject(item, alias);
collection.save(obj);

logger.debug("MongoDB save {}={}", name, value);
}

private Object convertValue(State state) {
Object value;
if (state instanceof PercentType type) {
value = type.toBigDecimal().doubleValue();
} else if (state instanceof DateTimeType type) {
value = Date.from(type.getZonedDateTime().toInstant());
} else if (state instanceof DecimalType type) {
value = type.toBigDecimal().doubleValue();
} else {
value = state.toString();
}
return value;
logger.debug("MongoDB save {}={}", item.getName(), item.getState());
}

@Override
Expand Down Expand Up @@ -369,7 +332,7 @@ public Iterable<HistoricItem> query(FilterCriteria filter) {
return Collections.emptyList();
}

Object value = convertValue(filterState);
Object value = MongoDBConvertUtils.stateToObject(filterState);
query.put(FIELD_VALUE, new BasicDBObject(op, value));
}

Expand All @@ -393,25 +356,7 @@ public Iterable<HistoricItem> query(FilterCriteria filter) {
while (cursor.hasNext()) {
BasicDBObject obj = (BasicDBObject) cursor.next();

final State state;
if (item instanceof NumberItem) {
state = new DecimalType(obj.getDouble(FIELD_VALUE));
} else if (item instanceof DimmerItem) {
state = new PercentType(obj.getInt(FIELD_VALUE));
} else if (item instanceof SwitchItem) {
state = OnOffType.valueOf(obj.getString(FIELD_VALUE));
} else if (item instanceof ContactItem) {
state = OpenClosedType.valueOf(obj.getString(FIELD_VALUE));
} else if (item instanceof RollershutterItem) {
state = new PercentType(obj.getInt(FIELD_VALUE));
} else if (item instanceof DateTimeItem) {
state = new DateTimeType(
ZonedDateTime.ofInstant(obj.getDate(FIELD_VALUE).toInstant(), ZoneId.systemDefault()));
} else {
state = new StringType(obj.getString(FIELD_VALUE));
}

items.add(new MongoDBItem(realItemName, state,
items.add(new MongoDBItem(realItemName, MongoDBConvertUtils.objectToState(obj, item),
ZonedDateTime.ofInstant(obj.getDate(FIELD_TIMESTAMP).toInstant(), ZoneId.systemDefault())));
}

Expand Down
Loading

0 comments on commit 21b3c11

Please sign in to comment.