From 1c786e7181bc2aa56228cee06085e830373f9923 Mon Sep 17 00:00:00 2001 From: Christoph Weitkamp Date: Sun, 17 May 2026 00:24:32 +0200 Subject: [PATCH] Add parameter on Hysteresis Profile to exclude current Item State from calculation (#5556) Signed-off-by: Christoph Weitkamp --- .../SystemHysteresisStateProfile.java | 31 ++++++++++++----- .../OH-INF/config/hysteresisProfile.xml | 5 +++ .../OH-INF/i18n/SystemProfiles.properties | 2 ++ .../SystemHysteresisStateProfileTest.java | 34 +++++++++++++++---- 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/bundles/org.openhab.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfile.java b/bundles/org.openhab.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfile.java index 9f2b73565..6193d4e94 100644 --- a/bundles/org.openhab.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfile.java +++ b/bundles/org.openhab.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfile.java @@ -43,8 +43,9 @@ import tech.units.indriya.AbstractUnit; public class SystemHysteresisStateProfile implements StateProfile { static final String LOWER_PARAM = "lower"; - private static final String UPPER_PARAM = "upper"; - private static final String INVERTED_PARAM = "inverted"; + static final String UPPER_PARAM = "upper"; + static final String INVERTED_PARAM = "inverted"; + static final String EXCLUDE_ITEM_STATE_PARAM = "excludeItemState"; private final Logger logger = LoggerFactory.getLogger(SystemHysteresisStateProfile.class); @@ -54,6 +55,7 @@ public class SystemHysteresisStateProfile implements StateProfile { private final QuantityType upper; private final OnOffType low; private final OnOffType high; + private final boolean excludeItemState; private Type previousType = UnDefType.UNDEF; @@ -75,10 +77,15 @@ public class SystemHysteresisStateProfile implements StateProfile { } this.upper = convertedUpperParam; - final Object paramValue = context.getConfiguration().get(INVERTED_PARAM); - final boolean inverted = paramValue != null && Boolean.parseBoolean(paramValue.toString()); + final Object invertedParamValue = context.getConfiguration().get(INVERTED_PARAM); + final boolean inverted = invertedParamValue != null && Boolean.parseBoolean(invertedParamValue.toString()); this.low = OnOffType.from(inverted); this.high = OnOffType.from(!inverted); + + final Object excludeItemStateParamValue = context.getConfiguration().get(EXCLUDE_ITEM_STATE_PARAM); + this.excludeItemState = excludeItemStateParamValue != null + && Boolean.parseBoolean(excludeItemStateParamValue.toString()); + ; } private @Nullable QuantityType getParam(ProfileContext context, String param) { @@ -161,10 +168,18 @@ public class SystemHysteresisStateProfile implements StateProfile { } private Type mapValue(double lower, double upper, double value) { - if (value <= lower) { - return low; - } else if (value >= upper) { - return high; + if (excludeItemState) { + if (value < lower) { + return low; + } else if (value >= upper) { + return high; + } + } else { + if (value <= lower) { + return low; + } else if (value > upper) { + return high; + } } return previousType; } diff --git a/bundles/org.openhab.core.thing/src/main/resources/OH-INF/config/hysteresisProfile.xml b/bundles/org.openhab.core.thing/src/main/resources/OH-INF/config/hysteresisProfile.xml index 43a1b7962..10c77c59a 100644 --- a/bundles/org.openhab.core.thing/src/main/resources/OH-INF/config/hysteresisProfile.xml +++ b/bundles/org.openhab.core.thing/src/main/resources/OH-INF/config/hysteresisProfile.xml @@ -18,5 +18,10 @@ Inverts resulting mapping of ON / OFF, if true. false + + + Do not include Item State in comparison. + false + diff --git a/bundles/org.openhab.core.thing/src/main/resources/OH-INF/i18n/SystemProfiles.properties b/bundles/org.openhab.core.thing/src/main/resources/OH-INF/i18n/SystemProfiles.properties index 8b2c08f35..26676974e 100644 --- a/bundles/org.openhab.core.thing/src/main/resources/OH-INF/i18n/SystemProfiles.properties +++ b/bundles/org.openhab.core.thing/src/main/resources/OH-INF/i18n/SystemProfiles.properties @@ -10,6 +10,8 @@ profile.config.system.hysteresis.upper.label = Upper Bound profile.config.system.hysteresis.upper.description = Maps to ON if value is greater than or equal to upper bound (plain number or number with unit). profile.config.system.hysteresis.inverted.label = Inverted profile.config.system.hysteresis.inverted.description = Inverts resulting mapping of ON / OFF, if true. +profile.config.system.hysteresis.excludeItemState.label = Exclude Item State +profile.config.system.hysteresis.excludeItemState.description = Do not include Item State in comparison. profile-type.system.range.label = Range profile.config.system.range.lower.label = Lower Bound profile.config.system.range.lower.description = Maps to ON if value is between lower and upper bound (plain number or number with unit). diff --git a/bundles/org.openhab.core.thing/src/test/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfileTest.java b/bundles/org.openhab.core.thing/src/test/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfileTest.java index cfcc39309..e45457213 100644 --- a/bundles/org.openhab.core.thing/src/test/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfileTest.java +++ b/bundles/org.openhab.core.thing/src/test/java/org/openhab/core/thing/internal/profiles/SystemHysteresisStateProfileTest.java @@ -185,13 +185,33 @@ public class SystemHysteresisStateProfileTest { @Test public void testInvertedParameter() { - final StateProfile profile = initProfile(BigDecimal.TEN, null, true); + final StateProfile profile = initProfile(BigDecimal.TEN, null, true, false); verifySendCommand(profile, PercentType.HUNDRED, OnOffType.OFF); + verifySendCommand(profile, PERCENT_TYPE_TEN, OnOffType.ON); verifySendCommand(profile, PercentType.ZERO, OnOffType.ON); verifySendUpdate(profile, PercentType.HUNDRED, OnOffType.OFF); + verifySendUpdate(profile, PERCENT_TYPE_TEN, OnOffType.ON); verifySendUpdate(profile, PercentType.ZERO, OnOffType.ON); } + @Test + public void testInvertedParameterExcludeItemState() { + final StateProfile profile = initProfile(BigDecimal.TEN, null, true, true); + verifySendCommand(profile, PercentType.HUNDRED, OnOffType.OFF); + verifySendCommand(profile, PERCENT_TYPE_TEN, OnOffType.OFF); + verifySendCommand(profile, PercentType.ZERO, OnOffType.ON); + verifySendUpdate(profile, PercentType.HUNDRED, OnOffType.OFF); + verifySendUpdate(profile, PERCENT_TYPE_TEN, OnOffType.OFF); + verifySendUpdate(profile, PercentType.ZERO, OnOffType.ON); + } + + @Test + public void testExcludeItemStateParameter() { + final StateProfile profile = initProfile(BigDecimal.TEN, null, false, true); + verifySendCommand(profile, PERCENT_TYPE_TEN, OnOffType.ON); + verifySendUpdate(profile, PERCENT_TYPE_TEN, OnOffType.ON); + } + @ParameterizedTest @MethodSource("parameters") public void testOnCommandFromHandler(ParameterSet parameterSet) { @@ -211,14 +231,16 @@ public class SystemHysteresisStateProfileTest { } private StateProfile initProfile(@Nullable Object lower, @Nullable Object upper) { - return initProfile(lower, upper, false); + return initProfile(lower, upper, false, false); } - private StateProfile initProfile(@Nullable Object lower, @Nullable Object upper, boolean inverted) { + private StateProfile initProfile(@Nullable Object lower, @Nullable Object upper, boolean inverted, + boolean excludeItemState) { final Map properties = new HashMap<>(2); - properties.put("lower", lower); - properties.put("upper", upper); - properties.put("inverted", inverted); + properties.put(SystemHysteresisStateProfile.LOWER_PARAM, lower); + properties.put(SystemHysteresisStateProfile.UPPER_PARAM, upper); + properties.put(SystemHysteresisStateProfile.INVERTED_PARAM, inverted); + properties.put(SystemHysteresisStateProfile.EXCLUDE_ITEM_STATE_PARAM, excludeItemState); when(mockContext.getConfiguration()).thenReturn(new Configuration(properties)); return new SystemHysteresisStateProfile(mockCallback, mockContext); }