added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.transform.exec</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,92 @@
# Exec Transformation Service
Transforms an input string with an external program.
Executes an external program and returns the output as a string.
In the given command line the placeholder `%s` is substituted with the input value.
The external program must either be in the executable search path of the server process, or an absolute path has to be used.
For security reasons all commands need to be whitelisted.
Allowed commands need to be added to the `misc/exec.whitelist` file in the configuration directory.
Every command needs to be on a separate line.
Example:
```shell
/bin/date -v1d -v+1m -v-1d -v-%s
numfmt --to=iec-i --suffix=B --padding=7 %s
```
## Examples
### General Setup
**Item**
This will replace the visible label in the UI with the transformation you apply with the command <TransformProgram>.
```java
String yourItem "Some info [EXEC(/absolute/path/to/your/<TransformProgram> %s):%s]"
```
**Rule**
```java
rule "Your Rule Name"
when
Item YourTriggeringItem changed
then
var formatted = transform("EXEC","/absolute/path/to/your/<TransformProgram>", YourTriggeringItem.state.toString)
yourFormattedItem.sendCommand(formatted.toString)
end
```
### Example with a program
Substitute the `/absolute/path/to/your/<TransformProgram>` with
```shell
/bin/date -v1d -v+1m -v-1d -v-%s
```
When the input argument for `%s` is `fri` the execution returns a string with the last weekday of the month, formated as readable text.
```
Fri 31 Mar 2017 13:58:47 IST`
```
Or replace it with
```shell
numfmt --to=iec-i --suffix=B --padding=7 %s
```
When the input argument for `%s` is 1234567 it will return the bytes formatted in a better readable form
```shell
1.2MiB
```
### Usage as a Profile
The functionality of this `TransformationService` can be used in a `Profile` on an `ItemChannelLink` too.
To do so, it can be configured in the `.items` file as follows:
```java
String <itemName> { channel="<channelUID>"[profile="transform:EXEC", function="<shellcommand>", sourceFormat="<valueFormat>"]}
```
The shell command to be executed has to be set in the `function` parameter.
The parameter `sourceFormat` is optional and can be used to format the input value **before** the transformation, i.e. `%.3f`.
If omitted the default is `%s`, so the input value will be put into the transformation without any format changes.
Please note: This profile is a one-way transformation, i.e. only values from a device towards the item are changed, the other direction is left untouched.
# Further Reading
* [Manual](http://man7.org/linux/man-pages/man1/date.1.html) and [tutorial](https://linode.com/docs/tools-reference/tools/use-the-date-command-in-linux/) for date.
* [Manual](http://man7.org/linux/man-pages/man1/numfmt.1.html) and [tutorial](https://www.pixelbeat.org/docs/numfmt.html) for numfmt.

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.transform.exec</artifactId>
<name>openHAB Add-ons :: Bundles :: Transformation Service :: Exec</name>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.transform.exec-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-transformation-exec" description="Exec Transformation" version="${project.version}">
<feature>openhab-runtime-base</feature>
<configfile finalname="${openhab.conf}/misc/exec.whitelist" override="false">mvn:${project.groupId}/openhab-addons-external/${project.version}/cfg/exec.whitelist</configfile>
<bundle start-level="75">mvn:org.openhab.addons.bundles/org.openhab.transform.exec/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.transform.exec.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.net.exec.ExecUtil;
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;
import org.slf4j.LoggerFactory;
/**
* The implementation of {@link TransformationService} which transforms the
* input by command line.
*
* @author Pauli Anttila - Initial contribution
* @author Jan N. Klug - added command whitelist service
*/
@NonNullByDefault
@Component(immediate = true, property = { "smarthome.transform=EXEC" })
public class ExecTransformationService implements TransformationService {
private final Logger logger = LoggerFactory.getLogger(ExecTransformationService.class);
private final ExecTransformationWhitelistWatchService execTransformationWhitelistWatchService;
@Activate
public ExecTransformationService(
@Reference ExecTransformationWhitelistWatchService execTransformationWhitelistWatchService) {
this.execTransformationWhitelistWatchService = execTransformationWhitelistWatchService;
}
/**
* Transforms the input <code>source</code> by the command line.
*
* @param commandLine the command to execute. Command line should contain %s string, which will be replaced by the
* input data.
* @param source the input to transform
*/
@Override
public @Nullable String transform(String commandLine, String source) throws TransformationException {
if (commandLine == null || source == null) {
throw new TransformationException("the given parameters 'commandLine' and 'source' must not be null");
}
if (!execTransformationWhitelistWatchService.isWhitelisted(commandLine)) {
logger.warn("Tried to execute '{}', but it is not contained in whitelist.", commandLine);
return null;
}
logger.debug("about to transform '{}' by the commandline '{}'", source, commandLine);
long startTime = System.currentTimeMillis();
String formattedCommandLine = String.format(commandLine, source);
String result = ExecUtil.executeCommandLineAndWaitResponse(formattedCommandLine, 5000);
logger.trace("command line execution elapsed {} ms", System.currentTimeMillis() - startTime);
return result;
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.transform.exec.internal;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigConstants;
import org.openhab.core.service.AbstractWatchService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ExecTransformationWhitelistWatchService} provides a whitelist check for exec commands
*
* @author Jan N. Klug - Initial contribution
*/
@Component(service = ExecTransformationWhitelistWatchService.class)
@NonNullByDefault
public class ExecTransformationWhitelistWatchService extends AbstractWatchService {
private static final String COMMAND_WHITELIST_PATH = ConfigConstants.getConfigFolder() + File.separator + "misc";
private static final String COMMAND_WHITELIST_FILE = "exec.whitelist";
private final Logger logger = LoggerFactory.getLogger(ExecTransformationWhitelistWatchService.class);
private final Set<String> commandWhitelist = new HashSet<>();
@Activate
public ExecTransformationWhitelistWatchService() {
super(COMMAND_WHITELIST_PATH);
processWatchEvent(null, null, Paths.get(COMMAND_WHITELIST_PATH, COMMAND_WHITELIST_FILE));
}
@Override
protected boolean watchSubDirectories() {
return false;
}
@Override
protected Kind<?>[] getWatchEventKinds(@Nullable Path directory) {
return new Kind<?>[] { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY };
}
@Override
protected void processWatchEvent(@Nullable WatchEvent<?> event, @Nullable Kind<?> kind, @Nullable Path path) {
if (path != null && path.endsWith(COMMAND_WHITELIST_FILE)) {
commandWhitelist.clear();
try {
Files.lines(path).filter(line -> !line.trim().startsWith("#")).forEach(commandWhitelist::add);
logger.debug("Updated command whitelist: {}", commandWhitelist);
} catch (IOException e) {
logger.warn("Cannot read whitelist file, exec transformations won't be processed: {}", e.getMessage());
}
}
}
/**
* Check if a command is whitelisted
*
* @param command the command to check alias
* @return true if whitelisted, false if not
*/
public boolean isWhitelisted(String command) {
return commandWhitelist.contains(command);
}
}

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.transform.exec.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;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.StateProfile;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationHelper;
import org.openhab.core.transform.TransformationService;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Profile to offer the ExecTransformationservice on a ItemChannelLink
*
* @author Stefan Triller - initial contribution
*/
@NonNullByDefault
public class ExecTransformationProfile implements StateProfile {
public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID(
TransformationService.TRANSFORM_PROFILE_SCOPE, "EXEC");
private final Logger logger = LoggerFactory.getLogger(ExecTransformationProfile.class);
private final TransformationService service;
private final ProfileCallback callback;
private static final String FUNCTION_PARAM = "function";
private static final String SOURCE_FORMAT_PARAM = "sourceFormat";
private @Nullable String function;
private @Nullable String sourceFormat;
public ExecTransformationProfile(ProfileCallback callback, ProfileContext context, TransformationService service) {
this.service = service;
this.callback = callback;
Object paramFunction = context.getConfiguration().get(FUNCTION_PARAM);
Object paramSource = context.getConfiguration().get(SOURCE_FORMAT_PARAM);
logger.debug("Profile configured with '{}'='{}', '{}'={}", FUNCTION_PARAM, paramFunction, SOURCE_FORMAT_PARAM,
paramSource);
// SOURCE_FORMAT_PARAM is an advanced parameter and we assume "%s" if it is not set
if (paramSource == null) {
paramSource = "%s";
}
if (paramFunction instanceof String && paramSource instanceof String) {
function = (String) paramFunction;
sourceFormat = (String) paramSource;
} else {
logger.error("Parameter '{}' and '{}' have to be Strings. Profile will be inactive.", FUNCTION_PARAM,
SOURCE_FORMAT_PARAM);
function = null;
sourceFormat = null;
}
}
@Override
public ProfileTypeUID getProfileTypeUID() {
return PROFILE_TYPE_UID;
}
@Override
public void onStateUpdateFromItem(State state) {
callback.handleUpdate(state);
}
@Override
public void onCommandFromItem(Command command) {
callback.handleCommand(command);
}
@Override
public void onCommandFromHandler(Command command) {
if (function == null || sourceFormat == null) {
logger.warn(
"Please specify a function and a source format for this Profile in the '{}', and '{}' parameters. Returning the original command now.",
FUNCTION_PARAM, SOURCE_FORMAT_PARAM);
callback.sendCommand(command);
return;
}
callback.sendCommand((Command) transformState(command));
}
@Override
public void onStateUpdateFromHandler(State state) {
if (function == null || sourceFormat == null) {
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);
callback.sendUpdate(state);
return;
}
callback.sendUpdate((State) transformState(state));
}
private Type transformState(Type state) {
String result = state.toFullString();
String function = this.function;
String sourceFormat = this.sourceFormat;
if (function == null || sourceFormat == null) {
logger.warn("Could not transform state '{}' with function '{}' and format '{}'", state, function,
sourceFormat);
} else {
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);
}
}
StringType resultType = new StringType(result);
logger.debug("Transformed '{}' into '{}'", state, resultType);
return resultType;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.transform.exec.internal.profiles;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.profiles.Profile;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileContext;
import org.openhab.core.thing.profiles.ProfileFactory;
import org.openhab.core.thing.profiles.ProfileType;
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.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* Profilefactory that creates the transformation profile for the exec transformation service
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
public class ExecTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
@NonNullByDefault({})
private TransformationService service;
@Override
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
return Arrays.asList(ProfileTypeBuilder.newState(ExecTransformationProfile.PROFILE_TYPE_UID,
ExecTransformationProfile.PROFILE_TYPE_UID.getId()).build());
}
@Override
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
ProfileContext profileContext) {
return new ExecTransformationProfile(callback, profileContext, service);
}
@Override
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
return Arrays.asList(ExecTransformationProfile.PROFILE_TYPE_UID);
}
@Reference(target = "(smarthome.transform=EXEC)")
public void addTransformationService(TransformationService service) {
this.service = service;
}
public void removeTransformationService(TransformationService service) {
this.service = null;
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="profile:transform:EXEC">
<parameter name="function" type="text" required="true">
<label>Command</label>
<description>Command to be executed on the command line. It should contain %s which will be substituted with the
state.</description>
</parameter>
<parameter name="sourceFormat" type="text" required="false">
<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>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1 @@
Bundle resources go in here!