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.xslt</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,126 @@
# XSLT Transformation Service
Transform input using the XML Stylesheet Language for Transformations (XSLT).
XSLT is a standard method to transform an XML structure from one document into a new document with a different structure.
The transformation expects the rule to be read from a file which is stored under the `transform` folder.
To organize the various transformations one should use subfolders.
General transformation rule summary:
* The directive `xsl:output` defines how the output document should be structured.
* The directive `xsl:template` specifies matching attributes for the XML node to find.
* The `xsl:template` tag contains the rule which specifies what should be done.
The Rule uses XPath to gather the XML node information.
For more information have a look at the [XPath transformation](https://docs.openhab.org/addons/transformations/xpath/readme.html) .
## Examples
### Basic Example
A simple but complete XSLT transformation looks like in the following example, which was taken from [here](https://en.wikipedia.org/wiki/Java_API_for_XML_Processing#Example).
**input XML**
```xml
<?xml version='1.0' encoding='UTF-8'?>
<root><node val='hello'/></root>
```
**transform/helloworld.xsl**
* `xsl:output`: transform incoming document into another XML-like document, without indentation.
* `xsl:template`: `match="/"` "any type of node", so the whole document.
* The `xsl` rule does `select` the node `/root/node` and extracts the `value-of` attribute `val`.
```xml
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xml' indent='no'/>
<xsl:template match='/'>
<reRoot><reNode><xsl:value-of select='/root/node/@val' /> world</reNode></reRoot>
</xsl:template>
</xsl:stylesheet>
```
**Output XML**
```xml
<reRoot><reNode>hello world</reNode></reRoot>
```
### Advanced Example
This example has a namespace defined, as you would find in real world applications, which has to be matched in the rule.
**input XML**
* The tag `<PTZStatus>` contains an attribute `xmlns=` which defines the namespace `http://www.hikvision.com/ver20/XMLSchema`.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<PTZStatus version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
<AbsoluteHigh>
<elevation>0</elevation>
<azimuth date="Fri, 18 Dec 2009 9:38 am PST" >450</azimuth>
<absoluteZoom>10</absoluteZoom>
</AbsoluteHigh>
</PTZStatus>
```
**transform/azimut.xsl**
In the rule, the tag `<xsl:stylesheet>` has to have an attribute `xmlns:xsl="http://www.w3.org/1999/XSL/Transform"` and a second attribute `xmlns:`.
This attribute has to be the same as the namespace for the input document.
In the rule each step traversed along the path to the next tag has to be prepended with the `xmlns` namespace, here defined as `h`.
* `xsl:output` transform incoming document into another XML-like document, no indentation, **without XML**.
* `xsl:template`: `match="/"` whole document.
* Full path to node `azimuth` reading out `date` attribute.
* Add a linebreak by setting `&#10;` as text.
* Search for node `azimuth` by prepending `//` and get the `text`.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:h="http://www.hikvision.com/ver20/XMLSchema">
<xsl:output method="xml" indent="no" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:value-of select="/h:PTZStatus/h:AbsoluteHigh/h:azimuth/@date" />
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="//h:azimuth/text()" />
</xsl:template>
</xsl:stylesheet>
```
**Output Document**
```
Fri, 18 Dec 2009 9:38 am PST
450
```
## 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:XSLT", function="<xsltExpression>", sourceFormat="<valueFormat>"]}
```
The XSLT file (from within the `transform` folder) to be used 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
* Extended introduction and more [examples](https://en.wikipedia.org/wiki/XSLT#XSLT_examples) at Wikipedia.
* A good [introduction](https://www.w3schools.com/xml/xsl_intro.asp) and [tutorial](https://www.w3schools.com/xml/xsl_transformation.asp) at W3School.
* An informative [tutorial](https://www.ibm.com/developerworks/library/x-xsltmistakes/) of common mistakes.
* Online XSL transformer tools like [this](https://www.freeformatter.com/xsl-transformer.html) to check the syntax.

View File

@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="//current_conditions">
<xsl:value-of select="temp_c/@data" />
</xsl:template>
</xsl:stylesheet>

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.xslt</artifactId>
<name>openHAB Add-ons :: Bundles :: Transformation Service :: Xslt</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.transform.xslt-${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-xslt" description="XSLT Transformation" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="75">mvn:org.openhab.addons.bundles/org.openhab.transform.xslt/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,95 @@
/**
* 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.xslt.internal;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigConstants;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationService;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* The implementation of {@link TransformationService} which transforms the input by XSLT.
*
* @author Thomas.Eichstaedt-Engelen
*/
@NonNullByDefault
@Component(immediate = true, property = { "smarthome.transform=XSLT" })
public class XsltTransformationService implements TransformationService {
private final Logger logger = LoggerFactory.getLogger(XsltTransformationService.class);
/**
* Transforms the input <code>source</code> by XSLT.
*
* The method expects the transformation rule to be read from a file which
* is stored under the 'configurations/transform' folder. To organize the
* various transformations one should use subfolders.
*
* @param filename the name of the file which contains the XSLT transformation rule.
* The name may contain subfoldernames as well
* @param source the input to transform
*/
@Override
public @Nullable String transform(String filename, String source) throws TransformationException {
if (filename == null || source == null) {
throw new TransformationException("the given parameters 'filename' and 'source' must not be null");
}
Source xsl = null;
try {
String path = ConfigConstants.getConfigFolder() + File.separator
+ TransformationService.TRANSFORM_FOLDER_NAME + File.separator + filename;
xsl = new StreamSource(new File(path));
} catch (Exception e) {
String message = "opening file '" + filename + "' throws exception";
logger.error("{}", message, e);
throw new TransformationException(message, e);
}
logger.debug("about to transform '{}' by the function '{}'", source, xsl);
StringReader xml = new StringReader(source);
StringWriter out = new StringWriter();
Transformer transformer;
try {
transformer = TransformerFactory.newInstance().newTransformer(xsl);
transformer.transform(new StreamSource(xml), new StreamResult(out));
} catch (Exception e) {
logger.error("transformation throws exception", e);
throw new TransformationException("transformation throws exception", e);
}
logger.debug("transformation resulted in '{}'", out.toString());
return out.toString();
}
}

View File

@@ -0,0 +1,130 @@
/**
* 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.xslt.internal.profiles;
import org.eclipse.jdt.annotation.NonNullByDefault;
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 XsltTransformationservice on a ItemChannelLink
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
public class XSLTTransformationProfile implements StateProfile {
public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID(
TransformationService.TRANSFORM_PROFILE_SCOPE, "XSLT");
private final Logger logger = LoggerFactory.getLogger(XSLTTransformationProfile.class);
private final TransformationService service;
private final ProfileCallback callback;
private static final String FUNCTION_PARAM = "function";
private static final String SOURCE_FORMAT_PARAM = "sourceFormat";
@NonNullByDefault({})
private final String function;
@NonNullByDefault({})
private final String sourceFormat;
public XSLTTransformationProfile(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.warn("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.error(
"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.error(
"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();
try {
result = TransformationHelper.transform(service, function, sourceFormat, state.toFullString());
} catch (TransformationException e) {
logger.debug("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.xslt.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 xslt transformation service
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
public class XSLTTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
@NonNullByDefault({})
private TransformationService service;
@Override
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
return Arrays.asList(ProfileTypeBuilder.newState(XSLTTransformationProfile.PROFILE_TYPE_UID,
XSLTTransformationProfile.PROFILE_TYPE_UID.getId()).build());
}
@Override
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
ProfileContext profileContext) {
return new XSLTTransformationProfile(callback, profileContext, service);
}
@Override
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
return Arrays.asList(XSLTTransformationProfile.PROFILE_TYPE_UID);
}
@Reference(target = "(smarthome.transform=XSLT)")
public void addTransformationService(TransformationService service) {
this.service = service;
}
public void removeTransformationService(TransformationService service) {
this.service = null;
}
}

View File

@@ -0,0 +1,18 @@
<?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:XSLT">
<parameter name="function" type="text" required="true">
<label>XSL Filename</label>
<description>XSL file name in transform folder, containing the transformation expression.</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!

View File

@@ -0,0 +1,35 @@
/**
* 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.xslt.internal;
/**
* @author Thomas.Eichstaedt-Engelen
*/
public abstract class AbstractTransformationServiceTest {
protected String source = "<?xml version=\"1.0\"?><xml_api_reply version=\"1\"><weather module_id=\"0\""
+ " tab_id=\"0\" mobile_row=\"0\" mobile_zipped=\"1\" row=\"0\" section=\"0\" ><forecast_information>"
+ "<city data=\"Krefeld, North Rhine-Westphalia\"/><postal_code data=\"Krefeld Germany\"/>"
+ "<latitude_e6 data=\"\"/><longitude_e6 data=\"\"/><forecast_date data=\"2011-03-01\"/>"
+ "<current_date_time data=\"2011-03-01 15:20:00 +0000\"/><unit_system data=\"SI\"/></forecast_information>"
+ "<current_conditions><condition data=\"Meistens bew<65>lkt\"/><temp_f data=\"46\"/><temp_c data=\"8\"/>"
+ "<humidity data=\"Feuchtigkeit: 66 %\"/><icon data=\"/ig/images/weather/mostly_cloudy.gif\"/>"
+ "<wind_condition data=\"Wind: N mit 26 km/h\"/></current_conditions><forecast_conditions><day_of_week data=\"Di.\"/>"
+ "<low data=\"-1\"/><high data=\"6\"/><icon data=\"/ig/images/weather/sunny.gif\"/><condition data=\"Klar\"/>"
+ "</forecast_conditions><forecast_conditions><day_of_week data=\"Mi.\"/><low data=\"-1\"/><high data=\"8\"/>"
+ "<icon data=\"/ig/images/weather/sunny.gif\"/><condition data=\"Klar\"/></forecast_conditions><forecast_conditions>"
+ "<day_of_week data=\"Do.\"/><low data=\"-1\"/><high data=\"8\"/><icon data=\"/ig/images/weather/sunny.gif\"/>"
+ "<condition data=\"Klar\"/></forecast_conditions><forecast_conditions><day_of_week data=\"Fr.\"/><low data=\"0\"/>"
+ "<high data=\"8\"/><icon data=\"/ig/images/weather/sunny.gif\"/><condition data=\"Klar\"/></forecast_conditions>"
+ "</weather></xml_api_reply>";
}

View File

@@ -0,0 +1,41 @@
/**
* 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.xslt.internal;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.openhab.core.transform.TransformationException;
/**
* @author Thomas.Eichstaedt-Engelen
*/
public class XsltTransformationServiceTest extends AbstractTransformationServiceTest {
private XsltTransformationService processor;
@Before
public void init() {
processor = new XsltTransformationService();
}
@Test
public void testTransformByXSLT() throws TransformationException {
// method under test
String transformedResponse = processor.transform("http/google_weather.xsl", source);
// Asserts
assertEquals("8", transformedResponse);
}
}