Add getLastStateChange, getLastStateUpdate, and getLastState to GenericItem (#4351)

* Add `getLastChange`, `getLastUpdate`, and `getPreviousState` to GenericItem
* Rename to getLastStateChange, getLastStateUpdate, getLastState

Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
jimtng
2025-02-15 19:21:43 +01:00
committed by GitHub
parent 2d29ff5545
commit 3b3fd3aa53
6 changed files with 124 additions and 11 deletions
@@ -24,9 +24,11 @@ import org.openhab.core.types.StateDescription;
*/
public class EnrichedGroupItemDTO extends EnrichedItemDTO {
public EnrichedGroupItemDTO(ItemDTO itemDTO, EnrichedItemDTO[] members, String link, String state,
String transformedState, StateDescription stateDescription, String unitSymbol) {
super(itemDTO, link, state, transformedState, stateDescription, null, unitSymbol);
public EnrichedGroupItemDTO(ItemDTO itemDTO, EnrichedItemDTO[] members, String link, String state, String lastState,
Long lastStateUpdate, Long lastStateChange, String transformedState, StateDescription stateDescription,
String unitSymbol) {
super(itemDTO, link, state, lastState, lastStateUpdate, lastStateChange, transformedState, stateDescription,
null, unitSymbol);
this.members = members;
this.groupType = ((GroupItemDTO) itemDTO).groupType;
this.function = ((GroupItemDTO) itemDTO).function;
@@ -32,13 +32,17 @@ public class EnrichedItemDTO extends ItemDTO {
public String state;
public String transformedState;
public StateDescription stateDescription;
public String unitSymbol;
public CommandDescription commandDescription;
public String lastState;
public Long lastStateUpdate;
public Long lastStateChange;
public String unitSymbol;
public Map<String, Object> metadata;
public Boolean editable;
public EnrichedItemDTO(ItemDTO itemDTO, String link, String state, String transformedState,
StateDescription stateDescription, CommandDescription commandDescription, String unitSymbol) {
public EnrichedItemDTO(ItemDTO itemDTO, String link, String state, String lastState, Long lastStateUpdate,
Long lastStateChange, String transformedState, StateDescription stateDescription,
CommandDescription commandDescription, String unitSymbol) {
this.type = itemDTO.type;
this.name = itemDTO.name;
this.label = itemDTO.label;
@@ -50,6 +54,9 @@ public class EnrichedItemDTO extends ItemDTO {
this.transformedState = transformedState;
this.stateDescription = stateDescription;
this.commandDescription = commandDescription;
this.lastState = lastState;
this.lastStateUpdate = lastStateUpdate;
this.lastStateChange = lastStateChange;
this.unitSymbol = unitSymbol;
}
}
@@ -18,6 +18,7 @@ import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -105,6 +106,12 @@ public class EnrichedItemDTOMapper {
}
StateDescription stateDescription = considerTransformation(item.getStateDescription(locale));
String lastState = Optional.ofNullable(item.getLastState()).map(State::toFullString).orElse(null);
Long lastStateUpdate = Optional.ofNullable(item.getLastStateUpdate()).map(zdt -> zdt.toInstant().toEpochMilli())
.orElse(null);
Long lastStateChange = Optional.ofNullable(item.getLastStateChange()).map(zdt -> zdt.toInstant().toEpochMilli())
.orElse(null);
final String link;
if (uriBuilder != null) {
link = uriBuilder.build(itemDTO.name).toASCIIString();
@@ -139,11 +146,11 @@ public class EnrichedItemDTOMapper {
} else {
memberDTOs = new EnrichedItemDTO[0];
}
enrichedItemDTO = new EnrichedGroupItemDTO(itemDTO, memberDTOs, link, state, transformedState,
stateDescription, unitSymbol);
enrichedItemDTO = new EnrichedGroupItemDTO(itemDTO, memberDTOs, link, state, lastState, lastStateUpdate,
lastStateChange, transformedState, stateDescription, unitSymbol);
} else {
enrichedItemDTO = new EnrichedItemDTO(itemDTO, link, state, transformedState, stateDescription,
item.getCommandDescription(locale), unitSymbol);
enrichedItemDTO = new EnrichedItemDTO(itemDTO, link, state, lastState, lastStateUpdate, lastStateChange,
transformedState, stateDescription, item.getCommandDescription(locale), unitSymbol);
}
return enrichedItemDTO;
@@ -12,6 +12,7 @@
*/
package org.openhab.core.items;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -77,6 +78,10 @@ public abstract class GenericItem implements ActiveItem {
protected final String type;
protected State state = UnDefType.NULL;
protected @Nullable State lastState;
protected @Nullable ZonedDateTime lastStateUpdate;
protected @Nullable ZonedDateTime lastStateChange;
protected @Nullable String label;
@@ -103,6 +108,21 @@ public abstract class GenericItem implements ActiveItem {
return state.as(typeClass);
}
@Override
public @Nullable State getLastState() {
return lastState;
}
@Override
public @Nullable ZonedDateTime getLastStateUpdate() {
return lastStateUpdate;
}
@Override
public @Nullable ZonedDateTime getLastStateChange() {
return lastStateChange;
}
@Override
public String getUID() {
return getName();
@@ -218,13 +238,20 @@ public abstract class GenericItem implements ActiveItem {
* @param state new state of this item
*/
protected final void applyState(State state) {
ZonedDateTime now = ZonedDateTime.now();
State oldState = this.state;
boolean stateChanged = !oldState.equals(state);
this.state = state;
if (stateChanged) {
lastState = oldState; // update before we notify listeners
}
notifyListeners(oldState, state);
sendStateUpdatedEvent(state);
if (!oldState.equals(state)) {
if (stateChanged) {
sendStateChangedEvent(state, oldState);
lastStateChange = now; // update after we've notified listeners
}
lastStateUpdate = now;
}
/**
@@ -12,6 +12,7 @@
*/
package org.openhab.core.items;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -51,6 +52,30 @@ public interface Item extends Identifiable<String> {
*/
<T extends State> @Nullable T getStateAs(Class<T> typeClass);
/**
* Returns the previous state of the item.
*
* @return the previous state of the item, or null if the item has never been changed.
*/
@Nullable
State getLastState();
/**
* Returns the time the item was last updated.
*
* @return the time the item was last updated, or null if the item has never been updated.
*/
@Nullable
ZonedDateTime getLastStateUpdate();
/**
* Returns the time the item was last changed.
*
* @return the time the item was last changed, or null if the item has never been changed.
*/
@Nullable
ZonedDateTime getLastStateChange();
/**
* returns the name of the item
*
@@ -13,11 +13,14 @@
package org.openhab.core.items;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Locale;
@@ -39,6 +42,7 @@ import org.openhab.core.types.CommandOption;
import org.openhab.core.types.State;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.openhab.core.types.StateOption;
import org.openhab.core.types.UnDefType;
/**
* The GenericItemTest tests functionality of the GenericItem.
@@ -133,6 +137,47 @@ public class GenericItemTest {
assertNull(item.getStateAs(toNull()));
}
@Test
public void testGetLastStateUpdate() {
TestItem item = new TestItem("member1");
assertNull(item.getLastStateUpdate());
item.setState(PercentType.HUNDRED);
assertThat(item.getLastStateUpdate().toInstant().toEpochMilli() * 1.0,
is(closeTo(ZonedDateTime.now().toInstant().toEpochMilli(), 5)));
}
@Test
public void testGetLastStateChange() throws InterruptedException {
TestItem item = new TestItem("member1");
assertNull(item.getLastStateChange());
item.setState(PercentType.HUNDRED);
ZonedDateTime initialChangeTime = ZonedDateTime.now();
assertThat(item.getLastStateChange().toInstant().toEpochMilli() * 1.0,
is(closeTo(initialChangeTime.toInstant().toEpochMilli(), 5)));
Thread.sleep(50);
item.setState(PercentType.HUNDRED);
assertThat(item.getLastStateChange().toInstant().toEpochMilli() * 1.0,
is(closeTo(initialChangeTime.toInstant().toEpochMilli(), 5)));
Thread.sleep(50);
ZonedDateTime secondChangeTime = ZonedDateTime.now();
item.setState(PercentType.ZERO);
assertThat(item.getLastStateChange().toInstant().toEpochMilli() * 1.0,
is(closeTo(secondChangeTime.toInstant().toEpochMilli(), 5)));
}
@Test
public void testGetLastState() {
TestItem item = new TestItem("member1");
assertEquals(UnDefType.NULL, item.getState());
assertNull(item.getLastState());
item.setState(PercentType.HUNDRED);
assertEquals(UnDefType.NULL, item.getLastState());
item.setState(PercentType.ZERO);
assertEquals(PercentType.HUNDRED, item.getLastState());
}
@Test
public void testDispose() {
TestItem item = new TestItem("test");