[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
@ -19,6 +19,7 @@ transform/getValue.js:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test JavaScript
|
## Test JavaScript
|
||||||
|
|
||||||
You can use online JavaScript testers to validate your script.
|
You can use online JavaScript testers to validate your script.
|
||||||
E.g. https://www.webtoolkitonline.com/javascript-tester.html
|
E.g. https://www.webtoolkitonline.com/javascript-tester.html
|
||||||
|
|
||||||
|
|||||||
@ -35,8 +35,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
/**
|
/**
|
||||||
* Simple cache for compiled JavaScript files.
|
* Simple cache for compiled JavaScript files.
|
||||||
*
|
*
|
||||||
|
* @author Thomas Kordelle - Initial contribution
|
||||||
* @author Thomas Kordelle - pre compiled scripts
|
* @author Thomas Kordelle - pre compiled scripts
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(service = JavaScriptEngineManager.class)
|
@Component(service = JavaScriptEngineManager.class)
|
||||||
|
|||||||
@ -12,14 +12,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.transform.javascript.internal;
|
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.Bindings;
|
||||||
import javax.script.CompiledScript;
|
import javax.script.CompiledScript;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
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.TransformationException;
|
||||||
import org.openhab.core.transform.TransformationService;
|
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.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -33,21 +45,24 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author Thomas Kordelle - pre compiled scripts
|
* @author Thomas Kordelle - pre compiled scripts
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(property = { "openhab.transform=JS" })
|
@Component(service = { TransformationService.class, ConfigOptionProvider.class }, property = { "openhab.transform=JS" })
|
||||||
public class JavaScriptTransformationService implements TransformationService {
|
public class JavaScriptTransformationService implements TransformationService, ConfigOptionProvider {
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(JavaScriptTransformationService.class);
|
private final Logger logger = LoggerFactory.getLogger(JavaScriptTransformationService.class);
|
||||||
private @NonNullByDefault({}) JavaScriptEngineManager manager;
|
|
||||||
|
|
||||||
@Reference
|
private static final char EXTENSION_SEPARATOR = '.';
|
||||||
public void setJavaScriptEngineManager(JavaScriptEngineManager manager) {
|
|
||||||
|
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;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) {
|
|
||||||
this.manager = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms the input <code>source</code> by Java Script. It expects the
|
* 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
|
* transformation rule to be read from a file which is stored under the
|
||||||
@ -83,4 +98,47 @@ public class JavaScriptTransformationService implements TransformationService {
|
|||||||
result);
|
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.OpenHAB;
|
||||||
import org.openhab.core.service.AbstractWatchService;
|
import org.openhab.core.service.AbstractWatchService;
|
||||||
import org.openhab.core.transform.TransformationService;
|
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.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
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
|
* The {@link TransformationScriptWatcher} watches the transformation directory for files. If a deleted/modified file is
|
||||||
* detected, the script is passed to the {@link JavaScriptEngineManager}.
|
* detected, the script is passed to the {@link JavaScriptEngineManager}.
|
||||||
*
|
*
|
||||||
|
* @author Thomas Kordelle - Initial contribution
|
||||||
* @author Thomas Kordelle - pre compiled scripts
|
* @author Thomas Kordelle - pre compiled scripts
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Component()
|
@Component
|
||||||
public class TransformationScriptWatcher extends AbstractWatchService {
|
public class TransformationScriptWatcher extends AbstractWatchService {
|
||||||
|
|
||||||
public static final String TRANSFORM_FOLDER = OpenHAB.getConfigFolder() + File.separator
|
public static final String TRANSFORM_FOLDER = OpenHAB.getConfigFolder() + File.separator
|
||||||
+ TransformationService.TRANSFORM_FOLDER_NAME;
|
+ TransformationService.TRANSFORM_FOLDER_NAME;
|
||||||
|
|
||||||
private JavaScriptEngineManager manager;
|
private final JavaScriptEngineManager manager;
|
||||||
|
|
||||||
public TransformationScriptWatcher() {
|
@Activate
|
||||||
|
public TransformationScriptWatcher(final @Reference JavaScriptEngineManager manager) {
|
||||||
super(TRANSFORM_FOLDER);
|
super(TRANSFORM_FOLDER);
|
||||||
}
|
|
||||||
|
|
||||||
@Reference
|
|
||||||
public void setJavaScriptEngineManager(JavaScriptEngineManager manager) {
|
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) {
|
|
||||||
this.manager = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
super.activate();
|
super.activate();
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
package org.openhab.transform.javascript.internal.profiles;
|
package org.openhab.transform.javascript.internal.profiles;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.thing.profiles.ProfileCallback;
|
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||||
import org.openhab.core.thing.profiles.ProfileContext;
|
import org.openhab.core.thing.profiles.ProfileContext;
|
||||||
@ -30,29 +31,26 @@ import org.slf4j.LoggerFactory;
|
|||||||
/**
|
/**
|
||||||
* Profile to offer the JavascriptTransformationservice on a ItemChannelLink
|
* Profile to offer the JavascriptTransformationservice on a ItemChannelLink
|
||||||
*
|
*
|
||||||
* @author Stefan Triller - initial contribution
|
* @author Stefan Triller - Initial contribution
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@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(
|
public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID(
|
||||||
TransformationService.TRANSFORM_PROFILE_SCOPE, "JS");
|
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 TransformationService service;
|
||||||
private final ProfileCallback callback;
|
private final ProfileCallback callback;
|
||||||
|
|
||||||
private static final String FUNCTION_PARAM = "function";
|
private final @Nullable String function;
|
||||||
private static final String SOURCE_FORMAT_PARAM = "sourceFormat";
|
private final @Nullable String sourceFormat;
|
||||||
|
|
||||||
@NonNullByDefault({})
|
public JavaScriptTransformationProfile(ProfileCallback callback, ProfileContext context,
|
||||||
private final String function;
|
|
||||||
@NonNullByDefault({})
|
|
||||||
private final String sourceFormat;
|
|
||||||
|
|
||||||
public JavascriptTransformationProfile(ProfileCallback callback, ProfileContext context,
|
|
||||||
TransformationService service) {
|
TransformationService service) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
@ -116,15 +114,23 @@ public class JavascriptTransformationProfile implements StateProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Type transformState(Type state) {
|
private Type transformState(Type state) {
|
||||||
String result = state.toFullString();
|
String localFunction = function, localSourceFormat = sourceFormat;
|
||||||
try {
|
if (localFunction != null && localSourceFormat != null) {
|
||||||
result = TransformationHelper.transform(service, function, sourceFormat, state.toFullString());
|
String result = state.toFullString();
|
||||||
} catch (TransformationException e) {
|
try {
|
||||||
logger.warn("Could not transform state '{}' with function '{}' and format '{}'", state, function,
|
result = TransformationHelper.transform(service, localFunction, localSourceFormat, result);
|
||||||
sourceFormat);
|
} 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.ProfileTypeProvider;
|
||||||
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
||||||
import org.openhab.core.transform.TransformationService;
|
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.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profilefactory that creates the transformation profile for the javascript transformation service
|
* {@link ProfileFactory} that creates the transformation profile for the {@link JavaScriptTransformationService}
|
||||||
*
|
|
||||||
* @author Stefan Triller - initial contribution
|
|
||||||
*
|
*
|
||||||
|
* @author Stefan Triller - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
|
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
|
||||||
public class JavascriptTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
|
public class JavaScriptTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
|
||||||
|
|
||||||
@NonNullByDefault({})
|
@NonNullByDefault({})
|
||||||
private TransformationService service;
|
private TransformationService service;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
|
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
|
||||||
return Arrays.asList(ProfileTypeBuilder.newState(JavascriptTransformationProfile.PROFILE_TYPE_UID,
|
return Arrays.asList(ProfileTypeBuilder.newState(JavaScriptTransformationProfile.PROFILE_TYPE_UID,
|
||||||
JavascriptTransformationProfile.PROFILE_TYPE_UID.getId()).build());
|
JavaScriptTransformationProfile.PROFILE_TYPE_UID.getId()).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
|
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
|
||||||
ProfileContext profileContext) {
|
ProfileContext profileContext) {
|
||||||
return new JavascriptTransformationProfile(callback, profileContext, service);
|
return new JavaScriptTransformationProfile(callback, profileContext, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
||||||
return Arrays.asList(JavascriptTransformationProfile.PROFILE_TYPE_UID);
|
return Arrays.asList(JavaScriptTransformationProfile.PROFILE_TYPE_UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Reference(target = "(openhab.transform=JS)")
|
@Reference(target = "(openhab.transform=JS)")
|
||||||
@ -9,8 +9,9 @@
|
|||||||
<label>JavaScript Filename</label>
|
<label>JavaScript Filename</label>
|
||||||
<description>Filename of the JavaScript in the transform folder. The state will be available in the variable
|
<description>Filename of the JavaScript in the transform folder. The state will be available in the variable
|
||||||
\"input\".</description>
|
\"input\".</description>
|
||||||
|
<limitToOptions>false</limitToOptions>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="sourceFormat" type="text" required="false">
|
<parameter name="sourceFormat" type="text">
|
||||||
<label>State Formatter</label>
|
<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>
|
<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>
|
<advanced>true</advanced>
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Bundle resources go in here!
|
|
||||||
Loading…
x
Reference in New Issue
Block a user