2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.persistence.jpa.internal;
15 import java.text.DateFormat;
16 import java.time.Instant;
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.util.List;
20 import java.util.Objects;
21 import java.util.stream.Collectors;
23 import javax.measure.Unit;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.core.items.Item;
28 import org.openhab.core.library.items.ContactItem;
29 import org.openhab.core.library.items.DateTimeItem;
30 import org.openhab.core.library.items.DimmerItem;
31 import org.openhab.core.library.items.LocationItem;
32 import org.openhab.core.library.items.NumberItem;
33 import org.openhab.core.library.items.RollershutterItem;
34 import org.openhab.core.library.items.SwitchItem;
35 import org.openhab.core.library.types.DateTimeType;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.library.types.OpenClosedType;
39 import org.openhab.core.library.types.PercentType;
40 import org.openhab.core.library.types.PointType;
41 import org.openhab.core.library.types.QuantityType;
42 import org.openhab.core.library.types.StringListType;
43 import org.openhab.core.library.types.StringType;
44 import org.openhab.core.library.unit.Units;
45 import org.openhab.core.persistence.HistoricItem;
46 import org.openhab.core.types.State;
47 import org.openhab.core.types.UnDefType;
48 import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * The historic item as returned when querying the service.
55 * @author Manfred Bergmann - Initial contribution
59 public class JpaHistoricItem implements HistoricItem {
60 private static final Logger logger = LoggerFactory.getLogger(JpaHistoricItem.class);
62 private final String name;
63 private final State state;
64 private final ZonedDateTime timestamp;
66 public JpaHistoricItem(String name, State state, ZonedDateTime timestamp) {
69 this.timestamp = timestamp;
73 public String getName() {
78 public ZonedDateTime getTimestamp() {
83 public State getState() {
88 public String toString() {
89 return DateFormat.getDateTimeInstance().format(timestamp) + ": " + name + " -> " + state.toString();
93 * This method maps {@link JpaPersistentItem}s to {@link HistoricItem}s.
95 * @param jpaQueryResult the result with jpa items
96 * @param item used for query information, like the state (State)
97 * @return list of historic items
99 public static List<HistoricItem> fromResultList(List<JpaPersistentItem> jpaQueryResult, Item item) {
100 return jpaQueryResult.stream().map(pItem -> fromPersistedItem(pItem, item)).filter(Objects::nonNull)
101 .map(Objects::requireNonNull).collect(Collectors.toList());
105 * Converts the string value of the persisted item to the state of a {@link HistoricItem}.
107 * @param pItem the persisted {@link JpaPersistentItem}
108 * @param item the source reference Item
109 * @return historic item
111 public static @Nullable HistoricItem fromPersistedItem(JpaPersistentItem pItem, Item item) {
113 if (item instanceof NumberItem numberItem) {
114 Unit<?> unit = numberItem.getUnit();
115 QuantityType<?> value = QuantityType.valueOf(pItem.getValue());
117 // Item has no unit; drop any persisted unit
118 state = Objects.requireNonNull(value.as(DecimalType.class));
119 } else if (value.getUnit() == Units.ONE) {
120 // No persisted unit; assume the item's unit
121 state = new QuantityType<>(value.toBigDecimal(), unit);
123 // Ensure we return in the item's unit
124 state = value.toUnit(unit);
126 logger.warn("Persisted state {} for item {} is incompatible with item's unit {}; ignoring", value,
127 item.getName(), unit);
131 } else if (item instanceof DimmerItem) {
132 state = new PercentType(Integer.parseInt(pItem.getValue()));
133 } else if (item instanceof SwitchItem) {
134 state = OnOffType.valueOf(pItem.getValue());
135 } else if (item instanceof ContactItem) {
136 state = OpenClosedType.valueOf(pItem.getValue());
137 } else if (item instanceof RollershutterItem) {
138 state = PercentType.valueOf(pItem.getValue());
139 } else if (item instanceof DateTimeItem) {
140 state = new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(pItem.getValue())),
141 ZoneId.systemDefault()));
142 } else if (item instanceof LocationItem) {
143 PointType pType = null;
144 String[] comps = pItem.getValue().split(";");
145 if (comps.length >= 2) {
146 pType = new PointType(new DecimalType(comps[0]), new DecimalType(comps[1]));
148 if (comps.length == 3) {
149 pType.setAltitude(new DecimalType(comps[2]));
152 state = pType == null ? UnDefType.UNDEF : pType;
153 } else if (item instanceof StringListType) {
154 state = new StringListType(pItem.getValue());
156 state = new StringType(pItem.getValue());
159 return new JpaHistoricItem(item.getName(), state, pItem.getTimestamp());