Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[basicprofiles] Fix StateFilterProfile to use linked Item system unit #18144

Merged
merged 28 commits into from
Feb 12, 2025
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b905a03
[basicprofiles] Improve StateFilterProfile unit based calculations
andrewfg Jan 20, 2025
b440dbb
formatting
andrewfg Jan 20, 2025
fca17b5
work in progress; some test still fail
andrewfg Jan 22, 2025
0e44b67
revert toList
andrewfg Jan 22, 2025
f156748
fix unit tests
andrewfg Jan 23, 2025
a3051d1
work in progress
andrewfg Jan 23, 2025
a995800
Merge remote-tracking branch 'upstream/main' into statefilter-functions
andrewfg Jan 23, 2025
baf9679
add todo
andrewfg Jan 23, 2025
75b3040
[basicprofiles] add invertible unit tests
andrewfg Jan 24, 2025
563c972
[basicprofiles] work in progress
andrewfg Jan 24, 2025
93ba80c
Merge branch 'main' into statefilter-functions
andrewfg Jan 24, 2025
90237fa
[basicprofiles] fix invertible unit calculations
andrewfg Jan 24, 2025
bf1b250
[basicprofiles] fix comment
andrewfg Jan 24, 2025
065a280
[basicprofiles] add time tests
andrewfg Jan 24, 2025
1cb58bd
[basicprofiles] optimise calculate and method order
andrewfg Jan 25, 2025
e11c8ba
Merge branch 'openhab:main' into statefilter-functions
andrewfg Jan 27, 2025
20b0a5e
readme and pre- merge prior PRs
andrewfg Jan 27, 2025
4a92b76
revert log message
andrewfg Jan 27, 2025
7a4a916
Merge branch 'openhab:main' into statefilter-functions
andrewfg Feb 1, 2025
4f6b04c
filter previousStates for DecimalType only
andrewfg Feb 3, 2025
3449378
Merge branch 'main' into statefilter-functions
andrewfg Feb 4, 2025
f497591
align with prior PRs
andrewfg Feb 4, 2025
b36852c
work in progress
andrewfg Feb 7, 2025
274b681
fix junit test failures
andrewfg Feb 8, 2025
3e708e2
eliminate dependency on OH core PR
andrewfg Feb 8, 2025
519e884
Merge branch 'main' into statefilter-functions
andrewfg Feb 9, 2025
8dae92e
fix merge conflict
andrewfg Feb 9, 2025
bcb1445
work in progress
andrewfg Feb 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[basicprofiles] work in progress
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
andrewfg committed Jan 24, 2025
commit 563c972e18a1923dd6fbbbe11c9e0505f9663918
Original file line number Diff line number Diff line change
@@ -106,7 +106,7 @@ public class StateFilterProfile implements StateProfile {

private State newState = UnDefType.UNDEF;
private State acceptedState = UnDefType.UNDEF;
private LinkedList<State> previousStates = new LinkedList<>();
private LinkedList<State> previousValidNumericStates = new LinkedList<>();

private final int windowSize;

@@ -163,6 +163,9 @@ public StateFilterProfile(ProfileCallback callback, ProfileContext context, Item
return systemUnit;
}

/**
* Return true if 'systemUnit' is defined.
*/
protected boolean hasSystemUnit() {
return initSystemUnit() != null;
}
@@ -197,6 +200,18 @@ protected List<QuantityType> systemUnitQuantityTypes(List<State> states) {
.map(s -> (QuantityType) s).toList();
}

/**
* Check if the given {@link State} is allowed. Non allowed means that there is a 'systemUnit', the {@link State}
* is a {@link QuantityType}, and the value is not compatible with 'systemUnit'.
*
* @param state the incoming state
* @return true if allowed
*/
protected boolean isStateAllowed(State state) {
return !(state instanceof QuantityType<?>) || !hasSystemUnit()
|| Objects.nonNull(systemUnitQuantityType(state));
}

private List<StateCondition> parseConditions(List<String> conditions, String separator) {
List<StateCondition> parsedConditions = new ArrayList<>();

@@ -254,19 +269,24 @@ public void onCommandFromHandler(Command command) {

@Override
public void onStateUpdateFromHandler(State state) {
if (!isStateAllowed(state)) {
logger.debug("Received non allowed state update from handler: {}, ignored", state);
return;
}
newState = state;
State resultState = checkCondition(state);
if (resultState != null) {
logger.debug("Received state update from handler: {}, forwarded as {}", state, resultState);
logger.debug("Received allowed state update from handler: {}, forwarded as {}", state, resultState);
acceptedState = resultState;
callback.sendUpdate(resultState);
} else {
logger.debug("Received state update from handler: {}, not forwarded to item", state);
logger.debug("Received allowed state update from handler: {}, not forwarded to item", state);
}
if (windowSize > 0 && ((state instanceof DecimalType) || ((state instanceof QuantityType)
&& (!hasSystemUnit() || (systemUnitQuantityType(state) != null))))) {
previousStates.add(state);
if (previousStates.size() > windowSize) {
previousStates.removeFirst();
&& (!hasSystemUnit() || Objects.nonNull(systemUnitQuantityType(state)))))) {
previousValidNumericStates.add(state);
if (previousValidNumericStates.size() > windowSize) {
previousValidNumericStates.removeFirst();
}
}
}
@@ -279,13 +299,7 @@ private State checkCondition(State state) {
callback.getItemChannelLink());
return null;
}

if (conditions.stream().allMatch(c -> c.check(state))) {
acceptedState = state;
return state;
} else {
return configMismatchState;
}
return conditions.stream().allMatch(c -> c.check(state)) ? state : configMismatchState;
}

