2 * Copyright (c) 2010-2020 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.dynamodb.internal;
15 import java.math.BigDecimal;
16 import java.text.DateFormat;
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.time.format.DateTimeFormatter;
20 import java.time.format.DateTimeParseException;
21 import java.util.HashMap;
24 import org.openhab.core.items.Item;
25 import org.openhab.core.library.items.CallItem;
26 import org.openhab.core.library.items.ColorItem;
27 import org.openhab.core.library.items.ContactItem;
28 import org.openhab.core.library.items.DateTimeItem;
29 import org.openhab.core.library.items.DimmerItem;
30 import org.openhab.core.library.items.LocationItem;
31 import org.openhab.core.library.items.NumberItem;
32 import org.openhab.core.library.items.PlayerItem;
33 import org.openhab.core.library.items.RollershutterItem;
34 import org.openhab.core.library.items.StringItem;
35 import org.openhab.core.library.items.SwitchItem;
36 import org.openhab.core.library.types.DateTimeType;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.HSBType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.OpenClosedType;
41 import org.openhab.core.library.types.PercentType;
42 import org.openhab.core.library.types.PlayPauseType;
43 import org.openhab.core.library.types.PointType;
44 import org.openhab.core.library.types.RewindFastforwardType;
45 import org.openhab.core.library.types.StringListType;
46 import org.openhab.core.library.types.StringType;
47 import org.openhab.core.library.types.UpDownType;
48 import org.openhab.core.persistence.HistoricItem;
49 import org.openhab.core.types.State;
50 import org.openhab.core.types.UnDefType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * Base class for all DynamoDBItem. Represents openHAB Item serialized in a suitable format for the database
57 * @param <T> Type of the state as accepted by the AWS SDK.
59 * @author Sami Salonen - Initial contribution
61 public abstract class AbstractDynamoDBItem<T> implements DynamoDBItem<T> {
63 public static final DateTimeFormatter DATEFORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT)
64 .withZone(ZoneId.of("UTC"));
66 private static final String UNDEFINED_PLACEHOLDER = "<org.openhab.core.types.UnDefType.UNDEF>";
68 private static final Map<Class<? extends Item>, Class<? extends DynamoDBItem<?>>> ITEM_CLASS_MAP = new HashMap<>();
71 ITEM_CLASS_MAP.put(CallItem.class, DynamoDBStringItem.class);
72 ITEM_CLASS_MAP.put(ContactItem.class, DynamoDBBigDecimalItem.class);
73 ITEM_CLASS_MAP.put(DateTimeItem.class, DynamoDBStringItem.class);
74 ITEM_CLASS_MAP.put(LocationItem.class, DynamoDBStringItem.class);
75 ITEM_CLASS_MAP.put(NumberItem.class, DynamoDBBigDecimalItem.class);
76 ITEM_CLASS_MAP.put(RollershutterItem.class, DynamoDBBigDecimalItem.class);
77 ITEM_CLASS_MAP.put(StringItem.class, DynamoDBStringItem.class);
78 ITEM_CLASS_MAP.put(SwitchItem.class, DynamoDBBigDecimalItem.class);
79 ITEM_CLASS_MAP.put(DimmerItem.class, DynamoDBBigDecimalItem.class); // inherited from SwitchItem (!)
80 ITEM_CLASS_MAP.put(ColorItem.class, DynamoDBStringItem.class); // inherited from DimmerItem
81 ITEM_CLASS_MAP.put(PlayerItem.class, DynamoDBStringItem.class);
84 public static final Class<DynamoDBItem<?>> getDynamoItemClass(Class<? extends Item> itemClass)
85 throws NullPointerException {
86 @SuppressWarnings("unchecked")
87 Class<DynamoDBItem<?>> dtoclass = (Class<DynamoDBItem<?>>) ITEM_CLASS_MAP.get(itemClass);
88 if (dtoclass == null) {
89 throw new IllegalArgumentException(String.format("Unknown item class %s", itemClass));
94 private final Logger logger = LoggerFactory.getLogger(AbstractDynamoDBItem.class);
96 protected String name;
98 protected ZonedDateTime time;
100 public AbstractDynamoDBItem(String name, T state, ZonedDateTime time) {
106 public static DynamoDBItem<?> fromState(String name, State state, ZonedDateTime time) {
107 if (state instanceof DecimalType && !(state instanceof HSBType)) {
108 // also covers PercentType which is inherited from DecimalType
109 return new DynamoDBBigDecimalItem(name, ((DecimalType) state).toBigDecimal(), time);
110 } else if (state instanceof OnOffType) {
111 return new DynamoDBBigDecimalItem(name,
112 ((OnOffType) state) == OnOffType.ON ? BigDecimal.ONE : BigDecimal.ZERO, time);
113 } else if (state instanceof OpenClosedType) {
114 return new DynamoDBBigDecimalItem(name,
115 ((OpenClosedType) state) == OpenClosedType.OPEN ? BigDecimal.ONE : BigDecimal.ZERO, time);
116 } else if (state instanceof UpDownType) {
117 return new DynamoDBBigDecimalItem(name,
118 ((UpDownType) state) == UpDownType.UP ? BigDecimal.ONE : BigDecimal.ZERO, time);
119 } else if (state instanceof DateTimeType) {
120 return new DynamoDBStringItem(name, ((DateTimeType) state).getZonedDateTime().format(DATEFORMATTER), time);
121 } else if (state instanceof UnDefType) {
122 return new DynamoDBStringItem(name, UNDEFINED_PLACEHOLDER, time);
123 } else if (state instanceof StringListType) {
124 return new DynamoDBStringItem(name, state.toFullString(), time);
126 // HSBType, PointType, PlayPauseType and StringType
127 return new DynamoDBStringItem(name, state.toFullString(), time);
132 public HistoricItem asHistoricItem(final Item item) {
133 final State[] state = new State[1];
134 accept(new DynamoDBItemVisitor() {
137 public void visit(DynamoDBStringItem dynamoStringItem) {
138 if (item instanceof ColorItem) {
139 state[0] = new HSBType(dynamoStringItem.getState());
140 } else if (item instanceof LocationItem) {
141 state[0] = new PointType(dynamoStringItem.getState());
142 } else if (item instanceof PlayerItem) {
143 String value = dynamoStringItem.getState();
145 state[0] = PlayPauseType.valueOf(value);
146 } catch (IllegalArgumentException e) {
147 state[0] = RewindFastforwardType.valueOf(value);
149 } else if (item instanceof DateTimeItem) {
151 // Parse ZoneDateTime from string. DATEFORMATTER assumes UTC in case it is not clear
152 // from the string (should be).
153 // We convert to default/local timezone for user convenience (e.g. display)
154 state[0] = new DateTimeType(ZonedDateTime.parse(dynamoStringItem.getState(), DATEFORMATTER)
155 .withZoneSameInstant(ZoneId.systemDefault()));
156 } catch (DateTimeParseException e) {
157 logger.warn("Failed to parse {} as date. Outputting UNDEF instead",
158 dynamoStringItem.getState());
159 state[0] = UnDefType.UNDEF;
161 } else if (dynamoStringItem.getState().equals(UNDEFINED_PLACEHOLDER)) {
162 state[0] = UnDefType.UNDEF;
163 } else if (item instanceof CallItem) {
164 String parts = dynamoStringItem.getState();
165 String[] strings = parts.split(",");
166 String orig = strings[0];
167 String dest = strings[1];
168 state[0] = new StringListType(orig, dest);
170 state[0] = new StringType(dynamoStringItem.getState());
175 public void visit(DynamoDBBigDecimalItem dynamoBigDecimalItem) {
176 if (item instanceof NumberItem) {
177 state[0] = new DecimalType(dynamoBigDecimalItem.getState());
178 } else if (item instanceof DimmerItem) {
179 state[0] = new PercentType(dynamoBigDecimalItem.getState());
180 } else if (item instanceof SwitchItem) {
181 state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OnOffType.ON
183 } else if (item instanceof ContactItem) {
184 state[0] = dynamoBigDecimalItem.getState().compareTo(BigDecimal.ONE) == 0 ? OpenClosedType.OPEN
185 : OpenClosedType.CLOSED;
186 } else if (item instanceof RollershutterItem) {
187 state[0] = new PercentType(dynamoBigDecimalItem.getState());
189 logger.warn("Not sure how to convert big decimal item {} to type {}. Using StringType as fallback",
190 dynamoBigDecimalItem.getName(), item.getClass());
191 state[0] = new StringType(dynamoBigDecimalItem.getState().toString());
195 return new DynamoDBHistoricItem(getName(), state[0], getTime());
199 * We define all getter and setters in the child class implement those. Having the getter
200 * and setter implementations here in the parent class does not work with introspection done by AWS SDK (1.11.56).
206 * @see org.openhab.persistence.dynamodb.internal.DynamoItem#accept(org.openhab.persistence.dynamodb.internal.
210 public abstract void accept(DynamoDBItemVisitor visitor);
213 public String toString() {
214 return DateFormat.getDateTimeInstance().format(time) + ": " + name + " -> " + state.toString();