diff --git a/bundles/org.openhab.transform.javascript/README.md b/bundles/org.openhab.transform.javascript/README.md index a6e8279894ce2..bd6a09018fc9c 100644 --- a/bundles/org.openhab.transform.javascript/README.md +++ b/bundles/org.openhab.transform.javascript/README.md @@ -19,6 +19,7 @@ transform/getValue.js: ``` ## Test JavaScript + You can use online JavaScript testers to validate your script. E.g. https://www.webtoolkitonline.com/javascript-tester.html diff --git a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptEngineManager.java b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptEngineManager.java index 677ee887bc762..97ec03dad3ab4 100644 --- a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptEngineManager.java +++ b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptEngineManager.java @@ -35,8 +35,8 @@ /** * Simple cache for compiled JavaScript files. * + * @author Thomas Kordelle - Initial contribution * @author Thomas Kordelle - pre compiled scripts - * */ @NonNullByDefault @Component(service = JavaScriptEngineManager.class) diff --git a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptTransformationService.java b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptTransformationService.java index 0a0179381936b..660bd632efd7c 100644 --- a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptTransformationService.java +++ b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/JavaScriptTransformationService.java @@ -12,14 +12,26 @@ */ package org.openhab.transform.javascript.internal; +import java.io.File; +import java.io.FilenameFilter; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + import javax.script.Bindings; import javax.script.CompiledScript; import javax.script.ScriptException; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.config.core.ConfigOptionProvider; +import org.openhab.core.config.core.ParameterOption; import org.openhab.core.transform.TransformationException; import org.openhab.core.transform.TransformationService; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -33,19 +45,22 @@ * @author Thomas Kordelle - pre compiled scripts */ @NonNullByDefault -@Component(property = { "openhab.transform=JS" }) -public class JavaScriptTransformationService implements TransformationService { +@Component(service = { TransformationService.class, ConfigOptionProvider.class }, property = { "openhab.transform=JS" }) +public class JavaScriptTransformationService implements TransformationService, ConfigOptionProvider { - private Logger logger = LoggerFactory.getLogger(JavaScriptTransformationService.class); - private @NonNullByDefault({}) JavaScriptEngineManager manager; + private final Logger logger = LoggerFactory.getLogger(JavaScriptTransformationService.class); - @Reference - public void setJavaScriptEngineManager(JavaScriptEngineManager manager) { - this.manager = manager; - } + private static final char EXTENSION_SEPARATOR = '.'; - public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) { - this.manager = null; + private static final String PROFILE_CONFIG_URI = "profile:transform:JS"; + private static final String CONFIG_PARAM_FUNCTION = "function"; + private static final String[] FILE_NAME_EXTENSIONS = { "js" }; + + private final JavaScriptEngineManager manager; + + @Activate + public JavaScriptTransformationService(final @Reference JavaScriptEngineManager manager) { + this.manager = manager; } /** @@ -83,4 +98,47 @@ public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) { result); } } + + @Override + public @Nullable Collection getParameterOptions(URI uri, String param, @Nullable String context, + @Nullable Locale locale) { + if (PROFILE_CONFIG_URI.equals(uri.toString())) { + switch (param) { + case CONFIG_PARAM_FUNCTION: + return getFilenames(FILE_NAME_EXTENSIONS).stream().map(f -> new ParameterOption(f, f)) + .collect(Collectors.toList()); + } + } + return null; + } + + /** + * Returns a list of all files with the given extensions in the transformation folder + */ + private List getFilenames(String[] validExtensions) { + File path = new File(TransformationScriptWatcher.TRANSFORM_FOLDER + File.separator); + return Arrays.asList(path.listFiles(new FileExtensionsFilter(validExtensions))).stream().map(f -> f.getName()) + .collect(Collectors.toList()); + } + + private class FileExtensionsFilter implements FilenameFilter { + + private final String[] validExtensions; + + public FileExtensionsFilter(String[] validExtensions) { + this.validExtensions = validExtensions; + } + + @Override + public boolean accept(@Nullable File dir, @Nullable String name) { + if (name != null) { + for (String extension : validExtensions) { + if (name.toLowerCase().endsWith(EXTENSION_SEPARATOR + extension)) { + return true; + } + } + } + return false; + } + } } diff --git a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/TransformationScriptWatcher.java b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/TransformationScriptWatcher.java index f5b0db0caccb5..ff06e9d52add9 100644 --- a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/TransformationScriptWatcher.java +++ b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/TransformationScriptWatcher.java @@ -22,6 +22,7 @@ import org.openhab.core.OpenHAB; import org.openhab.core.service.AbstractWatchService; import org.openhab.core.transform.TransformationService; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -29,30 +30,23 @@ * The {@link TransformationScriptWatcher} watches the transformation directory for files. If a deleted/modified file is * detected, the script is passed to the {@link JavaScriptEngineManager}. * + * @author Thomas Kordelle - Initial contribution * @author Thomas Kordelle - pre compiled scripts - * */ -@Component() +@Component public class TransformationScriptWatcher extends AbstractWatchService { public static final String TRANSFORM_FOLDER = OpenHAB.getConfigFolder() + File.separator + TransformationService.TRANSFORM_FOLDER_NAME; - private JavaScriptEngineManager manager; + private final JavaScriptEngineManager manager; - public TransformationScriptWatcher() { + @Activate + public TransformationScriptWatcher(final @Reference JavaScriptEngineManager manager) { super(TRANSFORM_FOLDER); - } - - @Reference - public void setJavaScriptEngineManager(JavaScriptEngineManager manager) { this.manager = manager; } - public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) { - this.manager = null; - } - @Override public void activate() { super.activate(); diff --git a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfile.java b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfile.java similarity index 74% rename from bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfile.java rename to bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfile.java index 82fc0a97e2ed0..087e6bda5106f 100644 --- a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfile.java +++ b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfile.java @@ -13,6 +13,7 @@ package org.openhab.transform.javascript.internal.profiles; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.profiles.ProfileCallback; import org.openhab.core.thing.profiles.ProfileContext; @@ -30,29 +31,26 @@ /** * Profile to offer the JavascriptTransformationservice on a ItemChannelLink * - * @author Stefan Triller - initial contribution - * + * @author Stefan Triller - Initial contribution */ @NonNullByDefault -public class JavascriptTransformationProfile implements StateProfile { +public class JavaScriptTransformationProfile implements StateProfile { + + private final Logger logger = LoggerFactory.getLogger(JavaScriptTransformationProfile.class); public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID( TransformationService.TRANSFORM_PROFILE_SCOPE, "JS"); - private final Logger logger = LoggerFactory.getLogger(JavascriptTransformationProfile.class); + private static final String FUNCTION_PARAM = "function"; + private static final String SOURCE_FORMAT_PARAM = "sourceFormat"; private final TransformationService service; private final ProfileCallback callback; - private static final String FUNCTION_PARAM = "function"; - private static final String SOURCE_FORMAT_PARAM = "sourceFormat"; + private final @Nullable String function; + private final @Nullable String sourceFormat; - @NonNullByDefault({}) - private final String function; - @NonNullByDefault({}) - private final String sourceFormat; - - public JavascriptTransformationProfile(ProfileCallback callback, ProfileContext context, + public JavaScriptTransformationProfile(ProfileCallback callback, ProfileContext context, TransformationService service) { this.service = service; this.callback = callback; @@ -116,15 +114,23 @@ public void onStateUpdateFromHandler(State state) { } private Type transformState(Type state) { - String result = state.toFullString(); - try { - result = TransformationHelper.transform(service, function, sourceFormat, state.toFullString()); - } catch (TransformationException e) { - logger.warn("Could not transform state '{}' with function '{}' and format '{}'", state, function, - sourceFormat); + String localFunction = function, localSourceFormat = sourceFormat; + if (localFunction != null && localSourceFormat != null) { + String result = state.toFullString(); + try { + result = TransformationHelper.transform(service, localFunction, localSourceFormat, result); + } catch (TransformationException e) { + logger.warn("Could not transform state '{}' with function '{}' and format '{}'", state, function, + sourceFormat); + } + StringType resultType = new StringType(result); + logger.debug("Transformed '{}' into '{}'", state, resultType); + return resultType; + } else { + logger.warn( + "Please specify a function and a source format for this Profile in the '{}' and '{}' parameters. Returning the original state now.", + FUNCTION_PARAM, SOURCE_FORMAT_PARAM); + return state; } - StringType resultType = new StringType(result); - logger.debug("Transformed '{}' into '{}'", state, resultType); - return resultType; } } diff --git a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfileFactory.java b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfileFactory.java similarity index 79% rename from bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfileFactory.java rename to bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfileFactory.java index a43fd75ff1136..2803b857c96fe 100644 --- a/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavascriptTransformationProfileFactory.java +++ b/bundles/org.openhab.transform.javascript/src/main/java/org/openhab/transform/javascript/internal/profiles/JavaScriptTransformationProfileFactory.java @@ -27,37 +27,37 @@ import org.openhab.core.thing.profiles.ProfileTypeProvider; import org.openhab.core.thing.profiles.ProfileTypeUID; import org.openhab.core.transform.TransformationService; +import org.openhab.transform.javascript.internal.JavaScriptTransformationService; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** - * Profilefactory that creates the transformation profile for the javascript transformation service - * - * @author Stefan Triller - initial contribution + * {@link ProfileFactory} that creates the transformation profile for the {@link JavaScriptTransformationService} * + * @author Stefan Triller - Initial contribution */ @NonNullByDefault @Component(service = { ProfileFactory.class, ProfileTypeProvider.class }) -public class JavascriptTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider { +public class JavaScriptTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider { @NonNullByDefault({}) private TransformationService service; @Override public Collection getProfileTypes(@Nullable Locale locale) { - return Arrays.asList(ProfileTypeBuilder.newState(JavascriptTransformationProfile.PROFILE_TYPE_UID, - JavascriptTransformationProfile.PROFILE_TYPE_UID.getId()).build()); + return Arrays.asList(ProfileTypeBuilder.newState(JavaScriptTransformationProfile.PROFILE_TYPE_UID, + JavaScriptTransformationProfile.PROFILE_TYPE_UID.getId()).build()); } @Override public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback, ProfileContext profileContext) { - return new JavascriptTransformationProfile(callback, profileContext, service); + return new JavaScriptTransformationProfile(callback, profileContext, service); } @Override public Collection getSupportedProfileTypeUIDs() { - return Arrays.asList(JavascriptTransformationProfile.PROFILE_TYPE_UID); + return Arrays.asList(JavaScriptTransformationProfile.PROFILE_TYPE_UID); } @Reference(target = "(openhab.transform=JS)") diff --git a/bundles/org.openhab.transform.javascript/src/main/resources/OH-INF/config/javascriptProfile.xml b/bundles/org.openhab.transform.javascript/src/main/resources/OH-INF/config/javascriptProfile.xml index 75887da9f0d33..a921594ef8582 100644 --- a/bundles/org.openhab.transform.javascript/src/main/resources/OH-INF/config/javascriptProfile.xml +++ b/bundles/org.openhab.transform.javascript/src/main/resources/OH-INF/config/javascriptProfile.xml @@ -9,8 +9,9 @@ Filename of the JavaScript in the transform folder. The state will be available in the variable \"input\". + false - + How to format the state on the channel before transforming it, i.e. %s or %.1f °C (default is %s) true diff --git a/bundles/org.openhab.transform.javascript/src/main/resources/readme.txt b/bundles/org.openhab.transform.javascript/src/main/resources/readme.txt deleted file mode 100644 index a2ee892dc45ba..0000000000000 --- a/bundles/org.openhab.transform.javascript/src/main/resources/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Bundle resources go in here!