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.scale</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,88 @@
# Scale Transformation Service
The Scale Transformation Service is an easy to handle tool that can help you with the discretization of number inputs.
It transforms a given input by matching it to specified ranges.
The input string must be in numerical format.
The file is expected to exist in the `transform` configuration directory and its ending has to be `.scale`.
It should follow the format given in the table below.
Range expressions always contain two parts.
The range to scale on, which is located left from the equality sign and the corresponding output string on the right of it.
A range consists of two bounds. Both are optional, the range is then open. Both bounds can be inclusive or exclusive.
| Scale Expression | Returns XYZ when the given value is |
|------------------|------------------------------------------------------------|
| `[12..23]=XYZ` | `between (or equal to) 12 and 23` |
| `]12..23[=XYZ` | `between 12 and 23 (12 and 23 are excluded in this case.)` |
| `[..23]=XYZ` | `lower than or equal to 23` |
| `]12..]=XYZ` | `greater than 12` |
These expressions are evaluated from top to bottom.
The first range that includes the value is selected.
## Special entries
Some special entries can be used in the scale file.
### Catchall Entry
`[..]=Catchall`
This entry will match all numeric values not met by a previous range. Obviously, this one should be put at the very end of the scale definition file.
### Not A Number
Scale transform is designed to work with numeric or quantity states. When the value presented to scale transform does not match this (most of the time with NULL or UNDEF states) it will not be handled and a warning is raised in the openhab.log . This case can be smoothly avoided with a
`NaN=Non Numeric State presented`
### Formatting output
At last, Scale transform can take care of formatting an output with this entry :
`format=%label% (%value%) !`
Where :
- `%label%` will be replaced by transformed value and
- `%value%` is the numeric value presen
## Example
The following example shows how to break down numeric UV values into fixed UV index categories.
We have an example UV sensor that sends numeric values from `0` to `100`, which we then want to scale into the [UV Index](https://en.wikipedia.org/wiki/Ultraviolet_index) range.
Example item:
```java
Number Uv_Sensor_Level "UV Level [SCALE(uvindex.scale):%s]"
```
Referenced scale file `uvindex.scale` in the `transform` folder:
```python
[..3]=1
]3..6]=2
]6..8]=3
]8..10]=4
]10..100]=5
```
Each value the item receives, will be categorized to one of the five given ranges.
Values **lower than or equal to 3** are matched with `[..3]=1`.
Greater values are catched in ranges with 2 values as criteria.
The only condition here is that the received value has to be lower or equal than `100` in our example, since we haven't defined other cases yet.
If **none** of the configured conditions matches the given value, the response will be empty.
Please note that all ranges for values above **3** are opened with a `]`.
So the border values (3, 6, 8 and 10) are always transformed to the lower range, since the `]` excludes the given critera.
## 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:SCALE", function="<filename>", sourceFormat="<valueFormat>"]}
```
The filename (within the `transform` folder) of the scale file 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.

View File

@@ -0,0 +1,5 @@
[-8..10[=low
[10..20[=middle
[20..300[=high
[300..]=extreme
[..]=catchall

View File

@@ -0,0 +1,5 @@
[-8..10[=low
[10..20[=middle
[20..300[=high
[300..]=extreme
NaN=Non Numeric

View File

@@ -0,0 +1,4 @@
]..10[=low
[1O..20[=middle
[20..300[=high
[300..]=extreme

View File

@@ -0,0 +1,3 @@
]..15[=first
[10..17[=second
[15..[=last

View File

@@ -0,0 +1,7 @@
[-40..20]=no significant
[20..29]=comfortable
[29..38]=some discomfort
[38..45]=avoid exertion
[45..54]=dangerous
[54..100]=heat stroke imminent
NaN=unknown

View File

@@ -0,0 +1,6 @@
[-40..20]=nicht wesentlich
[20..29]=komfortabel
[29..38]=etwas unannehmlich
[38..45]=Anstrengung vermeiden
[45..54]=gefährlich
[54..100]=Hitzschlag bevorstehend

View File

@@ -0,0 +1,6 @@
[-40..20]=non significatif
[20..29]=confortable
[29..38]=quelque inconfort
[38..45]=éviter de s'exposer
[45..54]=dangereux
[54..100]=coup de soleil imminent

View File

@@ -0,0 +1,4 @@
]..10[=low
[10..20[=middle
[20..300[=high
[300..]=extreme

View File

@@ -0,0 +1,6 @@
[0..899]=Excellente qualite d'air
[900..1099]=Correcte
[1100..1599]=Air intérieur contamine, aeration recommandee
[1600..10000]=Air intérieur fortement contamine
[..]=inconnu ⁉
format=%label% (%value%) !

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

View File

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

View File

@@ -0,0 +1,112 @@
/**
* 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.scale.internal;
import java.math.BigDecimal;
/**
* Range implementation using BigDecimals.
*
* @author Markus Rathgeb - Initial contribution
*/
public class Range {
public static Range open(final BigDecimal lower, final BigDecimal upper) {
return new Range(lower, false, upper, false);
}
public static Range closed(final BigDecimal lower, final BigDecimal upper) {
return new Range(lower, true, upper, true);
}
public static Range openClosed(final BigDecimal lower, final BigDecimal upper) {
return new Range(lower, false, upper, true);
}
public static Range closedOpen(final BigDecimal lower, final BigDecimal upper) {
return new Range(lower, true, upper, false);
}
public static Range greaterThan(final BigDecimal lower) {
return new Range(lower, false, null, false);
}
public static Range atLeast(final BigDecimal lower) {
return new Range(lower, true, null, false);
}
public static Range lessThan(final BigDecimal upper) {
return new Range(null, false, upper, false);
}
public static Range atMost(final BigDecimal upper) {
return new Range(null, false, upper, true);
}
public static Range all() {
return new Range(null, false, null, false);
}
public static Range range(final BigDecimal lower, final boolean lowerInclusive, final BigDecimal upper,
final boolean upperInclusive) {
return new Range(lower, lowerInclusive, upper, upperInclusive);
}
final BigDecimal min;
final boolean minInclusive;
final BigDecimal max;
final boolean maxInclusive;
private Range(final BigDecimal min, final boolean minInclusive, final BigDecimal max, final boolean maxInclusive) {
this.min = min;
this.minInclusive = minInclusive;
this.max = max;
this.maxInclusive = maxInclusive;
}
public boolean contains(final BigDecimal value) {
final boolean minMatch;
if (min == null) {
minMatch = true;
} else {
int cmp = value.compareTo(min);
if (minInclusive) {
minMatch = cmp == 0 || cmp == 1;
} else {
minMatch = cmp == 1;
}
}
if (!minMatch) {
return false;
}
final boolean maxMatch;
if (max == null) {
maxMatch = true;
} else {
int cmp = value.compareTo(max);
if (maxInclusive) {
maxMatch = cmp == 0 || cmp == -1;
} else {
maxMatch = cmp == -1;
}
}
if (!maxMatch) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,182 @@
/**
* 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.scale.internal;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.transform.AbstractFileTransformationService;
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;
/**
* The implementation of {@link TransformationService} which transforms the
* input by matching it between limits of ranges in a scale file
*
* @author Gaël L'hopital
* @author Markus Rathgeb - drop usage of Guava
*/
@Component(immediate = true, service = TransformationService.class, property = { "smarthome.transform=SCALE" })
public class ScaleTransformationService extends AbstractFileTransformationService<Map<Range, String>> {
private final Logger logger = LoggerFactory.getLogger(ScaleTransformationService.class);
/** RegEx to extract a scale definition */
private static final Pattern LIMITS_PATTERN = Pattern.compile("(\\[|\\])(.*)\\.\\.(.*)(\\[|\\])");
private static final String NON_NUMBER = "NaN";
private static final String FORMAT = "format";
private static final String FORMAT_VALUE = "%value%";
private static final String FORMAT_LABEL = "%label%";
/** Inaccessible range used to store presentation format ]0..0[ */
private static final Range FORMAT_RANGE = Range.range(BigDecimal.ZERO, false, BigDecimal.ZERO, false);
/**
* The implementation of {@link OrderedProperties} that let access
* properties in the same order than presented in the source file
* by using the orderedKeys function.
*
* This implementation is limited to the sole purpose of the class
* (e.g. it does not handle removing elements)
*
* @author Gaël L'hopital
*/
static class OrderedProperties extends Properties {
private static final long serialVersionUID = 3860553217028220119L;
private final HashSet<Object> keys = new LinkedHashSet<>();
Set<Object> orderedKeys() {
return keys;
}
@Override
public Enumeration<Object> keys() {
return Collections.<Object> enumeration(keys);
}
@Override
public Object put(Object key, Object value) {
keys.add(key);
return super.put(key, value);
}
}
/**
* Performs transformation of the input <code>source</code>
*
* The method transforms the input <code>source</code> by matching searching
* the range where it fits i.e. [min..max]=value or ]min..max]=value
*
* @param properties the list of properties defining all the available ranges
* @param source the input to transform
*
*/
@Override
protected String internalTransform(Map<Range, String> data, String source) throws TransformationException {
try {
final BigDecimal value = new BigDecimal(source);
return formatResult(data, source, value);
} catch (NumberFormatException e) {
// Scale can only be used with numeric inputs, so lets try to see if ever its a valid quantity type
try {
final QuantityType<?> quantity = new QuantityType<>(source);
return formatResult(data, source, quantity.toBigDecimal());
} catch (NumberFormatException e2) {
String nonNumeric = data.get(null);
if (nonNumeric != null) {
return nonNumeric;
} else {
throw new TransformationException(
"Scale must be used with numeric inputs, valid quantity types or a 'NaN' entry.");
}
}
}
}
private String formatResult(Map<Range, String> data, String source, final BigDecimal value)
throws TransformationException {
String format = data.get(FORMAT_RANGE);
String result = getScaleResult(data, source, value);
return format.replaceAll(FORMAT_VALUE, source).replaceAll(FORMAT_LABEL, result);
}
private String getScaleResult(Map<Range, String> data, String source, final BigDecimal value)
throws TransformationException {
return data.entrySet().stream().filter(entry -> entry.getKey() != null && entry.getKey().contains(value))
.findFirst().map(Map.Entry::getValue)
.orElseThrow(() -> new TransformationException("No matching range for '" + source + "'"));
}
@Override
protected Map<Range, String> internalLoadTransform(String filename) throws TransformationException {
try (FileReader reader = new FileReader(filename)) {
final Map<Range, String> data = new LinkedHashMap<>();
data.put(FORMAT_RANGE, FORMAT_LABEL);
final OrderedProperties properties = new OrderedProperties();
properties.load(reader);
for (Object orderedKey : properties.orderedKeys()) {
final String entry = (String) orderedKey;
final String value = properties.getProperty(entry);
final Matcher matcher = LIMITS_PATTERN.matcher(entry);
if (matcher.matches() && (matcher.groupCount() == 4)) {
final boolean lowerInclusive = matcher.group(1).equals("[");
final boolean upperInclusive = matcher.group(4).equals("]");
final String lowLimit = matcher.group(2);
final String highLimit = matcher.group(3);
try {
final BigDecimal lowValue = lowLimit.isEmpty() ? null : new BigDecimal(lowLimit);
final BigDecimal highValue = highLimit.isEmpty() ? null : new BigDecimal(highLimit);
final Range range = Range.range(lowValue, lowerInclusive, highValue, upperInclusive);
data.put(range, value);
} catch (NumberFormatException ex) {
throw new TransformationException("Error parsing bounds: " + lowLimit + ".." + highLimit);
}
} else {
if (NON_NUMBER.equals(entry)) {
data.put(null, value);
} else if (FORMAT.equals(entry)) {
data.put(FORMAT_RANGE, value);
} else {
logger.warn("Scale transform file '{}' does not comply with syntax for entry : '{}', '{}'",
filename, entry, value);
}
}
}
return data;
} catch (final IOException ex) {
throw new TransformationException("An error occurred while opening file.", ex);
}
}
}

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.scale.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 ScaleTransformationservice on a ItemChannelLink
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
public class ScaleTransformationProfile implements StateProfile {
public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID(
TransformationService.TRANSFORM_PROFILE_SCOPE, "SCALE");
private final Logger logger = LoggerFactory.getLogger(ScaleTransformationProfile.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 ScaleTransformationProfile(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.scale.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 scale transformation service
*
* @author Stefan Triller - initial contribution
*
*/
@NonNullByDefault
@Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
public class ScaleTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider {
@NonNullByDefault({})
private TransformationService service;
@Override
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
return Arrays.asList(ProfileTypeBuilder.newState(ScaleTransformationProfile.PROFILE_TYPE_UID,
ScaleTransformationProfile.PROFILE_TYPE_UID.getId()).build());
}
@Override
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
ProfileContext profileContext) {
return new ScaleTransformationProfile(callback, profileContext, service);
}
@Override
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
return Arrays.asList(ScaleTransformationProfile.PROFILE_TYPE_UID);
}
@Reference(target = "(smarthome.transform=SCALE)")
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:SCALE">
<parameter name="function" type="text" required="true">
<label>Filename</label>
<description>Filename containing the scale mappings.</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,157 @@
/**
* 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.scale.internal;
import static org.junit.Assert.fail;
import java.util.Locale;
import javax.measure.quantity.Dimensionless;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.transform.TransformationException;
/**
* @author Gaël L'hopital - Initial contribution
*/
public class ScaleTransformServiceTest {
private ScaleTransformationService processor;
@Before
public void init() {
processor = new ScaleTransformationService() {
@Override
protected Locale getLocale() {
return Locale.US;
}
};
}
@Test
public void testTransformByScale() throws TransformationException {
// need to be sure we'll have the german version
String existingscale = "scale/humidex_de.scale";
String source = "10";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("nicht wesentlich", transformedResponse);
existingscale = "scale/limits.scale";
source = "10";
transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("middle", transformedResponse);
}
@Test
public void testTransformByScaleLimits() throws TransformationException {
String existingscale = "scale/limits.scale";
// Testing upper bound opened range
String source = "500";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("extreme", transformedResponse);
// Testing lower bound opened range
source = "-10";
transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("low", transformedResponse);
// Testing unfinite up and down range
existingscale = "scale/catchall.scale";
source = "-10";
transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("catchall", transformedResponse);
}
@Test
public void testTransformByScaleUndef() throws TransformationException {
// check that for undefined/non numeric value we return empty string
// Issue #1107
String existingscale = "scale/humidex_fr.scale";
String source = "-";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("", transformedResponse);
}
@Test
public void testTransformByScaleErrorInBounds() throws TransformationException {
// the tested file contains inputs that generate a conversion error of the bounds
// of range
String existingscale = "scale/erroneous.scale";
String source = "15";
try {
@SuppressWarnings("unused")
String transformedResponse = processor.transform(existingscale, source);
fail();
} catch (TransformationException e) {
// awaited result
}
}
@Test
public void testTransformByScaleErrorInValue() throws TransformationException {
// checks that an error is raised when trying to scale an erroneous value
String existingscale = "scale/evaluationorder.scale";
String source = "azerty";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("", transformedResponse);
}
@Test
public void testEvaluationOrder() throws TransformationException {
// Ensures that only first matching scale as presented in the file is taken in account
String evaluationOrder = "scale/evaluationorder.scale";
// This value matches two lines of the scale file
String source = "12";
String transformedResponse = processor.transform(evaluationOrder, source);
Assert.assertEquals("first", transformedResponse);
}
@Test
public void testTransformQuantityType() throws TransformationException {
QuantityType<Dimensionless> airQuality = new QuantityType<>("992 ppm");
String aqScaleFile = "scale/netatmo_aq.scale";
String expected = "Correcte (992 ppm) !";
String transformedResponse = processor.transform(aqScaleFile, airQuality.toString());
Assert.assertEquals(expected, transformedResponse);
}
@Test
public void testCatchNonNumericValue() throws TransformationException {
// checks that an error is raised when trying to scale an erroneous value
String existingscale = "scale/catchnonnumeric.scale";
String source = "azerty";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("Non Numeric", transformedResponse);
}
@Test
public void testTransformAndFormat() throws TransformationException {
String existingscale = "scale/netatmo_aq.scale";
String source = "992";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("Correcte (992) !", transformedResponse);
}
@Test
public void testValueExceedsRange() throws TransformationException {
String existingscale = "scale/humidex.scale";
String source = "200";
String transformedResponse = processor.transform(existingscale, source);
Assert.assertEquals("", transformedResponse);
}
}