Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[js] Added 'ConfigOptionProvider' to provide filenames ending with '.js' in Profile configuration #9646

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bundles/org.openhab.transform.javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -83,4 +98,47 @@ public void unsetJavaScriptEngineManager(JavaScriptEngineManager manager) {
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,31 @@
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;

/**
* 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<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)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

This file was deleted.