mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
[profiles] Added 'Range' profile (#2046)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
|
||||
import tec.uom.se.AbstractUnit;
|
||||
|
||||
/***
|
||||
* This is the default implementation for a {@link SystemHysteresisStateProfile}}.
|
||||
* This is the default implementation for a {@link SystemHysteresisStateProfile}.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
|
||||
+7
-5
@@ -64,13 +64,13 @@ public class SystemProfileFactory implements ProfileFactory, ProfileAdvisor, Pro
|
||||
private final ChannelTypeRegistry channelTypeRegistry;
|
||||
|
||||
private static final Set<ProfileType> SUPPORTED_PROFILE_TYPES = Set.of(DEFAULT_TYPE, FOLLOW_TYPE, HYSTERESIS_TYPE,
|
||||
OFFSET_TYPE, RAWBUTTON_ON_OFF_SWITCH_TYPE, RAWBUTTON_TOGGLE_PLAYER_TYPE, RAWBUTTON_TOGGLE_SWITCH_TYPE,
|
||||
RAWROCKER_DIMMER_TYPE, RAWROCKER_NEXT_PREVIOUS_TYPE, RAWROCKER_ON_OFF_TYPE, RAWROCKER_PLAY_PAUSE_TYPE,
|
||||
RAWROCKER_REWIND_FASTFORWARD_TYPE, RAWROCKER_STOP_MOVE_TYPE, RAWROCKER_UP_DOWN_TYPE, TIMESTAMP_CHANGE_TYPE,
|
||||
TIMESTAMP_UPDATE_TYPE);
|
||||
OFFSET_TYPE, RANGE_TYPE, RAWBUTTON_ON_OFF_SWITCH_TYPE, RAWBUTTON_TOGGLE_PLAYER_TYPE,
|
||||
RAWBUTTON_TOGGLE_SWITCH_TYPE, RAWROCKER_DIMMER_TYPE, RAWROCKER_NEXT_PREVIOUS_TYPE, RAWROCKER_ON_OFF_TYPE,
|
||||
RAWROCKER_PLAY_PAUSE_TYPE, RAWROCKER_REWIND_FASTFORWARD_TYPE, RAWROCKER_STOP_MOVE_TYPE,
|
||||
RAWROCKER_UP_DOWN_TYPE, TIMESTAMP_CHANGE_TYPE, TIMESTAMP_UPDATE_TYPE);
|
||||
|
||||
private static final Set<ProfileTypeUID> SUPPORTED_PROFILE_TYPE_UIDS = Set.of(DEFAULT, FOLLOW, HYSTERESIS, OFFSET,
|
||||
RAWBUTTON_ON_OFF_SWITCH, RAWBUTTON_TOGGLE_PLAYER, RAWBUTTON_TOGGLE_SWITCH, RAWROCKER_DIMMER,
|
||||
RANGE, RAWBUTTON_ON_OFF_SWITCH, RAWBUTTON_TOGGLE_PLAYER, RAWBUTTON_TOGGLE_SWITCH, RAWROCKER_DIMMER,
|
||||
RAWROCKER_NEXT_PREVIOUS, RAWROCKER_ON_OFF, RAWROCKER_PLAY_PAUSE, RAWROCKER_REWIND_FASTFORWARD,
|
||||
RAWROCKER_STOP_MOVE, RAWROCKER_UP_DOWN, TIMESTAMP_CHANGE, TIMESTAMP_UPDATE);
|
||||
|
||||
@@ -99,6 +99,8 @@ public class SystemProfileFactory implements ProfileFactory, ProfileAdvisor, Pro
|
||||
return new SystemHysteresisStateProfile(callback, context);
|
||||
} else if (OFFSET.equals(profileTypeUID)) {
|
||||
return new SystemOffsetProfile(callback, context);
|
||||
} else if (RANGE.equals(profileTypeUID)) {
|
||||
return new SystemRangeStateProfile(callback, context);
|
||||
} else if (RAWBUTTON_ON_OFF_SWITCH.equals(profileTypeUID)) {
|
||||
return new RawButtonOnOffSwitchProfile(callback);
|
||||
} else if (RAWBUTTON_TOGGLE_SWITCH.equals(profileTypeUID)) {
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.thing.internal.profiles;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||
import org.openhab.core.thing.profiles.ProfileContext;
|
||||
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
||||
import org.openhab.core.thing.profiles.StateProfile;
|
||||
import org.openhab.core.thing.profiles.SystemProfiles;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import tec.uom.se.AbstractUnit;
|
||||
|
||||
/***
|
||||
* This is the default implementation for a {@link SystemRangeStateProfile}.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SystemRangeStateProfile implements StateProfile {
|
||||
|
||||
static final String LOWER_PARAM = "lower";
|
||||
static final String UPPER_PARAM = "upper";
|
||||
static final String INVERTED_PARAM = "inverted";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SystemRangeStateProfile.class);
|
||||
|
||||
private final ProfileCallback callback;
|
||||
|
||||
private final QuantityType<?> lower;
|
||||
private final QuantityType<?> upper;
|
||||
private final OnOffType inRange;
|
||||
private final OnOffType notInRange;
|
||||
|
||||
private Type previousType = UnDefType.UNDEF;
|
||||
|
||||
public SystemRangeStateProfile(ProfileCallback callback, ProfileContext context) {
|
||||
this.callback = callback;
|
||||
|
||||
final QuantityType<?> lowerParam = getParam(context, LOWER_PARAM);
|
||||
if (lowerParam == null) {
|
||||
throw new IllegalArgumentException(String.format("Parameter '%s' is not a Number value.", LOWER_PARAM));
|
||||
}
|
||||
this.lower = lowerParam;
|
||||
final QuantityType<?> upperParam = getParam(context, UPPER_PARAM);
|
||||
if (upperParam == null) {
|
||||
throw new IllegalArgumentException(String.format("Parameter '%s' is not a Number value.", UPPER_PARAM));
|
||||
}
|
||||
final QuantityType<?> convertedUpperParam = upperParam.toUnit(lower.getUnit());
|
||||
if (convertedUpperParam == null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Units of parameters '%s' and '%s' are not compatible: %s != %s", LOWER_PARAM,
|
||||
UPPER_PARAM, lower, upperParam));
|
||||
}
|
||||
if (convertedUpperParam.doubleValue() <= lower.doubleValue()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Parameter '%s' (%s) is less than or equal to '%s' (%s) parameter.", UPPER_PARAM,
|
||||
convertedUpperParam, LOWER_PARAM, lower));
|
||||
}
|
||||
this.upper = convertedUpperParam;
|
||||
|
||||
final Object paramValue = context.getConfiguration().get(INVERTED_PARAM);
|
||||
final boolean inverted = paramValue == null ? false : Boolean.valueOf(paramValue.toString());
|
||||
this.inRange = inverted ? OnOffType.OFF : OnOffType.ON;
|
||||
this.notInRange = inverted ? OnOffType.ON : OnOffType.OFF;
|
||||
}
|
||||
|
||||
private @Nullable QuantityType<?> getParam(ProfileContext context, String param) {
|
||||
final Object paramValue = context.getConfiguration().get(param);
|
||||
logger.debug("Configuring profile with {} parameter '{}'", param, paramValue);
|
||||
if (paramValue instanceof String) {
|
||||
try {
|
||||
return new QuantityType<>((String) paramValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Cannot convert value '{}' of parameter {} into a valid QuantityType.", paramValue, param);
|
||||
}
|
||||
} else if (paramValue instanceof BigDecimal) {
|
||||
final BigDecimal value = (BigDecimal) paramValue;
|
||||
return QuantityType.valueOf(value.doubleValue(), AbstractUnit.ONE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileTypeUID getProfileTypeUID() {
|
||||
return SystemProfiles.RANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateUpdateFromItem(State state) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandFromHandler(Command command) {
|
||||
final Type mappedCommand = mapValue(command);
|
||||
logger.trace("Mapped command from '{}' to command '{}'.", command, mappedCommand);
|
||||
if (mappedCommand instanceof Command) {
|
||||
callback.sendCommand((Command) mappedCommand);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandFromItem(Command command) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateUpdateFromHandler(State state) {
|
||||
final Type mappedState = mapValue(state);
|
||||
logger.trace("Mapped state from '{}' to state '{}'.", state, mappedState);
|
||||
if (mappedState instanceof State) {
|
||||
callback.sendUpdate((State) mappedState);
|
||||
}
|
||||
}
|
||||
|
||||
private Type mapValue(Type value) {
|
||||
if (value instanceof QuantityType) {
|
||||
final QuantityType<?> qtState = (QuantityType<?>) value;
|
||||
final QuantityType<?> finalLower;
|
||||
final QuantityType<?> finalUpper;
|
||||
if (lower.getUnit() == Units.ONE && upper.getUnit() == Units.ONE) {
|
||||
// allow bounds without unit -> implicitly assume its the same as the one from the state, but warn
|
||||
// the user
|
||||
finalLower = new QuantityType<>(lower.toBigDecimal(), qtState.getUnit());
|
||||
finalUpper = new QuantityType<>(upper.toBigDecimal(), qtState.getUnit());
|
||||
logger.warn(
|
||||
"Received a QuantityType '{}' with unit, but the boundaries are defined as a plain number without units (lower={}, upper={}), please consider adding units to them.",
|
||||
value, lower, upper);
|
||||
} else {
|
||||
finalLower = lower.toUnit(qtState.getUnit());
|
||||
finalUpper = upper.toUnit(qtState.getUnit());
|
||||
if (finalLower == null || finalUpper == null) {
|
||||
logger.warn(
|
||||
"Cannot compare state '{}' to boundaries because units (lower={}, upper={}) do not match.",
|
||||
qtState, lower, upper);
|
||||
return previousType;
|
||||
}
|
||||
}
|
||||
return previousType = mapValue(finalLower.doubleValue(), finalUpper.doubleValue(), qtState.doubleValue());
|
||||
} else if (value instanceof DecimalType) {
|
||||
return previousType = mapValue(lower.doubleValue(), upper.doubleValue(),
|
||||
((DecimalType) value).doubleValue());
|
||||
}
|
||||
return previousType;
|
||||
}
|
||||
|
||||
private Type mapValue(double lower, double upper, double value) {
|
||||
return lower <= value && value <= upper ? inRange : notInRange;
|
||||
}
|
||||
}
|
||||
+7
@@ -30,6 +30,7 @@ public interface SystemProfiles {
|
||||
ProfileTypeUID FOLLOW = new ProfileTypeUID(SYSTEM_SCOPE, "follow");
|
||||
ProfileTypeUID OFFSET = new ProfileTypeUID(SYSTEM_SCOPE, "offset");
|
||||
ProfileTypeUID HYSTERESIS = new ProfileTypeUID(SYSTEM_SCOPE, "hysteresis");
|
||||
ProfileTypeUID RANGE = new ProfileTypeUID(SYSTEM_SCOPE, "range");
|
||||
ProfileTypeUID RAWBUTTON_ON_OFF_SWITCH = new ProfileTypeUID(SYSTEM_SCOPE, "rawbutton-on-off-switch");
|
||||
ProfileTypeUID RAWBUTTON_TOGGLE_PLAYER = new ProfileTypeUID(SYSTEM_SCOPE, "rawbutton-toggle-player");
|
||||
ProfileTypeUID RAWBUTTON_TOGGLE_ROLLERSHUTTER = new ProfileTypeUID(SYSTEM_SCOPE, "rawbutton-toggle-rollershutter");
|
||||
@@ -51,11 +52,17 @@ public interface SystemProfiles {
|
||||
StateProfileType OFFSET_TYPE = ProfileTypeBuilder.newState(OFFSET, "Offset")
|
||||
.withSupportedItemTypes(CoreItemFactory.NUMBER).withSupportedItemTypesOfChannel(CoreItemFactory.NUMBER)
|
||||
.build();
|
||||
|
||||
ProfileType HYSTERESIS_TYPE = ProfileTypeBuilder.newState(HYSTERESIS, "Hysteresis") //
|
||||
.withSupportedItemTypesOfChannel(CoreItemFactory.DIMMER, CoreItemFactory.NUMBER) //
|
||||
.withSupportedItemTypes(CoreItemFactory.SWITCH) //
|
||||
.build();
|
||||
|
||||
ProfileType RANGE_TYPE = ProfileTypeBuilder.newState(RANGE, "Range") //
|
||||
.withSupportedItemTypesOfChannel(CoreItemFactory.DIMMER, CoreItemFactory.NUMBER) //
|
||||
.withSupportedItemTypes(CoreItemFactory.SWITCH) //
|
||||
.build();
|
||||
|
||||
TriggerProfileType RAWBUTTON_ON_OFF_SWITCH_TYPE = ProfileTypeBuilder
|
||||
.newTrigger(RAWBUTTON_ON_OFF_SWITCH, "Raw Button To On Off")
|
||||
.withSupportedItemTypes(CoreItemFactory.SWITCH, CoreItemFactory.DIMMER, CoreItemFactory.COLOR)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="profile:system:range">
|
||||
<parameter name="lower" type="text" required="true">
|
||||
<label>Lower Bound</label>
|
||||
<description>Maps to ON if value is between lower and upper bound (plain number or number with unit).</description>
|
||||
</parameter>
|
||||
<parameter name="upper" type="text" required="true">
|
||||
<label>Upper Bound</label>
|
||||
<description>Maps to ON if value is between lower and upper bound (plain number or number with unit).</description>
|
||||
</parameter>
|
||||
<parameter name="inverted" type="boolean">
|
||||
<label>Inverted</label>
|
||||
<description>Inverts resulting mapping of ON / OFF, if true.</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
+11
-4
@@ -2,13 +2,20 @@ profile-type.system.default.label = Standard
|
||||
profile-type.system.follow.label = Folgen
|
||||
profile-type.system.offset.label = Versatz
|
||||
profile.config.system.offset.offset.label = Versatz
|
||||
profile.config.system.offset.offset.description = Versatz (Numerischer Wert oder numerischer Wert mit Einheit), welcher auf den Wert addiert wird. Ein negativer Versatz wird vom Wert abgezogen.
|
||||
profile.config.system.offset.offset.description = Versatz (numerischer Wert oder numerischer Wert mit Einheit), welcher auf den Wert addiert wird. Ein negativer Versatz wird vom Wert abgezogen.
|
||||
profile-type.system.hysteresis.label = Hysterese
|
||||
profile.config.system.hysteresis.lower.label = Untere Grenze
|
||||
profile.config.system.hysteresis.lower.description = Bildet bei Unterschreitung des unteren Grenzwertes OFF auf ein Switch Item ab (Numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.hysteresis.lower.description = Bildet bei Unterschreitung des unteren Grenzwertes OFF auf ein Switch Item ab (numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.hysteresis.upper.label = Obere Grenze
|
||||
profile.config.system.hysteresis.upper.description = Bildet bei Überschreitung des oberen Grenzwertes ON auf ein Switch Item ab (Numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.hysteresis.upper.description = Bildet bei Überschreitung des oberen Grenzwertes ON auf ein Switch Item ab (numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.hysteresis.inverted.label = Invertiert
|
||||
profile.config.system.hysteresis.inverted.description = Invertiert die resultierende Zuordnung von ON / OFF, falls true.
|
||||
profile-type.system.timestamp-change.label = Zeitstempel bei Änderung
|
||||
profile-type.system.range.label = Bereich
|
||||
profile.config.system.range.lower.label = Untere Grenze
|
||||
profile.config.system.range.lower.description = Bildet bei ON auf ein Switch Item ab, wenn der Wert zwischen der unteren und oberen Grenze liegt (numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.range.upper.label = Obere Grenze
|
||||
profile.config.system.range.upper.description = Bildet bei ON auf ein Switch Item ab, wenn der Wert zwischen der unteren und oberen Grenze liegt (numerischer Wert oder numerischer Wert mit Einheit).
|
||||
profile.config.system.range.inverted.label = Invertiert
|
||||
profile.config.system.range.inverted.description = Invertiert die resultierende Zuordnung von ON / OFF, falls true.
|
||||
profile-type.system.timestamp-change.label = Zeitstempel bei Änderung
|
||||
profile-type.system.timestamp-update.label = Zeitstempel bei Aktualisierung
|
||||
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.thing.internal.profiles;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.MockitoAnnotations.openMocks;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||
import org.openhab.core.thing.profiles.ProfileContext;
|
||||
import org.openhab.core.thing.profiles.StateProfile;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Basic unit tests for {@link SystemRangeStateProfileTest}.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class SystemRangeStateProfileTest {
|
||||
|
||||
private static final String STRING_FOURTY = "40";
|
||||
private static final String QUANTITY_STRING_TEN = "10 %";
|
||||
private static final String QUANTITY_STRING_FOURTY = "40 %";
|
||||
private static final BigDecimal BIGDECIMAL_FOURTY = new BigDecimal(STRING_FOURTY);
|
||||
private static final PercentType PERCENT_TYPE_TEN = new PercentType(BigDecimal.TEN);
|
||||
private static final PercentType PERCENT_TYPE_TWENTY_FIVE = new PercentType(BigDecimal.valueOf(25));
|
||||
|
||||
@NonNullByDefault
|
||||
public static class ParameterSet {
|
||||
public final List<State> states;
|
||||
public final List<State> resultingStates;
|
||||
public final List<Command> commands;
|
||||
public final List<@Nullable Command> resultingCommands;
|
||||
public final Object lower;
|
||||
public final @Nullable Object upper;
|
||||
public final boolean inverted;
|
||||
|
||||
public ParameterSet(List<? extends Type> sources, List<? extends Type> results, Object lower,
|
||||
@Nullable Object upper, boolean inverted) {
|
||||
this.states = (List<State>) sources;
|
||||
this.resultingStates = (List<State>) results;
|
||||
this.commands = (List<Command>) sources;
|
||||
this.resultingCommands = new ArrayList<>(results.size());
|
||||
results.forEach(result -> {
|
||||
resultingCommands.add(result instanceof Command ? (Command) result : null);
|
||||
});
|
||||
this.lower = lower;
|
||||
this.upper = upper;
|
||||
this.inverted = inverted;
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<Object[]> parameters() {
|
||||
return Arrays.asList(new Object[][] { //
|
||||
// lower bound = 10, upper bound = 40 (as BigDecimal), one state update / command (PercentType), not
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(PercentType.HUNDRED), List.of(OnOffType.OFF), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(PERCENT_TYPE_TWENTY_FIVE), List.of(OnOffType.ON), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(PERCENT_TYPE_TEN), List.of(OnOffType.ON), BigDecimal.TEN, BIGDECIMAL_FOURTY,
|
||||
false) }, //
|
||||
{ new ParameterSet(List.of(PercentType.ZERO), List.of(OnOffType.OFF), BigDecimal.TEN, BIGDECIMAL_FOURTY,
|
||||
false) }, //
|
||||
// lower bound = 10, upper bound = 40 (as BigDecimal), one state update / command (QuantityType), not
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("100 %")), List.of(OnOffType.OFF), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("25 %")), List.of(OnOffType.ON), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf(QUANTITY_STRING_TEN)), List.of(OnOffType.ON),
|
||||
BigDecimal.TEN, BIGDECIMAL_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("0 %")), List.of(OnOffType.OFF), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, false) }, //
|
||||
// lower bound = 10, upper bound = 40 (as QuantityType), one state update / command (QuantityType), not
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("100 %")), List.of(OnOffType.OFF), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("25 %")), List.of(OnOffType.ON), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf(QUANTITY_STRING_TEN)), List.of(OnOffType.ON),
|
||||
QUANTITY_STRING_TEN, QUANTITY_STRING_FOURTY, false) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("0 %")), List.of(OnOffType.OFF), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, false) }, //
|
||||
// lower bound = 10, upper bound = 40 (as QuantityType), one state update / command (QuantityType) ->
|
||||
// values
|
||||
// are converted to the same unit, not inverted
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("10 m")), List.of(OnOffType.OFF), "25 cm", "100cm",
|
||||
false) }, //
|
||||
// lower bound = upper bound = 10 (as QuantityType), one state update / command (QuantityType) ->
|
||||
// incompatible units cannot be compared
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("10 m")), List.of(UnDefType.UNDEF), "25 °C", "30 °C",
|
||||
false) }, //
|
||||
// lower bound = 10, upper bound = 40 (as BigDecimal), one state update / command (PercentType),
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(PercentType.HUNDRED), List.of(OnOffType.ON), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(PERCENT_TYPE_TWENTY_FIVE), List.of(OnOffType.OFF), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(PERCENT_TYPE_TEN), List.of(OnOffType.OFF), BigDecimal.TEN, BIGDECIMAL_FOURTY,
|
||||
true) }, //
|
||||
{ new ParameterSet(List.of(PercentType.ZERO), List.of(OnOffType.ON), BigDecimal.TEN, BIGDECIMAL_FOURTY,
|
||||
true) }, //
|
||||
// lower bound = 10, upper bound = 40 (as BigDecimal), one state update / command (QuantityType),
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("100 %")), List.of(OnOffType.ON), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("25 %")), List.of(OnOffType.OFF), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf(QUANTITY_STRING_TEN)), List.of(OnOffType.OFF),
|
||||
BigDecimal.TEN, BIGDECIMAL_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("0 %")), List.of(OnOffType.ON), BigDecimal.TEN,
|
||||
BIGDECIMAL_FOURTY, true) }, //
|
||||
// lower bound = 10, upper bound = 40 (as QuantityType), one state update / command (QuantityType),
|
||||
// inverted
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("100 %")), List.of(OnOffType.ON), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("25 %")), List.of(OnOffType.OFF), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf(QUANTITY_STRING_TEN)), List.of(OnOffType.OFF),
|
||||
QUANTITY_STRING_TEN, QUANTITY_STRING_FOURTY, true) }, //
|
||||
{ new ParameterSet(List.of(QuantityType.valueOf("0 %")), List.of(OnOffType.ON), QUANTITY_STRING_TEN,
|
||||
QUANTITY_STRING_FOURTY, true) }, //
|
||||
});
|
||||
}
|
||||
|
||||
private AutoCloseable mocksCloseable;
|
||||
|
||||
private @Mock ProfileCallback mockCallback;
|
||||
private @Mock ProfileContext mockContext;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
mocksCloseable = openMocks(this);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() throws Exception {
|
||||
mocksCloseable.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongParameterLower() {
|
||||
assertThrows(IllegalArgumentException.class, () -> initProfile(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongParameterUpper() {
|
||||
assertThrows(IllegalArgumentException.class, () -> initProfile(QUANTITY_STRING_TEN, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongParameterUnits() {
|
||||
assertThrows(IllegalArgumentException.class, () -> initProfile(QUANTITY_STRING_TEN, "5 °C"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongParameterUpperLessThanOrEqualsToLower() {
|
||||
assertThrows(IllegalArgumentException.class, () -> initProfile(QUANTITY_STRING_FOURTY, QUANTITY_STRING_TEN));
|
||||
assertThrows(IllegalArgumentException.class, () -> initProfile(QUANTITY_STRING_FOURTY, QUANTITY_STRING_FOURTY));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("parameters")
|
||||
public void testOnCommandFromHandler(ParameterSet parameterSet) {
|
||||
final StateProfile profile = initProfile(parameterSet.lower, parameterSet.upper, parameterSet.inverted);
|
||||
for (int i = 0; i < parameterSet.commands.size(); i++) {
|
||||
verifySendCommand(profile, parameterSet.commands.get(i), parameterSet.resultingCommands.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("parameters")
|
||||
public void testOnStateUpdateFromHandler(ParameterSet parameterSet) {
|
||||
final StateProfile profile = initProfile(parameterSet.lower, parameterSet.upper, parameterSet.inverted);
|
||||
for (int i = 0; i < parameterSet.states.size(); i++) {
|
||||
verifySendUpdate(profile, parameterSet.states.get(i), parameterSet.resultingStates.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private StateProfile initProfile(Object lower, Object upper) {
|
||||
return initProfile(lower, upper, false);
|
||||
}
|
||||
|
||||
private StateProfile initProfile(Object lower, Object upper, boolean inverted) {
|
||||
final Map<String, @Nullable Object> properties = new HashMap<>(2);
|
||||
properties.put(SystemRangeStateProfile.LOWER_PARAM, lower);
|
||||
properties.put(SystemRangeStateProfile.UPPER_PARAM, upper);
|
||||
properties.put(SystemRangeStateProfile.INVERTED_PARAM, inverted);
|
||||
when(mockContext.getConfiguration()).thenReturn(new Configuration(properties));
|
||||
return new SystemRangeStateProfile(mockCallback, mockContext);
|
||||
}
|
||||
|
||||
private void verifySendCommand(StateProfile profile, Command command, @Nullable Command expectedCommand) {
|
||||
reset(mockCallback);
|
||||
profile.onCommandFromHandler(command);
|
||||
Command eC = expectedCommand;
|
||||
if (eC == null) {
|
||||
verifyNoInteractions(mockCallback);
|
||||
} else {
|
||||
verify(mockCallback, times(1)).sendCommand(eq(eC));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifySendUpdate(StateProfile profile, State state, State expectedState) {
|
||||
reset(mockCallback);
|
||||
profile.onStateUpdateFromHandler(state);
|
||||
verify(mockCallback, times(1)).sendUpdate(eq(expectedState));
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -50,7 +50,8 @@ import org.openhab.core.thing.type.ChannelType;
|
||||
public class SystemProfileFactoryOSGiTest extends JavaOSGiTest {
|
||||
|
||||
private final Map<String, Object> properties = Map.of(SystemOffsetProfile.OFFSET_PARAM, BigDecimal.ZERO,
|
||||
SystemHysteresisStateProfile.LOWER_PARAM, BigDecimal.TEN);
|
||||
SystemHysteresisStateProfile.LOWER_PARAM, BigDecimal.TEN, SystemRangeStateProfile.UPPER_PARAM,
|
||||
BigDecimal.valueOf(40));
|
||||
|
||||
private SystemProfileFactory profileFactory;
|
||||
|
||||
@@ -68,7 +69,7 @@ public class SystemProfileFactoryOSGiTest extends JavaOSGiTest {
|
||||
@Test
|
||||
public void systemProfileTypesAndUidsShouldBeAvailable() {
|
||||
Collection<ProfileTypeUID> systemProfileTypeUIDs = profileFactory.getSupportedProfileTypeUIDs();
|
||||
assertEquals(16, systemProfileTypeUIDs.size());
|
||||
assertEquals(17, systemProfileTypeUIDs.size());
|
||||
|
||||
Collection<ProfileType> systemProfileTypes = profileFactory.getProfileTypes(null);
|
||||
assertEquals(systemProfileTypeUIDs.size(), systemProfileTypes.size());
|
||||
|
||||
Reference in New Issue
Block a user