Skip to content

Commit

Permalink
Create dynamic channels for writable numeric parameters (openhab#3)
Browse files Browse the repository at this point in the history
* Create dynamic channels for writable numeric parameters

Signed-off-by: Anders Alfredsson <[email protected]>
  • Loading branch information
pacive authored and alexf2015 committed Jul 25, 2024
1 parent 956d23b commit c617610
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class MyUplinkBindingConstants {
public static final String CHANNEL_TYPE_UNIT_NONE = "NO_UNIT";
public static final String CHANNEL_TYPE_PREFIX_RW = "rw";
public static final String CHANNEL_TYPE_ENUM_PRFIX = "type-enum-";
public static final String CHANNEL_TYPE_NUMERIC_PRFIX = "type-numeric-";
public static final String CHANNEL_TYPE_DEFAULT_DATATYPE = "Number";

public static final String CHANNEL_TYPE_ENERGY = "type-energy";
Expand Down Expand Up @@ -80,6 +81,9 @@ public class MyUplinkBindingConstants {
public static final String JSON_KEY_CHANNEL_LABEL = "parameterName";
public static final String JSON_KEY_CHANNEL_UNIT = "parameterUnit";
public static final String JSON_KEY_CHANNEL_SCALE = "scaleValue";
public static final String JSON_KEY_CHANNEL_MIN = "minValue";
public static final String JSON_KEY_CHANNEL_MAX = "maxValue";
public static final String JSON_KEY_CHANNEL_STEP = "stepValue";
public static final String JSON_KEY_SYSTEMS = "systems";
public static final String JSON_KEY_DEVICES = "devices";
public static final String JSON_KEY_GENERIC_ID = "id";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static org.openhab.binding.myuplink.internal.MyUplinkBindingConstants.*;

import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
Expand Down Expand Up @@ -133,6 +134,18 @@ public static int getAsInt(@Nullable JsonObject jsonObject, String key) {
return (element instanceof JsonPrimitive) ? element.getAsInt() : 0;
}

/**
* get element as BigDecimal.
*
* @param jsonObject
* @param key
* @return
*/
public static @Nullable BigDecimal getAsBigDecimal(@Nullable JsonObject jsonObject, String key) {
JsonElement element = jsonObject == null ? null : jsonObject.get(key);
return (element == null || element instanceof JsonNull) ? null : element.getAsBigDecimal();
}

/**
* get element as boolean.
*
Expand Down Expand Up @@ -230,4 +243,12 @@ public static String capitalize(final String str) {
}
return new String(buffer);
}

public static String fixUnit(String originalUnit) {
return switch (originalUnit) {
case "l/m" -> "l/min";
case "hrs" -> "h";
default -> originalUnit;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Channel;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -65,7 +63,9 @@ public Map<Channel, State> transform(JsonObject jsonData, String group) {

logger.debug("received channel data: {}", channelData.toString());

var value = Utils.getAsString(channelData.getAsJsonObject(), JSON_KEY_CHANNEL_VALUE);
var value = Utils.getAsBigDecimal(channelData.getAsJsonObject(), JSON_KEY_CHANNEL_VALUE);
var unit = UnitUtils.parseUnit(
Utils.fixUnit(Utils.getAsString(channelData.getAsJsonObject(), JSON_KEY_CHANNEL_UNIT, "")));
var channelId = Utils.getAsString(channelData.getAsJsonObject(), JSON_KEY_CHANNEL_ID, GENERIC_NO_VAL);

Channel channel;
Expand All @@ -87,30 +87,16 @@ public Map<Channel, State> transform(JsonObject jsonData, String group) {
} else {
try {
var channelTypeId = Utils.getChannelTypeId(channel);
var newState = switch (channelTypeId) {
case CHANNEL_TYPE_ENERGY ->
new QuantityType<>(Double.parseDouble(value), MetricPrefix.KILO(Units.WATT_HOUR));
case CHANNEL_TYPE_PRESSURE -> new QuantityType<>(Double.parseDouble(value), Units.BAR);
case CHANNEL_TYPE_PERCENT -> new QuantityType<>(Double.parseDouble(value), Units.PERCENT);
case CHANNEL_TYPE_TEMPERATURE ->
new QuantityType<>(Double.parseDouble(value), SIUnits.CELSIUS);
case CHANNEL_TYPE_FREQUENCY -> new QuantityType<>(Double.parseDouble(value), Units.HERTZ);
case CHANNEL_TYPE_FLOW ->
new QuantityType<>(Double.parseDouble(value), Units.LITRE.divide(Units.MINUTE));
case CHANNEL_TYPE_ELECTRIC_CURRENT ->
new QuantityType<>(Double.parseDouble(value), Units.AMPERE);
case CHANNEL_TYPE_TIME -> new QuantityType<>(Double.parseDouble(value), Units.HOUR);
case CHANNEL_TYPE_INTEGER -> new DecimalType(Double.valueOf(value).longValue());
case CHANNEL_TYPE_DOUBLE -> new DecimalType(Double.parseDouble(value));
case CHANNEL_TYPE_ON_OFF -> new DecimalType(Double.valueOf(value).longValue());
case CHANNEL_TYPE_RW_SWITCH -> convertToOnOffType(value);
case CHANNEL_TYPE_RW_COMMAND -> new StringType(value);

default -> channelTypeId.startsWith(CHANNEL_TYPE_ENUM_PRFIX)
|| channelTypeId.startsWith(CHANNEL_TYPE_PREFIX_RW + CHANNEL_TYPE_ENUM_PRFIX)
? new DecimalType(Double.valueOf(value).longValue())
: UnDefType.NULL;
};
State newState;
if (channelTypeId.equals(CHANNEL_TYPE_RW_SWITCH)) {
newState = convertToOnOffType(value.stripTrailingZeros().toString());
} else if (channelTypeId.equals(CHANNEL_TYPE_RW_COMMAND)) {
newState = new StringType(value.toString());
} else if (unit != null) {
newState = new QuantityType<>(value, unit);
} else {
newState = new DecimalType(value.stripTrailingZeros());
}

if (newState == UnDefType.NULL) {
logger.warn("no mapping implemented for channel type '{}'", channelTypeId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static org.openhab.binding.myuplink.internal.MyUplinkBindingConstants.*;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -66,14 +67,21 @@ public ChannelFactory(@Reference MyUplinkChannelTypeProvider channelTypeProvider
public Channel createChannel(ThingUID thingUID, JsonObject channelData) {
final var channelId = Utils.getAsString(channelData, JSON_KEY_CHANNEL_ID, GENERIC_NO_VAL);
final var label = Utils.getAsString(channelData, JSON_KEY_CHANNEL_LABEL, GENERIC_NO_VAL);
final var unit = Utils.getAsString(channelData, JSON_KEY_CHANNEL_UNIT, GENERIC_NO_VAL);
final var unit = Utils.fixUnit(Utils.getAsString(channelData, JSON_KEY_CHANNEL_UNIT, ""));
final var strVal = Utils.getAsString(channelData, JSON_KEY_CHANNEL_STR_VAL, GENERIC_NO_VAL);
final var writable = Utils.getAsBool(channelData, JSON_KEY_CHANNEL_WRITABLE, Boolean.FALSE);
final var enumValues = Utils.getAsJsonArray(channelData, JSON_KEY_CHANNEL_ENUM_VALUES);
final var minValue = Utils.getAsBigDecimal(channelData, JSON_KEY_CHANNEL_MIN);
final var maxValue = Utils.getAsBigDecimal(channelData, JSON_KEY_CHANNEL_MAX);
final var stepValue = Utils.getAsBigDecimal(channelData, JSON_KEY_CHANNEL_STEP);

ChannelTypeUID channelTypeUID = null;
if (enumValues.isEmpty()) {
channelTypeUID = determineStaticChannelTypeUID(unit, strVal.contains(JSON_VAL_DECIMAL_SEPARATOR));
if (!writable) {
channelTypeUID = determineStaticChannelTypeUID(unit, strVal.contains(JSON_VAL_DECIMAL_SEPARATOR));
} else {
channelTypeUID = getOrBuildNumberChannelType(channelId, unit, minValue, maxValue, stepValue);
}
} else {
channelTypeUID = determineEnumChannelTypeUID(channelId, enumValues, writable);
}
Expand Down Expand Up @@ -152,6 +160,38 @@ private ChannelTypeUID getOrBuildDynamicEnumType(String channelId, JsonArray enu
return channelTypeUID;
}

private ChannelTypeUID getOrBuildNumberChannelType(String channelId, String unit, @Nullable BigDecimal min,
@Nullable BigDecimal max, @Nullable BigDecimal step) {
final var channelTypeUID = new ChannelTypeUID(BINDING_ID,
CHANNEL_TYPE_PREFIX_RW + CHANNEL_TYPE_NUMERIC_PRFIX + channelId);
var type = channelTypeRegistry.getChannelType(channelTypeUID);

if (type == null) {
var stateBuilder = StateDescriptionFragmentBuilder.create().withReadOnly(false);

if (min != null) {
stateBuilder.withMinimum(min);
}
if (max != null) {
stateBuilder.withMaximum(max);
}
if (step != null) {
stateBuilder.withStep(step);
}

var itemType = determineAcceptedType(channelTypeUID, unit);
var typeBuilder = ChannelTypeBuilder.state(channelTypeUID, channelId, itemType)
.withStateDescriptionFragment(stateBuilder.build());
if (!itemType.equals(CoreItemFactory.NUMBER)) {
typeBuilder.withUnitHint(unit);
}

channelTypeProvider.putChannelType(typeBuilder.build());
}

return channelTypeUID;
}

List<StateOption> extractEnumValues(JsonArray enumValues) {
List<StateOption> list = new ArrayList<>();
for (var element : enumValues) {
Expand Down

0 comments on commit c617610

Please sign in to comment.