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`
| parameter | optional | default | description |
import org.openhab.binding.http.internal.config.HttpChannelConfig;
import org.openhab.binding.http.internal.config.HttpChannelMode;
import org.openhab.binding.http.internal.config.HttpThingConfig;
-import org.openhab.binding.http.internal.converter.AbstractTransformingItemConverter;
-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.converter.*;
import org.openhab.binding.http.internal.http.*;
import org.openhab.binding.http.internal.transform.ValueTransformationProvider;
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.StringType;
import org.openhab.core.thing.Channel;
itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, PointType::new);
break;
case "Number":
- itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig,
- DecimalType::new);
+ itemValueConverter = createItemConverter(NumberItemConverter::new, commandUrl, channelUID,
+ channelConfig);
break;
case "Player":
itemValueConverter = createItemConverter(PlayerItemConverter::new, commandUrl, channelUID,
public HttpChannelMode mode = HttpChannelMode.READWRITE;
+ // number
+ public @Nullable String unit;
+
// switch, dimmer, color
public @Nullable String onValue;
public @Nullable String offValue;
--- /dev/null
+/**
+ * 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();
+ }
+}
</parameter>
</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">
<parameter name="stateExtension" type="text">
<label>State URL Extension</label>
<channel-type id="number">
<item-type>Number</item-type>
<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 id="player">
import org.openhab.binding.http.internal.transform.NoOpValueTransformation;
import org.openhab.core.library.types.DecimalType;
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.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
/**
* The {@link ConverterTest} is a test class for state converters
@NonNullByDefault
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
public void stringTypeConverter() {
GenericItemConverter converter = createConverter(StringType::new);