]> git.basschouten.com Git - openhab-addons.git/commitdiff
[mqtt.generic] Add optional stopCommandTopic for rollershutters (#17158)
authorCody Cutrer <cody@cutrer.us>
Tue, 30 Jul 2024 18:10:50 +0000 (12:10 -0600)
committerGitHub <noreply@github.com>
Tue, 30 Jul 2024 18:10:50 +0000 (20:10 +0200)
* [mqtt.generic] Add optional stopCommandTopic for rollershutters

Signed-off-by: Cody Cutrer <cody@cutrer.us>
bundles/org.openhab.binding.mqtt.generic/README.md
bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfig.java
bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfigBuilder.java
bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java
bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/rollershutter-channel-config.xml
bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/i18n/mqtt.properties
bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java

index 46aae9e0eeac7b75ddef289eeac50cace3c35321..8aa4f81913e1841b205012451e8ed677d6a6df14 100644 (file)
@@ -199,6 +199,7 @@ The channel expects values on the corresponding MQTT topic to be in this format
 - **on**: An optional string (like "Open") that is recognized as `UP` state.
 - **off**: An optional string (like "Close") that is recognized as `DOWN` state.
 - **stop**: An optional string (like "Stop") that is recognized as `STOP` state.
+- **stopCommandTopic**: An optional topic to send `STOP` commands to. If not set, `STOP` commands are sent to the main **commandTopic**.
 
 Internally `UP` is converted to 0%, `DOWN` to 100%.
 If strings are defined for these values, they are used for sending commands to the broker, too.
index d3adf2677861af70fd9801f4d4e03996829e9bc4..b907052019c888052ca3e2c10a386e82fc7f8ee1 100644 (file)
@@ -32,6 +32,7 @@ public class ChannelConfig {
     /** This is either a state topic or a trigger topic, depending on {@link #trigger}. */
     public String stateTopic = "";
     public String commandTopic = "";
+    public String stopCommandTopic = "";
 
     /**
      * If true, the channel state is not updated on a new message.
index 9c4fe483669d686d89a1888462a73ab8a9579a50..8df35ecb57aef15f5f72d1785bde632376bdcc71 100644 (file)
@@ -59,6 +59,13 @@ public class ChannelConfigBuilder {
         return this;
     }
 
+    public ChannelConfigBuilder withStopCommandTopic(@Nullable String topic) {
+        if (topic != null) {
+            config.stopCommandTopic = topic;
+        }
+        return this;
+    }
+
     public ChannelConfigBuilder withRetain(boolean retain) {
         config.retained = retain;
         return this;
index 832f94397e13a39cf52b4a2394ddc332e133cf37..06ea08fbdbc531cdbf9f08f68fea1f3f2dcb0dc2 100644 (file)
@@ -30,6 +30,7 @@ import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
 import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
 import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StopMoveType;
 import org.openhab.core.library.types.StringType;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.types.Command;
@@ -420,7 +421,13 @@ public class ChannelState implements MqttMessageSubscriber {
 
         int qos = (config.qos != null) ? config.qos : connection.getQos();
 
-        return connection.publish(config.commandTopic, commandString.getBytes(), qos, config.retained);
+        String commandTopic;
+        if (command.equals(StopMoveType.STOP) && !config.stopCommandTopic.isEmpty()) {
+            commandTopic = config.stopCommandTopic;
+        } else {
+            commandTopic = config.commandTopic;
+        }
+        return connection.publish(commandTopic, commandString.getBytes(), qos, config.retained);
     }
 
     /**
index b87161d70a3bcae01f8b2704fc6a719aab1e07cf..25f78a194b1cd131ce9145e8eb25ed423dac5277 100644 (file)
                        <description>An MQTT topic that this thing will send a command to. If not set, this will be a read-only
                                rollershutter.</description>
                </parameter>
+               <parameter name="stopCommandTopic" type="text">
+                       <label>MQTT Stop Command Topic</label>
+                       <description>An MQTT topic that this thing will send a STOP command to. If not set, it will send STOP commands to the
+                               main commandTopic.</description>
+               </parameter>
                <parameter name="transformationPattern" type="text" groupName="transformations">
                        <label>Incoming Value Transformations</label>
                        <description><![CDATA[
index c7e6188782aa8e28bbe2e65ae9549827bd8d4287..691ed7e0a92541070e0b6e0f5d8f2545d8dfe45d 100644 (file)
@@ -156,6 +156,8 @@ thing-type.config.mqtt.rollershutter_channel.stateTopic.label = MQTT State Topic
 thing-type.config.mqtt.rollershutter_channel.stateTopic.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.
 thing-type.config.mqtt.rollershutter_channel.stop.label = Stop Command
 thing-type.config.mqtt.rollershutter_channel.stop.description = A string (like "STOP") that is sent when commanding the rollershutter to stop.
+thing-type.config.mqtt.rollershutter_channel.stopCommandTopic.label = MQTT Stop Command Topic
+thing-type.config.mqtt.rollershutter_channel.stopCommandTopic.description = An MQTT topic that this thing will send a STOP command to. If not set, it will send STOP commands to the main commandTopic.
 thing-type.config.mqtt.rollershutter_channel.transformExtentsToString.label = Transform Commands at Extents to String
 thing-type.config.mqtt.rollershutter_channel.transformExtentsToString.description = If a command is 0 or 100, send that as UP or DOWN commands instead. Useful if your device doesn't support going to a specific position - only opening or closing, but you have front ends (say HomeKit) or rules that will only send percentage commands instead of UP/DOWN.
 thing-type.config.mqtt.rollershutter_channel.transformationPattern.label = Incoming Value Transformations
index 018abe734b51be47981ea13cb5e45b77b8783b78..69c96ea2e969b4001e98f8f19477db48903c8a0f 100644 (file)
@@ -52,6 +52,7 @@ import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
 import org.openhab.core.library.types.HSBType;
 import org.openhab.core.library.types.PercentType;
 import org.openhab.core.library.types.RawType;
+import org.openhab.core.library.types.StopMoveType;
 import org.openhab.core.library.types.StringType;
 import org.openhab.core.library.unit.Units;
 import org.openhab.core.thing.ChannelUID;
@@ -134,6 +135,41 @@ public class ChannelStateTests {
         verify(connectionMock).unsubscribe(eq("state"), eq(c));
     }
 
+    @Test
+    public void publishStopTest() throws Exception {
+        ChannelConfig config = ChannelConfigBuilder.create("state", "command").build();
+        config.stop = "STOP";
+        ChannelState c = spy(new ChannelState(config, channelUIDMock, textValue, channelStateUpdateListenerMock));
+
+        c.start(connectionMock, scheduler, 0).get(50, TimeUnit.MILLISECONDS);
+        verify(connectionMock).subscribe(eq("state"), eq(c));
+
+        c.publishValue(StopMoveType.STOP).get();
+        verify(connectionMock).publish(eq("command"), argThat(p -> Arrays.equals(p, "STOP".getBytes())), anyInt(),
+                eq(false));
+
+        c.stop().get();
+        verify(connectionMock).unsubscribe(eq("state"), eq(c));
+    }
+
+    @Test
+    public void publishStopSeparateTopicTest() throws Exception {
+        ChannelConfig config = ChannelConfigBuilder.create("state", "command").withStopCommandTopic("stopCommand")
+                .build();
+        config.stop = "STOP";
+        ChannelState c = spy(new ChannelState(config, channelUIDMock, textValue, channelStateUpdateListenerMock));
+
+        c.start(connectionMock, scheduler, 0).get(50, TimeUnit.MILLISECONDS);
+        verify(connectionMock).subscribe(eq("state"), eq(c));
+
+        c.publishValue(StopMoveType.STOP).get();
+        verify(connectionMock).publish(eq("stopCommand"), argThat(p -> Arrays.equals(p, "STOP".getBytes())), anyInt(),
+                eq(false));
+
+        c.stop().get();
+        verify(connectionMock).unsubscribe(eq("state"), eq(c));
+    }
+
     @Test
     public void receiveWildcardTest() throws Exception {
         ChannelState c = spy(new ChannelState(ChannelConfigBuilder.create("state/+/topic", "command").build(),