Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

[mqtt] Rollershutter STOP command, outgoing transform, chained incoming transformations #6695

Merged
merged 3 commits into from Jan 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -35,7 +35,6 @@
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceAttributes.ReadyState;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceCallback;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceStatsAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.Node;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.NodeAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.Property;
Expand Down Expand Up @@ -248,8 +247,8 @@ public void parseHomieTree() throws InterruptedException, ExecutionException, Ti
// Create a Homie Device object. Because spied Nodes are required for call verification,
// the full Device constructor need to be used and a ChildMap object need to be created manually.
ChildMap<Node> nodeMap = new ChildMap<>();
Device device = spy(new Device(ThingChannelConstants.testHomieThing, callback, new DeviceAttributes(),
new DeviceStatsAttributes(), nodeMap, Device.createDeviceStatisticsListener(handler)));
Device device = spy(
new Device(ThingChannelConstants.testHomieThing, callback, new DeviceAttributes(), nodeMap));

// Intercept creating a node in initialize()->start() and inject a spy'ed node.
doAnswer(this::createSpyNode).when(device).createNode(any());
Expand Down
Expand Up @@ -192,7 +192,8 @@ public void receiveDecimalFractionalTest() throws InterruptedException, Executio

@Test
public void receivePercentageTest() throws InterruptedException, ExecutionException, TimeoutException {
PercentageValue value = new PercentageValue(new BigDecimal(-100), new BigDecimal(100), new BigDecimal(10));
PercentageValue value = new PercentageValue(new BigDecimal(-100), new BigDecimal(100), new BigDecimal(10), null,
null);
ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
c.start(connection, mock(ScheduledExecutorService.class), 100);

Expand Down
Expand Up @@ -106,7 +106,7 @@ public void initialize() throws MqttException {

thingHandler.initialize();
ChannelState channelConfig = thingHandler.getChannelState(textChannelUID);
assertThat(channelConfig.transformations.get(0).pattern, is(jsonPathPattern));
assertThat(channelConfig.transformationsIn.get(0).pattern, is(jsonPathPattern));
}

@SuppressWarnings("null")
Expand All @@ -118,7 +118,7 @@ public void processMessageWithJSONPath() throws Exception {
ChannelState channelConfig = thingHandler.getChannelState(textChannelUID);
channelConfig.setChannelStateUpdateListener(thingHandler);

ChannelStateTransformation transformation = channelConfig.transformations.get(0);
ChannelStateTransformation transformation = channelConfig.transformationsIn.get(0);

byte payload[] = jsonPathJSON.getBytes();
assertThat(transformation.pattern, is(jsonPathPattern));
Expand Down
Expand Up @@ -34,7 +34,6 @@
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.Device;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceAttributes.ReadyState;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.DeviceStatsAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.Node;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.NodeAttributes;
import org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homie300.Property;
Expand Down Expand Up @@ -132,8 +131,7 @@ public void setUp() {
thingHandler = spy(handler);
thingHandler.setCallback(callback);
final Device device = new Device(thing.getUID(), thingHandler, spy(new DeviceAttributes()),
spy(new DeviceStatsAttributes()), new ChildMap<>(),
Device.createDeviceStatisticsListener(thingHandler));
spy(new ChildMap<>()));
thingHandler.setInternalObjects(spy(device),
spy(new DelayedBatchProcessing<Object>(500, thingHandler, scheduler)));

Expand Down Expand Up @@ -366,35 +364,4 @@ public void propertiesChanged() throws InterruptedException, ExecutionException
assertThat(properties.get(MqttBindingConstants.HOMIE_PROPERTY_VERSION), is("3.0"));
assertThat(properties.size(), is(1));
}

@Test
public void heartBeatInterval()
throws InterruptedException, ExecutionException, NoSuchFieldException, SecurityException {
thingHandler.device.initialize("homie", "device", new ArrayList<Channel>());
thingHandler.connection = connection;

// Inject spy'ed subscriber object
doAnswer(this::createSubscriberAnswer).when(thingHandler.device.stats).createSubscriber(any(), any(), any(),
anyBoolean());
doAnswer(this::createSubscriberAnswer).when(thingHandler.device.attributes).createSubscriber(any(), any(),
any(), anyBoolean());

thingHandler.device.attributes.state = ReadyState.ready;
thingHandler.device.attributes.name = "device";
thingHandler.device.attributes.homie = "3.0";
thingHandler.device.attributes.nodes = new String[] {};

thingHandler.initialize();

assertThat(thingHandler.device.isInitialized(), is(true));

verify(thingHandler.device).attributesReceived(any(), any(), anyInt());
verify(thingHandler.device.stats).subscribeAndReceive(any(), any(), anyString(), any(), anyInt());

// Emulate a received value for the "interval" topic
thingHandler.device.stats.fieldChanged(DeviceStatsAttributes.class.getDeclaredField("interval"), 60);

verify(thingHandler).heartbeatIntervalChanged(anyInt());
verify(callback).thingUpdated(any());
}
}
Expand Up @@ -83,7 +83,7 @@ public void illegalNumberCommand() {

@Test(expected = IllegalArgumentException.class)
public void illegalPercentCommand() {
PercentageValue v = new PercentageValue(null, null, null);
PercentageValue v = new PercentageValue(null, null, null, null, null);
v.update(OnOffType.OFF);
}

Expand All @@ -95,7 +95,7 @@ public void illegalOnOffCommand() {

@Test(expected = IllegalArgumentException.class)
public void illegalPercentUpdate() {
PercentageValue v = new PercentageValue(null, null, null);
PercentageValue v = new PercentageValue(null, null, null, null, null);
v.update(new DecimalType(101.0));
}

Expand Down Expand Up @@ -180,16 +180,35 @@ public void rollershutterUpdate() {

@Test
public void percentCalc() {
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0));
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0), null,
null);
v.update(new DecimalType(110.0));
assertThat((PercentType) v.getChannelState(), is(new PercentType(100)));
assertThat(v.getMQTTpublishValue(), is("110"));
v.update(new DecimalType(10.0));
assertThat((PercentType) v.getChannelState(), is(new PercentType(0)));
assertThat(v.getMQTTpublishValue(), is("10"));

