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.jsonpath</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,31 @@
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
== Third-party Content
json-path
* License: Apache 2.0
* Project: https://github.com/jayway/JsonPath
* Source: https://github.com/jayway/JsonPath
json-smart, accessors-smart
* License: Apache 2.0
* Project: https://github.com/netplex/json-smart-v2
* Source: https://github.com/netplex/json-smart-v2
asm
* License: BSD
* Project: https://asm.ow2.io/index.html
* Source: https://gitlab.ow2.org/asm/asm

View File

@@ -0,0 +1,68 @@
# JsonPath Transformation Service
Extracts values from a JSON string using a [JsonPath](https://github.com/jayway/JsonPath#jayway-jsonpath) expression.
Given the following JSON string:
```
[{ "device": { "location": "Outside", "status": { "temperature": 23.2 }}}]
```
The expression `$.device.location` extracts the string `Outside`.
The JsonPath expression `$.device.status.temperature` extracts the string `23.2`.
## Examples
### Items
```
String Temperature_json "Temperature [JSONPATH($.device.status.temperature):%s °C]" {...}
Number Temperature "Temperature [%.1f °C]"
```
### Rules
```php
rule "Convert JSON to Item Type Number"
when
Item Temperature_json changed
then
// use the transformation service to retrieve the value
val newValue = transform("JSONPATH", "$.device.status.temperature", Temperature_json.state.toString)
// post the new value to the Number Item
Temperature.postUpdate( newValue )
end
```
Now the resulting Number can also be used in the label to [change the color](https://docs.openhab.org/configuration/sitemaps.html#label-and-value-colors) or in a rule as a value to compare.
## Differences to standard JsonPath
Compared to standard JsonPath, the transformation returns single values instead of arrays.
The transformation also will not ever return `null`.
This makes it possible to use the transform in labels or output channels of Things.
If the JsonPath expression provided results in no matches, the transformation will return the entire original JSON string.
## Usage as a Profile
The transformation can be used in a `Profile` on an `ItemChannelLink` too.
One example for configuring it in the `.items` file:
```java
String <itemName> { channel="<channelUID>"[profile="transform:JSONPATH", function="<jsonPath>", sourceFormat="<valueFormat>"]}
```
The JsonPath expression 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 returned from the transformation without any format changes.
This profile is a one-way transformation; only values from a device toward the item are changed.
## Further Reading
* An extended [introduction](https://www.w3schools.com/js/js_json_intro.asp) can be found at W3School.
* As JsonPath transformation is based on [Jayway](https://github.com/json-path/JsonPath), using an [online validator](https://jsonpath.herokuapp.com/) which also uses Jayway will give the most similar results.

View File

@@ -0,0 +1,48 @@
<?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.jsonpath</artifactId>
<name>openHAB Add-ons :: Bundles :: Transformation Service :: JSonPath</name>
<properties>
<bnd.importpackage>!org.apache.tapestry5.json.*,!org.codehaus.jettison.json.*,!org.json.*,!com.fasterxml.jackson.*</bnd.importpackage>
</properties>
<dependencies>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>accessors-smart</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

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

View File

@@ -0,0 +1,103 @@
/**
* 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.jsonpath.internal;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationService;
import org.openhab.core.types.UnDefType;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
/**
* <p>
* The implementation of {@link TransformationService} which transforms the input by JSonPath Expressions.
*
* @author Gaël L'hopital
* @author Sebastian Janzen
*
*/
@NonNullByDefault
@Component(immediate = true, property = { "smarthome.transform=JSONPATH" })
public class JSonPathTransformationService implements TransformationService {
private final Logger logger = LoggerFactory.getLogger(JSonPathTransformationService.class);
/**
* Transforms the input <code>source</code> by JSonPath expression.
*
* @param function JsonPath expression
* @param source String which contains JSON
* @throws TransformationException If the JsonPath expression is invalid, a {@link InvalidPathException} is thrown,
* which is encapsulated in a {@link TransformationException}.
*/
@Override
public @Nullable String transform(String jsonPathExpression, String source) throws TransformationException {
if (jsonPathExpression == null || source == null) {
throw new TransformationException("the given parameters 'JSonPath' and 'source' must not be null");
}
logger.debug("about to transform '{}' by the function '{}'", source, jsonPathExpression);
try {
Object transformationResult = JsonPath.read(source, jsonPathExpression);
logger.debug("transformation resulted in '{}'", transformationResult);
if (transformationResult == null) {
return null;
} else if (transformationResult instanceof List) {
return flattenList((List<?>) transformationResult);
} else {
return transformationResult.toString();
}
} catch (PathNotFoundException e) {
throw new TransformationException("Invalid path '" + jsonPathExpression + "' in '" + source + "'");
} catch (InvalidPathException | InvalidJsonException e) {
throw new TransformationException("An error occurred while transforming JSON expression.", e);
}
}
private String flattenList(List<?> list) {
if (list.size() == 1) {
return list.get(0).toString();
}
if (list.size() > 1) {
if (list.get(0) instanceof Number || list.get(0) instanceof Boolean) {
return createNumberList(list);
} else if (list.get(0) instanceof String) {
return createStringList(list);
}
logger.warn(
"JsonPath expressions with more than one result are only supported for Boolean, Number and String data types, please adapt your selector. Result: {}",
list);
}
return UnDefType.NULL.toFullString();
}
private String createNumberList(List<?> list) {
return list.stream().map(n -> String.valueOf(n)).collect(Collectors.joining(", ", "[", "]"));
}
private String createStringList(List<?> list) {
return list.stream().map(n -> "\"" + String.valueOf(n) + "\"").collect(Collectors.joining(", ", "[", "]"));
}
}

View File

@@ -0,0 +1,131 @@
/**
* 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.jsonpath.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 JSonPathTransformationservice on a ItemChannelLink
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
public class JSonPathTransformationProfile implements StateProfile {
public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID(
TransformationService.TRANSFORM_PROFILE_SCOPE, "JSONPATH");
private final Logger logger = LoggerFactory.getLogger(JSonPathTransformationProfile.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 JSonPathTransformationProfile(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();
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.jsonpath.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 jsonpath transformation service
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
public class JSonPathTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
@NonNullByDefault({})
private TransformationService service;
@Override
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
return Arrays.asList(ProfileTypeBuilder.newState(JSonPathTransformationProfile.PROFILE_TYPE_UID,
JSonPathTransformationProfile.PROFILE_TYPE_UID.getId()).build());
}
@Override
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
ProfileContext profileContext) {
return new JSonPathTransformationProfile(callback, profileContext, service);
}
@Override
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
return Arrays.asList(JSonPathTransformationProfile.PROFILE_TYPE_UID);
}
@Reference(target = "(smarthome.transform=JSONPATH)")
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:JSONPATH">
<parameter name="function" type="text" required="true">
<label>JSONPath Expression</label>
<description>Expression to be applied on the state. For example: $.device.status.temperature</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,131 @@
/**
* 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.jsonpath.internal;
import static org.junit.Assert.assertEquals;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openhab.core.transform.TransformationException;
/**
* @author Gaël L'hopital
*/
public class JSonPathTransformationServiceTest {
private JSonPathTransformationService processor;
@Before
public void init() {
processor = new JSonPathTransformationService();
}
@Test
public void testTransformByJSon() throws TransformationException {
String json = "{'store':{'book':[{'category':'reference','author':'Nigel Rees','title': 'Sayings of the Century', 'price': 8.95 } ], 'bicycle': { 'color': 'red', 'price': 19.95} }}";
// method under test
String transformedResponse = processor.transform("$.store.book[0].author", json);
// Asserts
Assert.assertEquals("Nigel Rees", transformedResponse);
}
private static final String jsonArray = "[" + //
"{ \"id\":1, \"name\":\"bob\", \"empty\":null }," + //
"{ \"id\":2, \"name\":\"alice\" }" + //
"]";
@Test
public void testValidPath1() throws TransformationException {
String transformedResponse = processor.transform("$[0].name", jsonArray);
assertEquals("bob", transformedResponse);
}
@Test
public void testValidPath2() throws TransformationException {
String transformedResponse = processor.transform("$[1].id", jsonArray);
assertEquals("2", transformedResponse);
}
@Test(expected = TransformationException.class)
public void testInvalidPathThrowsException() throws TransformationException {
processor.transform("$$", jsonArray);
}
@Test(expected = TransformationException.class)
public void testPathMismatchReturnNull() throws TransformationException {
processor.transform("$[5].id", jsonArray);
}
@Test(expected = TransformationException.class)
public void testInvalidJsonReturnNull() throws TransformationException {
processor.transform("$", "{id:");
}
@Test
public void testNullValue() throws TransformationException {
String transformedResponse = processor.transform("$[0].empty", jsonArray);
assertEquals(null, transformedResponse);
}
@Test
public void testIndefinite_filteredToSingle() throws TransformationException {
String transformedResponse = processor.transform("$.*[?(@.name=='bob')].id", jsonArray);
assertEquals("1", transformedResponse);
}
@Test
public void testIndefinite_notFiltered() throws TransformationException {
String transformedResponse = processor.transform("$.*.id", jsonArray);
assertEquals("[1, 2]", transformedResponse);
}
@Test
public void testIndefinite_noMatch() throws TransformationException {
String transformedResponse = processor.transform("$.*[?(@.name=='unknown')].id", jsonArray);
assertEquals("NULL", transformedResponse);
}
@Test
public void testBooleanList() throws TransformationException {
final String list = "[true, false, true, true, false]";
final String json = "{\"data\":" + list + "}";
String transformedResponse = processor.transform("$.data", json);
assertEquals(list, transformedResponse);
}
@Test
public void testNumberList() throws TransformationException {
final String list = "[0, 160, 253, -9, 21]";
final String json = "{\"data\":" + list + "}";
String transformedResponse = processor.transform("$.data", json);
assertEquals(list, transformedResponse);
}
@Test
public void testDoubleList() throws TransformationException {
final String list = "[1.0, 2.16, 5.253, -3.9, 21.21]";
final String json = "{\"data\":" + list + "}";
String transformedResponse = processor.transform("$.data", json);
assertEquals(list, transformedResponse);
}
@Test
public void testStringList() throws TransformationException {
final String list = "[\"test1\", \"test2\", \"test3\", \"test4\"]";
final String json = "{\"data\":" + list + "}";
String transformedResponse = processor.transform("$.data", json);
assertEquals(list, transformedResponse);
}
}