private @Nullable Item getLinkedItem() {
@@ -399,10 +413,8 @@ public boolean check(State input) {
if (rhsState == null) {
rhsItem = getItemOrNull(rhsString);
} else if (rhsState instanceof FunctionType rhsFunction) {
if (acceptedState == UnDefType.UNDEF
&& (rhsFunction.getType() == FunctionType.Function.DELTA
|| rhsFunction.getType() == FunctionType.Function.DELTA_PERCENT)
&& (!hasSystemUnit() || systemUnitQuantityType(input) != null)) {
if (acceptedState == UnDefType.UNDEF && (rhsFunction.getType() == FunctionType.Function.DELTA
|| rhsFunction.getType() == FunctionType.Function.DELTA_PERCENT)) {
return true;
}
rhsItem = getLinkedItem();
@@ -422,10 +434,8 @@ public boolean check(State input) {
return false;
}
} else if (lhsState instanceof FunctionType lhsFunction) {
if (acceptedState == UnDefType.UNDEF
&& (lhsFunction.getType() == FunctionType.Function.DELTA
|| lhsFunction.getType() == FunctionType.Function.DELTA_PERCENT)
&& (!hasSystemUnit() || systemUnitQuantityType(input) != null)) {
if (acceptedState == UnDefType.UNDEF && (lhsFunction.getType() == FunctionType.Function.DELTA
|| lhsFunction.getType() == FunctionType.Function.DELTA_PERCENT)) {
return true;
}
lhsItem = getLinkedItem();
@@ -604,9 +614,10 @@ public FunctionType(Function type, Optional<Integer> windowSize) {

public @Nullable State calculate() {
logger.debug("Calculating function: {}", this);
int size = previousStates.size();
int size = previousValidNumericStates.size();
int start = windowSize.map(w -> size - w).orElse(0);
List<State> states = start <= 0 ? previousStates : previousStates.subList(start, size);
List<State> states = start <= 0 ? previousValidNumericStates
: previousValidNumericStates.subList(start, size);
return switch (type) {
case DELTA -> calculateDelta();
case DELTA_PERCENT -> calculateDeltaPercent();
Original file line number Diff line number Diff line change
@@ -813,6 +813,11 @@ public void testFirstDataIsAcceptedForDeltaFunctions(String conditions) throws I
}

public static Stream<Arguments> testMixedStates() {
/*
* -------------------------------------------------------------------------------------
* TODO some of these test cases will fail unless OH Core PR #4561 will have been merged
* -------------------------------------------------------------------------------------
*/
NumberItem powerItem = new NumberItem("Number:Power", "powerItem", UNIT_PROVIDER);

List<State> states = List.of( //
@@ -886,6 +891,9 @@ public void testMixedStates(Item item, String condition, List<State> states, Sta
verify(mockCallback, times(expected ? 1 : 0)).sendUpdate(input);
}

/**
* A {@link UnitProvider} that provides Units.MIRED
*/
protected static class MirekUnitProvider implements UnitProvider {

@SuppressWarnings("unchecked")
@@ -906,32 +914,37 @@ public Collection<Class<? extends Quantity<?>>> getAllDimensions() {
}

public static Stream<Arguments> testColorTemperatureValues() {
/*
* -----------------------------------------------------------------------------------------------
* TODO some of these test cases will fail unless OH Core PR #4561 and #4571 will have been merged
* -----------------------------------------------------------------------------------------------
*/
NumberItem kelvinItem = new NumberItem("Number:Temperature", "kelvinItem", UNIT_PROVIDER);
NumberItem mirekItem = new NumberItem("Number:Temperature", "mirekItem", new MirekUnitProvider());

List<State> states = List.of( //
QuantityType.valueOf(500, Units.MIRED), //
QuantityType.valueOf(2000, Units.KELVIN), //
QuantityType.valueOf(1726.85, SIUnits.CELSIUS), //
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT));
QuantityType.valueOf(2000 + (1 * 100), Units.KELVIN), //
QuantityType.valueOf(1726.85 + (2 * 100), SIUnits.CELSIUS), //
QuantityType.valueOf(3140.33 + (3 * 180), ImperialUnits.FAHRENHEIT));

return Stream.of( //
// kelvin based item
Arguments.of(kelvinItem, "== $MIN", states, QuantityType.valueOf("2000 K"), true),
Arguments.of(kelvinItem, "== $MAX", states, QuantityType.valueOf("2000 K"), true),

// mirek based item
Arguments.of(mirekItem, "== $MIN", states, QuantityType.valueOf("500 mired"), true),
Arguments.of(mirekItem, "== $MAX", states, QuantityType.valueOf("500 mired"), true),

// celsius
Arguments.of(kelvinItem, "== $AVG", states, QuantityType.valueOf("2150 K"), true),
Arguments.of(kelvinItem, "== $MAX", states, QuantityType.valueOf("2300 K"), true),
Arguments.of(kelvinItem, "== $MIN", states, QuantityType.valueOf(500, Units.MIRED), true),
Arguments.of(kelvinItem, "== $MIN", states, QuantityType.valueOf(1726.85, SIUnits.CELSIUS), true),
Arguments.of(mirekItem, "== $MAX", states, QuantityType.valueOf(1726.85, SIUnits.CELSIUS), true),

// fahrenheit
Arguments.of(kelvinItem, "== $MIN", states, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT),
true),
Arguments.of(mirekItem, "== $MAX", states, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT),

// mirek based item
Arguments.of(mirekItem, "== $MIN", states, QuantityType.valueOf("2000 K"), true),
Arguments.of(mirekItem, "== $AVG", states, QuantityType.valueOf("2150 K"), true),
Arguments.of(mirekItem, "== $MAX", states, QuantityType.valueOf("2300 K"), true),
Arguments.of(mirekItem, "== $MIN", states, QuantityType.valueOf(500, Units.MIRED), true),
Arguments.of(mirekItem, "== $MIN", states, QuantityType.valueOf(1726.85, SIUnits.CELSIUS), true),
Arguments.of(mirekItem, "== $MIN", states, QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT),
true) //
);
}