v.update(OnOffType.ON);
assertThat((PercentType) v.getChannelState(), is(new PercentType(100)));
v.update(OnOffType.OFF);
assertThat((PercentType) v.getChannelState(), is(new PercentType(0)));
}

@Test
public void percentCustomOnOff() {
PercentageValue v = new PercentageValue(new BigDecimal(0.0), new BigDecimal(100.0), new BigDecimal(1.0), "on",
"off");
v.update(new StringType("on"));
assertThat((PercentType) v.getChannelState(), is(new PercentType(100)));
v.update(new StringType("off"));
assertThat((PercentType) v.getChannelState(), is(new PercentType(0)));
}

@Test
public void decimalCalc() {
PercentageValue v = new PercentageValue(new BigDecimal(0.1), new BigDecimal(1.0), new BigDecimal(0.1));
PercentageValue v = new PercentageValue(new BigDecimal(0.1), new BigDecimal(1.0), new BigDecimal(0.1), null,
null);
v.update(new DecimalType(1.0));
assertThat((PercentType) v.getChannelState(), is(new PercentType(100)));
v.update(new DecimalType(0.1));
Expand All @@ -200,7 +219,8 @@ public void decimalCalc() {

@Test(expected = IllegalArgumentException.class)
public void percentCalcInvalid() {
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0));
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0), null,
null);
v.update(new DecimalType(9.0));
}
}
Expand Up @@ -5,25 +5,53 @@
xsi:schemaLocation="http://eclipse.org/smarthome/schemas/config-description/v1.0.0 http://eclipse.org/smarthome/schemas/config-description-1.0.0.xsd">

<config-description uri="thing-type:mqtt:color_channel">
<parameter name="stateTopic" type="text">
<label>MQTT state topic</label>
<description>An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.</description>
</parameter>
<parameter name="commandTopic" type="text">
<label>MQTT command topic</label>
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
</parameter>
<parameter name="transformationPattern" type="text">
<label>Incoming value transformation</label>
<description>Applies a transformation to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. Any supported transformation service can be used.</description>
<advanced>true</advanced>
</parameter>
<parameter name="formatBeforePublish" type="text">
<label>Outgoing value format</label>
<description>Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f".</description>
<advanced>true</advanced>
<default>%s</default>
</parameter>
<parameter-group name="transformations">
<label>Transform values</label>
<description>These configuration parameters allow you to alter a value before it is published to MQTT or before a received value is assigned to an item.</description>
<advanced>true</advanced>
</parameter-group>

