diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java index 144fcf72725..fe864be0635 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java @@ -83,6 +83,8 @@ public class ComponentFactory { return new Sensor(componentConfiguration, newStyleChannels); case "switch": return new Switch(componentConfiguration, newStyleChannels); + case "text": + return new Text(componentConfiguration, newStyleChannels); case "update": return new Update(componentConfiguration, newStyleChannels); case "vacuum": diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Text.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Text.java new file mode 100644 index 00000000000..690123b5007 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Text.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2024 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.binding.mqtt.homeassistant.internal.component; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mqtt.generic.values.TextValue; +import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType; +import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration; + +import com.google.gson.annotations.SerializedName; + +/** + * A MQTT select, following the https://www.home-assistant.io/integrations/text.mqtt/ specification. + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class Text extends AbstractComponent { + public static final String TEXT_CHANNEL_ID = "text"; + + public static final String MODE_TEXT = "text"; + public static final String MODE_PASSWORD = "password"; + + /** + * Configuration class for MQTT component + */ + static class ChannelConfiguration extends AbstractChannelConfiguration { + ChannelConfiguration() { + super("MQTT Text"); + } + + @SerializedName("command_template") + protected @Nullable String commandTemplate; + @SerializedName("command_topic") + protected String commandTopic = ""; + @SerializedName("state_topic") + protected @Nullable String stateTopic; + + // openHAB has no way to handle these restrictions in its UI + protected int min = 0; // Minimum and maximum length of the display we're controlling + protected int max = 255; + protected @Nullable String pattern; // Regular expression + protected String mode = MODE_TEXT; // Presumably for a password, it should mask any controls in the UI + } + + public Text(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) { + super(componentConfiguration, ChannelConfiguration.class, newStyleChannels); + + TextValue value = new TextValue(); + + buildChannel(TEXT_CHANNEL_ID, ComponentChannelType.STRING, value, getName(), + componentConfiguration.getUpdateListener()) + .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate()) + .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(), + channelConfiguration.getQos(), channelConfiguration.commandTemplate) + .inferOptimistic(false).build(); + finalizeChannels(); + } +} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/TextTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/TextTests.java new file mode 100644 index 00000000000..7e985f0a431 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/TextTests.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2024 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.binding.mqtt.homeassistant.internal.component; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.mqtt.generic.values.TextValue; +import org.openhab.core.library.types.StringType; + +/** + * Tests for {@link TExt} + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class TextTests extends AbstractComponentTests { + public static final String CONFIG_TOPIC = "text/0x54ef44100064b266"; + + @SuppressWarnings("null") + @Test + public void test() { + var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """ + { + "command_topic": "txt/cmd", + "state_topic": "txt/state", + "min": 2, + "max": 20 + } + """); + + assertThat(component.channels.size(), is(1)); + assertThat(component.getName(), is("MQTT Text")); + + assertChannel(component, Text.TEXT_CHANNEL_ID, "txt/state", "txt/cmd", "MQTT Text", TextValue.class); + + publishMessage("txt/state", "stuff"); + assertState(component, Text.TEXT_CHANNEL_ID, new StringType("stuff")); + + component.getChannel(Text.TEXT_CHANNEL_ID).getState().publishValue(new StringType("near")); + assertPublished("txt/cmd", "near"); + } + + @Override + protected Set getConfigTopics() { + return Set.of(CONFIG_TOPIC); + } +}