[js] Added 'ConfigOptionProvider' to provide filenames ending with '.js' in Profile configuration (#9646)
* Added ConfigOptionProvider to provide file names ending with '.js' in Profile configuration Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
parent
ceb119b02c
commit
25947bfa9d
bundles/org.openhab.transform.javascript
README.md
src/main
java/org/openhab/transform/javascript/internal
JavaScriptEngineManager.javaJavaScriptTransformationService.javaTransformationScriptWatcher.java
profiles
resources
@ -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
|
||||
|
||||
|
@ -35,8 +35,8 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* Simple cache for compiled JavaScript files.
|
||||
*
|
||||
* @author Thomas Kordelle - Initial contribution
|
||||
* @author Thomas Kordelle - pre compiled scripts
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = JavaScriptEngineManager.class)
|
||||
|
@ -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,21 +45,24 @@ import org.slf4j.LoggerFactory;
|
||||
* @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) {
|
||||
private static final char EXTENSION_SEPARATOR = '.';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) {
|
||||
this.manager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the input <code>source</code> by Java Script. It expects the
|
||||
* transformation rule to be read from a file which is stored under the
|
||||
@ -83,4 +98,47 @@ public class JavaScriptTransformationService implements TransformationService {
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> 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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import java.nio.file.WatchEvent.Kind;
|
||||
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 @@ import org.osgi.service.component.annotations.Reference;
|
||||
* 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();
|
||||
|
@ -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 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* 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 class JavascriptTransformationProfile implements StateProfile {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -27,37 +27,37 @@ import org.openhab.core.thing.profiles.ProfileTypeBuilder;
|
||||
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<ProfileType> 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<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
||||
return Arrays.asList(JavascriptTransformationProfile.PROFILE_TYPE_UID);
|
||||
return Arrays.asList(JavaScriptTransformationProfile.PROFILE_TYPE_UID);
|
||||
}
|
||||
|
||||
@Reference(target = "(openhab.transform=JS)")
|
@ -9,8 +9,9 @@
|
||||
<label>JavaScript Filename</label>
|
||||
<description>Filename of the JavaScript in the transform folder. The state will be available in the variable
|
||||
\"input\".</description>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
<parameter name="sourceFormat" type="text" required="false">
|
||||
<parameter name="sourceFormat" type="text">
|
||||
<label>State Formatter</label>
|
||||
<description>How to format the state on the channel before transforming it, i.e. %s or %.1f °C (default is %s)</description>
|
||||
<advanced>true</advanced>
|
||||
|
@ -1 +0,0 @@
|
||||
Bundle resources go in here!
|
Loading…
x
Reference in New Issue
Block a user