[http] enable UoM for number channels (#9601)

* add unit

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>

* documentation an XML

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>

* address review comments

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>

* improvements

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>

* improvements

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
J-N-K 2021-01-06 22:12:31 +01:00 committed by GitHub
parent cb5d659c9e
commit 1480c41606
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 166 additions and 13 deletions

View File

@ -101,6 +101,16 @@ All values that are not `onValue`, `offValue`, `increaseValue`, `decreaseValue`
All values that are not `onValue`, `offValue`, `increaseValue`, `decreaseValue` are interpreted as brightness 0-100% and need to be numeric only. All values that are not `onValue`, `offValue`, `increaseValue`, `decreaseValue` are interpreted as brightness 0-100% and need to be numeric only.
### `number`
| parameter | optional | default | description |
|-------------------------|----------|-------------|-------------|
| `unit` | yes | - | The unit label for this channel |
`number` channels can be used for `DecimalType` or `QuantityType` values.
If a unit is given in the `unit` parameter, the binding tries to create a `QuantityType` state before updating the channel, if no unit is present, it creates a `DecimalType`.
Please note that incompatible units (e.g. `°C` for a `Number:Density` item) will fail silently, i.e. no error message is logged even if the state update fails.
### `player` ### `player`
| parameter | optional | default | description | | parameter | optional | default | description |

View File

@ -33,19 +33,10 @@ import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.http.internal.config.HttpChannelConfig; import org.openhab.binding.http.internal.config.HttpChannelConfig;
import org.openhab.binding.http.internal.config.HttpChannelMode; import org.openhab.binding.http.internal.config.HttpChannelMode;
import org.openhab.binding.http.internal.config.HttpThingConfig; import org.openhab.binding.http.internal.config.HttpThingConfig;
import org.openhab.binding.http.internal.converter.AbstractTransformingItemConverter; import org.openhab.binding.http.internal.converter.*;
import org.openhab.binding.http.internal.converter.ColorItemConverter;
import org.openhab.binding.http.internal.converter.DimmerItemConverter;
import org.openhab.binding.http.internal.converter.FixedValueMappingItemConverter;
import org.openhab.binding.http.internal.converter.GenericItemConverter;
import org.openhab.binding.http.internal.converter.ImageItemConverter;
import org.openhab.binding.http.internal.converter.ItemValueConverter;
import org.openhab.binding.http.internal.converter.PlayerItemConverter;
import org.openhab.binding.http.internal.converter.RollershutterItemConverter;
import org.openhab.binding.http.internal.http.*; import org.openhab.binding.http.internal.http.*;
import org.openhab.binding.http.internal.transform.ValueTransformationProvider; import org.openhab.binding.http.internal.transform.ValueTransformationProvider;
import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
@ -260,8 +251,8 @@ public class HttpThingHandler extends BaseThingHandler {
itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, PointType::new); itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, PointType::new);
break; break;
case "Number": case "Number":
itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, itemValueConverter = createItemConverter(NumberItemConverter::new, commandUrl, channelUID,
DecimalType::new); channelConfig);
break; break;
case "Player": case "Player":
itemValueConverter = createItemConverter(PlayerItemConverter::new, commandUrl, channelUID, itemValueConverter = createItemConverter(PlayerItemConverter::new, commandUrl, channelUID,

View File

@ -48,6 +48,9 @@ public class HttpChannelConfig {
public HttpChannelMode mode = HttpChannelMode.READWRITE; public HttpChannelMode mode = HttpChannelMode.READWRITE;
// number
public @Nullable String unit;
// switch, dimmer, color // switch, dimmer, color
public @Nullable String onValue; public @Nullable String onValue;
public @Nullable String offValue; public @Nullable String offValue;

View File

@ -0,0 +1,74 @@
/**
* 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.binding.http.internal.converter;
import java.util.function.Consumer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.http.internal.config.HttpChannelConfig;
import org.openhab.binding.http.internal.transform.ValueTransformation;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* The {@link NumberItemConverter} implements {@link org.openhab.core.library.items.NumberItem} conversions
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class NumberItemConverter extends AbstractTransformingItemConverter {
public NumberItemConverter(Consumer<State> updateState, Consumer<Command> postCommand,
@Nullable Consumer<String> sendHttpValue, ValueTransformation stateTransformations,
ValueTransformation commandTransformations, HttpChannelConfig channelConfig) {
super(updateState, postCommand, sendHttpValue, stateTransformations, commandTransformations, channelConfig);
}
@Override
protected @Nullable Command toCommand(String value) {
return null;
}
@Override
protected State toState(String value) {
String trimmedValue = value.trim();
if (!trimmedValue.isEmpty()) {
try {
if (channelConfig.unit != null) {
// we have a given unit - use that
return new QuantityType<>(trimmedValue + " " + channelConfig.unit);
} else {
try {
// try if we have a simple number
return new DecimalType(trimmedValue);
} catch (IllegalArgumentException e1) {
// not a plain number, maybe with unit?
return new QuantityType<>(trimmedValue);
}
}
} catch (IllegalArgumentException e) {
// finally failed
}
}
return UnDefType.UNDEF;
}
@Override
protected String toString(Command command) {
return command.toString();
}
}

View File

@ -202,6 +202,43 @@
</parameter> </parameter>
</config-description> </config-description>
<config-description uri="channel-type:http:channel-config-number">
<parameter name="stateExtension" type="text">
<label>State URL Extension</label>
<description>This value is added to the base URL configured in the thing for retrieving values.</description>
<advanced>true</advanced>
</parameter>
<parameter name="commandExtension" type="text">
<label>Command URL Extension</label>
<description>This value is added to the base URL configured in the thing for sending values.</description>
<advanced>true</advanced>
</parameter>
<parameter name="stateTransformation" type="text">
<label>State Transformation</label>
<description>Transformation pattern used when receiving values.</description>
</parameter>
<parameter name="commandTransformation" type="text">
<label>Command Transformation</label>
<description>Transformation pattern used when sending values.</description>
</parameter>
<parameter name="mode" type="text">
<label>Read/Write Mode</label>
<options>
<option value="READWRITE">Read/Write</option>
<option value="READONLY">Read Only</option>
<option value="WRITEONLY">Write Only</option>
</options>
<limitToOptions>true</limitToOptions>
<advanced>true</advanced>
<default>READWRITE</default>
</parameter>
<parameter name="unit" type="text">
<label>Unit</label>
<description>Unit to append to the (transformed) value.</description>
<advanced>true</advanced>
</parameter>
</config-description>
<config-description uri="channel-type:http:channel-config-player"> <config-description uri="channel-type:http:channel-config-player">
<parameter name="stateExtension" type="text"> <parameter name="stateExtension" type="text">
<label>State URL Extension</label> <label>State URL Extension</label>

View File

@ -142,7 +142,7 @@
<channel-type id="number"> <channel-type id="number">
<item-type>Number</item-type> <item-type>Number</item-type>
<label>Number Channel</label> <label>Number Channel</label>
<config-description-ref uri="channel-type:http:channel-config"/> <config-description-ref uri="channel-type:http:channel-config-number"/>
</channel-type> </channel-type>
<channel-type id="player"> <channel-type id="player">

View File

@ -21,9 +21,13 @@ import org.openhab.binding.http.internal.config.HttpChannelConfig;
import org.openhab.binding.http.internal.transform.NoOpValueTransformation; import org.openhab.binding.http.internal.transform.NoOpValueTransformation;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.State; import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/** /**
* The {@link ConverterTest} is a test class for state converters * The {@link ConverterTest} is a test class for state converters
@ -33,6 +37,40 @@ import org.openhab.core.types.State;
@NonNullByDefault @NonNullByDefault
public class ConverterTest { public class ConverterTest {
@Test
public void numberItemConverter() {
NumberItemConverter converter = new NumberItemConverter(this::updateState, this::postCommand,
this::sendHttpValue, NoOpValueTransformation.getInstance(), NoOpValueTransformation.getInstance(),
new HttpChannelConfig());
// without unit
Assertions.assertEquals(new DecimalType(1234), converter.toState("1234"));
// unit in transformation result
Assertions.assertEquals(new QuantityType<>(100, SIUnits.CELSIUS), converter.toState("100°C"));
// no valid value
Assertions.assertEquals(UnDefType.UNDEF, converter.toState("W"));
Assertions.assertEquals(UnDefType.UNDEF, converter.toState(""));
}
@Test
public void numberItemConverterWithUnit() {
HttpChannelConfig channelConfig = new HttpChannelConfig();
channelConfig.unit = "W";
NumberItemConverter converter = new NumberItemConverter(this::updateState, this::postCommand,
this::sendHttpValue, NoOpValueTransformation.getInstance(), NoOpValueTransformation.getInstance(),
channelConfig);
// without unit
Assertions.assertEquals(new QuantityType<>(500, Units.WATT), converter.toState("500"));
// no valid value
Assertions.assertEquals(UnDefType.UNDEF, converter.toState("100°C"));
Assertions.assertEquals(UnDefType.UNDEF, converter.toState("foo"));
Assertions.assertEquals(UnDefType.UNDEF, converter.toState(""));
}
@Test @Test
public void stringTypeConverter() { public void stringTypeConverter() {
GenericItemConverter converter = createConverter(StringType::new); GenericItemConverter converter = createConverter(StringType::new);