Skip to content

Commit

Permalink
Add support for QuantityType
Browse files Browse the repository at this point in the history
Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo committed Oct 3, 2024
1 parent b20bf5d commit c6d8c3c
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.core.dto.ConfigDescriptionDTOMapper;
import org.openhab.core.config.core.dto.ConfigDescriptionParameterDTO;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.io.rest.LocaleService;
import org.openhab.core.io.rest.RESTConstants;
import org.openhab.core.io.rest.RESTResource;
Expand Down Expand Up @@ -99,15 +100,17 @@ public class ThingActionsResource implements RESTResource {
private final Logger logger = LoggerFactory.getLogger(ThingActionsResource.class);

private final LocaleService localeService;
private final UnitProvider unitProvider;
private final ModuleTypeRegistry moduleTypeRegistry;

Map<ThingUID, Map<String, ThingActions>> thingActionsMap = new ConcurrentHashMap<>();
private List<ModuleHandlerFactory> moduleHandlerFactories = new ArrayList<>();

@Activate
public ThingActionsResource(@Reference LocaleService localeService,
public ThingActionsResource(@Reference LocaleService localeService, @Reference UnitProvider unitProvider,
@Reference ModuleTypeRegistry moduleTypeRegistry) {
this.localeService = localeService;
this.unitProvider = unitProvider;
this.moduleTypeRegistry = moduleTypeRegistry;
}

Expand Down Expand Up @@ -181,7 +184,7 @@ public Response getActions(@PathParam("thingUID") @Parameter(description = "thin
}

List<ConfigDescriptionParameter> inputParameters = ActionInputsToConfigDescriptionParameters
.map(actionType.getInputs());
.map(actionType.getInputs(), unitProvider);
if (inputParameters == null) {
logger.info("Thing action {} has an input with an unsupported type, hiding it in the UI.",
actionType.getUID());
Expand Down Expand Up @@ -240,7 +243,8 @@ public Response executeThingAction(@PathParam("thingUID") @Parameter(description

try {
Map<String, Object> returnValue = Objects.requireNonNullElse(
handler.execute(SerialisedInputsToActionInputs.map(actionType, actionInputs)), Map.of());
handler.execute(SerialisedInputsToActionInputs.map(actionType, actionInputs, unitProvider)),
Map.of());
moduleHandlerFactory.ungetHandler(action, ruleUID, handler);
return Response.ok(returnValue).build();
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public AnnotationActionHandler(Action module, ActionType mt, Method method, Obje
// fallback to configuration as this is where the UI stores the input values
if (value == null) {
value = SerialisedInputsToActionInputs.map(moduleType, moduleType.getInputs().get(i),
module.getConfiguration().get(inputAnnotation.name()));
module.getConfiguration().get(inputAnnotation.name()), null);
}
args.add(i, value);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.i18n.UnitProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -146,6 +147,11 @@ private List<Output> getOutputsFromAction(Method method) {
}

public @Nullable ActionType buildModuleType(String uid, Map<String, Set<ModuleInformation>> moduleInformation) {
return buildModuleType(uid, moduleInformation, null);
}

public @Nullable ActionType buildModuleType(String uid, Map<String, Set<ModuleInformation>> moduleInformation,
@Nullable UnitProvider unitProvider) {
Set<ModuleInformation> mis = moduleInformation.get(uid);
List<ConfigDescriptionParameter> configDescriptions = new ArrayList<>();

Expand Down Expand Up @@ -174,7 +180,7 @@ private List<Output> getOutputsFromAction(Method method) {
if (kind == ActionModuleKind.THING) {
// we have a Thing module, so we have to map the inputs to config description parameters for the UI
List<ConfigDescriptionParameter> inputConfigDescriptions = ActionInputsToConfigDescriptionParameters
.map(mi.getInputs());
.map(mi.getInputs(), unitProvider);
if (inputConfigDescriptions != null) {
// all inputs have a supported type
configDescriptions.addAll(inputConfigDescriptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.openhab.core.automation.type.ModuleType;
import org.openhab.core.automation.type.ModuleTypeProvider;
import org.openhab.core.common.registry.ProviderChangeListener;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
Expand Down Expand Up @@ -66,10 +67,13 @@ public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFac
private final AnnotationActionModuleTypeHelper helper = new AnnotationActionModuleTypeHelper();

private final ModuleTypeI18nService moduleTypeI18nService;
private final UnitProvider unitProvider;

@Activate
public AnnotatedThingActionModuleTypeProvider(final @Reference ModuleTypeI18nService moduleTypeI18nService) {
public AnnotatedThingActionModuleTypeProvider(final @Reference ModuleTypeI18nService moduleTypeI18nService,
final @Reference UnitProvider unitProvider) {
this.moduleTypeI18nService = moduleTypeI18nService;
this.unitProvider = unitProvider;
}

@Override
Expand All @@ -92,7 +96,7 @@ public void removeProviderChangeListener(ProviderChangeListener<ModuleType> list
public Collection<ModuleType> getAll() {
Collection<ModuleType> moduleTypes = new ArrayList<>();
for (String moduleUID : moduleInformation.keySet()) {
ModuleType mt = helper.buildModuleType(moduleUID, moduleInformation);
ModuleType mt = helper.buildModuleType(moduleUID, moduleInformation, unitProvider);
if (mt != null) {
moduleTypes.add(mt);
}
Expand Down Expand Up @@ -125,7 +129,7 @@ public <T extends ModuleType> Collection<T> getModuleTypes(@Nullable Locale loca
ModuleInformation mi = mis.iterator().next();

Bundle bundle = FrameworkUtil.getBundle(mi.getActionProvider().getClass());
ModuleType mt = helper.buildModuleType(uid, moduleInformation);
ModuleType mt = helper.buildModuleType(uid, moduleInformation, unitProvider);
return moduleTypeI18nService.getModuleTypePerLocale(mt, locale, bundle);
}
return null;
Expand All @@ -145,7 +149,7 @@ public void addAnnotatedThingActions(ThingActions annotatedThingActions) {

ModuleType oldType = null;
if (moduleInformation.containsKey(mi.getUID())) {
oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
oldType = helper.buildModuleType(mi.getUID(), moduleInformation, unitProvider);
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
availableModuleConfigs.add(mi);
} else {
Expand All @@ -154,7 +158,7 @@ public void addAnnotatedThingActions(ThingActions annotatedThingActions) {
moduleInformation.put(mi.getUID(), configs);
}

ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation, unitProvider);
if (mt != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
Expand Down Expand Up @@ -184,14 +188,14 @@ public void removeAnnotatedThingActions(ThingActions annotatedThingActions) {

Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
if (availableModuleConfigs != null) {
ModuleType oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
ModuleType oldType = helper.buildModuleType(mi.getUID(), moduleInformation, unitProvider);
if (availableModuleConfigs.size() > 1) {
availableModuleConfigs.remove(mi);
} else {
moduleInformation.remove(mi.getUID());
}

ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation, unitProvider);
// localize moduletype -> remove from map
if (oldType != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
Expand Down Expand Up @@ -231,7 +235,8 @@ public Collection<String> getTypes() {
ModuleInformation finalMI = helper.getModuleInformationForIdentifier(actionModule, moduleInformation,
true);
if (finalMI != null) {
ActionType moduleType = helper.buildModuleType(module.getTypeUID(), moduleInformation);
ActionType moduleType = helper.buildModuleType(module.getTypeUID(), moduleInformation,
unitProvider);
if (moduleType == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.measure.Quantity;
import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.type.Input;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -32,17 +39,21 @@
public class ActionInputsToConfigDescriptionParameters {
private static final Logger LOGGER = LoggerFactory.getLogger(ActionInputsToConfigDescriptionParameters.class);

public static final Pattern QUANTITY_TYPE_PATTERN = Pattern
.compile("(.+\\.)?QuantityType<(.+\\.)?(?<dimension>\\w+)>");

/**
* Maps a list of {@link Input}s to a list of {@link ConfigDescriptionParameter}s.
*
* @param inputs the list of inputs to map to config description parameters
* @return the list of config description parameters or null if an input parameter has an unsupported type
*/
public static @Nullable List<ConfigDescriptionParameter> map(List<Input> inputs) {
public static @Nullable List<ConfigDescriptionParameter> map(List<Input> inputs,
@Nullable UnitProvider unitProvider) {
List<ConfigDescriptionParameter> configDescriptionParameters = new ArrayList<>();

for (Input input : inputs) {
ConfigDescriptionParameter parameter = ActionInputsToConfigDescriptionParameters.map(input);
ConfigDescriptionParameter parameter = ActionInputsToConfigDescriptionParameters.map(input, unitProvider);
if (parameter != null) {
configDescriptionParameters.add(parameter);
} else {
Expand All @@ -62,59 +73,68 @@ public class ActionInputsToConfigDescriptionParameters {
* @param input the input to map to a config description parameter
* @return the config description parameter or null if the input parameter has an unsupported type
*/
public static @Nullable ConfigDescriptionParameter map(Input input) {
public static @Nullable ConfigDescriptionParameter map(Input input, @Nullable UnitProvider unitProvider) {
boolean supported = true;
ConfigDescriptionParameter.Type parameterType = ConfigDescriptionParameter.Type.TEXT;
String defaultValue = null;
Unit<?> unit = null;
boolean required = false;
String context = null;
switch (input.getType()) {
case "boolean":
defaultValue = "false";
required = true;
case "java.lang.Boolean":
parameterType = ConfigDescriptionParameter.Type.BOOLEAN;
break;
case "byte":
case "short":
case "int":
case "long":
defaultValue = "0";
required = true;
case "java.lang.Byte":
case "java.lang.Short":
case "java.lang.Integer":
case "java.lang.Long":
parameterType = ConfigDescriptionParameter.Type.INTEGER;
break;
case "float":
case "double":
defaultValue = "0";
required = true;
case "java.lang.Float":
case "java.lang.Double":
parameterType = ConfigDescriptionParameter.Type.DECIMAL;
break;
case "java.lang.String":
break;
case "java.time.LocalDate":
context = "date";
break;
case "java.time.LocalTime":
context = "time";
break;
case "java.time.LocalDateTime":
case "java.time.ZonedDateTime":
context = "datetime";
break;
case "org.openhab.core.library.types.DecimalType":
parameterType = ConfigDescriptionParameter.Type.DECIMAL;
break;
case "org.openhab.core.library.types.QuantityType":
break;
default:
Matcher matcher = QUANTITY_TYPE_PATTERN.matcher(input.getType());
if (matcher.matches() && unitProvider != null) {
parameterType = ConfigDescriptionParameter.Type.DECIMAL;
try {
unit = getDefaultUnit(matcher.group("dimension"), unitProvider);
} catch (IllegalArgumentException e) {
supported = false;
break;
}
} else {
switch (input.getType()) {
case "boolean":
defaultValue = "false";
required = true;
case "java.lang.Boolean":
parameterType = ConfigDescriptionParameter.Type.BOOLEAN;
break;
case "byte":
case "short":
case "int":
case "long":
defaultValue = "0";
required = true;
case "java.lang.Byte":
case "java.lang.Short":
case "java.lang.Integer":
case "java.lang.Long":
parameterType = ConfigDescriptionParameter.Type.INTEGER;
break;
case "float":
case "double":
defaultValue = "0";
required = true;
case "java.lang.Float":
case "java.lang.Double":
parameterType = ConfigDescriptionParameter.Type.DECIMAL;
break;
case "java.lang.String":
break;
case "java.time.LocalDate":
context = "date";
break;
case "java.time.LocalTime":
context = "time";
break;
case "java.time.LocalDateTime":
case "java.time.ZonedDateTime":
context = "datetime";
break;
case "org.openhab.core.library.types.DecimalType":
parameterType = ConfigDescriptionParameter.Type.DECIMAL;
break;
default:
supported = false;
break;
}
}
if (!supported) {
LOGGER.debug("Input parameter '{}' with type {} cannot be converted into a config description parameter!",
Expand All @@ -131,6 +151,18 @@ public class ActionInputsToConfigDescriptionParameters {
} else if (defaultValue != null) {
builder = builder.withDefault(defaultValue);
}
if (unit != null) {
builder = builder.withUnitLabel(unit.getSymbol());
}
return builder.build();
}

public static Unit<?> getDefaultUnit(String dimensionName, UnitProvider unitProvider)
throws IllegalArgumentException {
Class<? extends Quantity<?>> dimension = UnitUtils.parseDimension(dimensionName);
if (dimension == null) {
throw new IllegalArgumentException("Unknwon dimension " + dimensionName);
}
return unitProvider.getUnit((Class<? extends Quantity>) dimension);
}
}
Loading

0 comments on commit c6d8c3c

Please sign in to comment.