[mapdb] Store and restore lastState, lastStateChange and lastChangeUpdate (#17820)

* persist and restore previous state and last state change

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
pull/17250/merge
Mark Herwege 2025-02-16 12:16:48 +01:00 committed by GitHub
parent 94313ba102
commit c123341902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 10 deletions

View File

@ -13,14 +13,13 @@
package org.openhab.persistence.mapdb.internal;
import java.text.DateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistedItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@ -29,21 +28,24 @@ import org.openhab.core.types.UnDefType;
* This is a Java bean used to persist item states with timestamps in the database.
*
* @author Jens Viebig - Initial contribution
* @author Mark Herwege - Add lastState and lastStateChange
*
*/
@NonNullByDefault
public class MapDbItem implements HistoricItem, PersistenceItemInfo {
class MapDbItem implements PersistedItem, PersistenceItemInfo {
private String name = "";
private State state = UnDefType.NULL;
private Date timestamp = new Date(0);
private @Nullable State lastState = null;
private @Nullable Date lastStateChange = null;
@Override
public String getName() {
return name;
}
public void setName(String name) {
void setName(String name) {
this.name = name;
}
@ -52,7 +54,7 @@ public class MapDbItem implements HistoricItem, PersistenceItemInfo {
return state;
}
public void setState(State state) {
void setState(State state) {
this.state = state;
}
@ -61,13 +63,27 @@ public class MapDbItem implements HistoricItem, PersistenceItemInfo {
return ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault());
}
public void setTimestamp(Date timestamp) {
void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
@Override
public Instant getInstant() {
return timestamp.toInstant();
public @Nullable State getLastState() {
return lastState;
}
void setLastState(@Nullable State lastState) {
this.lastState = lastState;
}
@Override
public @Nullable ZonedDateTime getLastStateChange() {
return lastStateChange != null ? ZonedDateTime.ofInstant(lastStateChange.toInstant(), ZoneId.systemDefault())
: null;
}
void setLastStateChange(@Nullable Date lastStateChange) {
this.lastStateChange = lastStateChange;
}
@Override
@ -90,7 +106,7 @@ public class MapDbItem implements HistoricItem, PersistenceItemInfo {
return null;
}
public boolean isValid() {
boolean isValid() {
return name != null && state != null && timestamp != null;
}
}

View File

@ -18,6 +18,7 @@ import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -38,6 +39,7 @@ import org.openhab.core.items.Item;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistedItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.QueryablePersistenceService;
@ -185,7 +187,11 @@ public class MapDbPersistenceService implements QueryablePersistenceService {
MapDbItem mItem = new MapDbItem();
mItem.setName(localAlias);
mItem.setState(state);
mItem.setTimestamp(new Date());
mItem.setLastState(item.getLastState());
ZonedDateTime lastStateUpdate = item.getLastStateUpdate();
mItem.setTimestamp(lastStateUpdate != null ? Date.from(lastStateUpdate.toInstant()) : new Date());
ZonedDateTime lastStateChange = item.getLastStateChange();
mItem.setLastStateChange(lastStateChange != null ? Date.from(lastStateChange.toInstant()) : null);
threadPool.submit(() -> {
String json = serialize(mItem);
map.put(localAlias, json);
@ -204,6 +210,16 @@ public class MapDbPersistenceService implements QueryablePersistenceService {
return item.isPresent() ? List.of(item.get()) : List.of();
}
@Override
public @Nullable PersistedItem persistedItem(String itemName) {
String json = map.get(itemName);
if (json == null) {
return null;
}
Optional<MapDbItem> item = deserialize(json);
return item.orElse(null);
}
private String serialize(MapDbItem item) {
return mapper.toJson(item);
}

View File

@ -17,12 +17,14 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.collection.IsEmptyIterable.emptyIterable;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.ZonedDateTime;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
@ -32,6 +34,7 @@ import org.openhab.core.items.GenericItem;
import org.openhab.core.library.items.ColorItem;
import org.openhab.core.library.items.DimmerItem;
import org.openhab.core.library.items.SwitchItem;
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.PercentType;
@ -133,4 +136,26 @@ public class MapDbPersistenceServiceOSGiTest extends JavaOSGiTest {
waitForAssert(() -> assertThat(persistenceService.query(filterByAlias),
contains(allOf(hasProperty("name", equalTo(alias)), hasProperty("state", equalTo(state))))));
}
@Test
public void persistedItemShouldFindItem() {
String name = "decimal";
State state = DecimalType.valueOf("100");
State lastState = DecimalType.ZERO;
ZonedDateTime lastStateUpdate = ZonedDateTime.now().minusHours(1);
ZonedDateTime lastStateChange = ZonedDateTime.now().minusHours(2);
GenericItem item = new DimmerItem(name);
item.setState(state, lastState, lastStateUpdate, lastStateChange);
assertNull(persistenceService.persistedItem(name));
persistenceService.store(item);
waitForAssert(() -> assertThat(persistenceService.persistedItem(name),
allOf(hasProperty("name", equalTo(name)), hasProperty("state", equalTo(state)),
hasProperty("lastState", equalTo(lastState)),
hasProperty("timestamp", any(ZonedDateTime.class)),
hasProperty("lastStateChange", any(ZonedDateTime.class)))));
}
}