<parameter name="stateTopic" type="text">
<label>MQTT state topic</label>
<description>An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.</description>
</parameter>
<parameter name="commandTopic" type="text">
<label>MQTT command topic</label>
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
</parameter>
<parameter name="transformationPattern" type="text" groupName="transformations">
<label>Incoming value transformations</label>
<description><![CDATA[
Applies transformations to an incoming MQTT topic value.
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
a json {device: {status: { temperature: 23.2 }}}.

You can chain transformations by separating them with the intersection character ∩.
]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="transformationPatternOut" type="text" groupName="transformations">
<label>Outgoing value transformation</label>
<description><![CDATA[
Applies a transformation before publishing a MQTT topic value.

Transformations are specialised in extracting a value, but some transformations like
the MAP one could be useful.
]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="formatBeforePublish" type="text" groupName="transformations">
<label>Outgoing value format</label>
<description><![CDATA[
Format a value before it is published to the MQTT broker.
The default is to just pass the channel/item state.

If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s".
If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f".
]]></description>
<advanced>true</advanced>
<default>%s</default>
</parameter>
<parameter name="retained" type="boolean">
<label>Retained</label>
<description>The value will be published to the command topic as retained message. A retained value stays on the broker and can even be seen by MQTT clients that are subscribing at a later point in time.</description>
Expand Down
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="http://eclipse.org/smarthome/schemas/config-description/v1.0.0"
xsi:schemaLocation="http://eclipse.org/smarthome/schemas/config-description/v1.0.0 http://eclipse.org/smarthome/schemas/config-description-1.0.0.xsd">

<config-description uri="thing-type:mqtt:dimmer_channel">
<parameter-group name="transformations">
<label>Transform values</label>
<description>These configuration parameters allow you to alter a value before it is published to MQTT or before a received value is assigned to an item.</description>
<advanced>true</advanced>
</parameter-group>

<parameter name="stateTopic" type="text">
<label>MQTT state topic</label>
<description>An MQTT topic that this thing will subscribe to, to receive the state. This can be left empty, the channel will be state-less command-only channel.</description>
</parameter>
<parameter name="commandTopic" type="text">
<label>MQTT command topic</label>
<description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch.</description>
</parameter>
<parameter name="transformationPattern" type="text" groupName="transformations">
<label>Incoming value transformations</label>
<description><![CDATA[
Applies transformations to an incoming MQTT topic value.
A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for
a json {device: {status: { temperature: 23.2 }}}.

You can chain transformations by separating them with the intersection character ∩.
]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="transformationPatternOut" type="text" groupName="transformations">
<label>Outgoing value transformation</label>
<description><![CDATA[
Applies a transformation before publishing a MQTT topic value.

Transformations are specialised in extracting a value, but some transformations like
the MAP one could be useful.
]]></description>
<advanced>true</advanced>
</parameter>
<parameter name="formatBeforePublish" type="text" groupName="transformations">
<label>Outgoing value format</label>
<description><![CDATA[
Format a value before it is published to the MQTT broker.
The default is to just pass the channel/item state.

If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s".
If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f".
]]></description>
<advanced>true</advanced>
<default>%s</default>
</parameter>
<parameter name="retained" type="boolean">
<label>Retained</label>
<description>The value will be published to the command topic as retained message. A retained value stays on the broker and can even be seen by MQTT clients that are subscribing at a later point in time.</description>
<default>false</default>
<advanced>true</advanced>
</parameter>

<parameter name="min" type="decimal">
<label>Absolute minimum</label>
<description>This configuration represents the minimum of the allowed range. For a percentage channel that equals zero percent.</description>
</parameter>
<parameter name="max" type="decimal">
<label>Absolute maximum</label>
<description>This configuration represents the maximum of the allowed range. For a percentage channel that equals one-hundred percent.</description>
</parameter>
<parameter name="step" type="decimal">
<label>Delta value</label>
<description>A number/dimmer channel can receive Increase/Decrease commands and computes the target number by adding or subtracting this delta value.</description>
<default>1.0</default>
<advanced>true</advanced>
</parameter>
<parameter name="on" type="text">
<label>Custom On/Open value</label>
<description>A number (like 1, 10) or a string (like "enabled") that is additionally recognised as on/open state. You can use this parameter for a second keyword, next to ON (OPEN respectively on a Contact).</description>
<default>1</default>
</parameter>
<parameter name="off" type="text">
<label>Custom Off/Closed value</label>
<description>A number (like 0, -10) or a string (like "disabled") that is additionally recognised as off/closed state. You can use this parameter for a second keyword, next to OFF (CLOSED respectively on a Contact).</description>
<default>0</default>
</parameter>
</config-description>
</config-description:config-descriptions>