From f25b0d0fb93c425baca74828e07338d3cc729cb2 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Sun, 19 Nov 2023 09:06:54 -0700 Subject: [PATCH] [mqtt.homeassistant] Add support for Button component (#15892) * [mqtt.homeassistant] Add support for Button component * use a StringValue instead of an OnOffValue --------- Signed-off-by: Cody Cutrer --- .../internal/component/Button.java | 58 +++++++++++++++ .../internal/component/ComponentFactory.java | 2 + .../component/AbstractComponentTests.java | 9 +++ .../internal/component/ButtonTests.java | 72 +++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Button.java create mode 100644 bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ButtonTests.java diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Button.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Button.java new file mode 100644 index 0000000000000..97aa60f01b056 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Button.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2023 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.config.dto.AbstractChannelConfiguration; + +import com.google.gson.annotations.SerializedName; + +/** + * An MQTT button, following the https://www.home-assistant.io/integrations/button.mqtt/ specification. + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class Button extends AbstractComponent { + public static final String BUTTON_CHANNEL_ID = "button"; + + /** + * Configuration class for MQTT component + */ + static class ChannelConfiguration extends AbstractChannelConfiguration { + ChannelConfiguration() { + super("MQTT Button"); + } + + protected @Nullable Boolean optimistic; + + @SerializedName("command_topic") + protected @Nullable String commandTopic; + + @SerializedName("payload_press") + protected String payloadPress = "PRESS"; + } + + public Button(ComponentFactory.ComponentConfiguration componentConfiguration) { + super(componentConfiguration, ChannelConfiguration.class); + + TextValue value = new TextValue(new String[] { channelConfiguration.payloadPress }); + + buildChannel(BUTTON_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener()) + .commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(), + channelConfiguration.getQos()) + .build(); + } +} 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 9855eec9c49ce..6c95d5eeddecc 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 @@ -57,6 +57,8 @@ public static AbstractComponent createComponent(ThingUID thingUID, HaID haID, return new AlarmControlPanel(componentConfiguration); case "binary_sensor": return new BinarySensor(componentConfiguration); + case "button": + return new Button(componentConfiguration); case "camera": return new Camera(componentConfiguration); case "cover": diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponentTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponentTests.java index fc5d8c3cf1369..e2d88086db786 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponentTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponentTests.java @@ -230,6 +230,15 @@ protected void assertNotPublished(String mqttTopic, String payload) { ArgumentMatchers.eq(payload.getBytes(StandardCharsets.UTF_8)), anyInt(), anyBoolean()); } + /** + * Assert that nothing was published on given topic. + * + * @param mqttTopic Mqtt topic + */ + protected void assertNothingPublished(String mqttTopic) { + verify(bridgeConnection, never()).publish(eq(mqttTopic), any(), anyInt(), anyBoolean()); + } + /** * Publish payload to all subscribers on specified topic. * diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ButtonTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ButtonTests.java new file mode 100644 index 0000000000000..943a6c4b0f275 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ButtonTests.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2010-2023 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 static org.junit.jupiter.api.Assertions.assertThrows; + +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 Button} + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class ButtonTests extends AbstractComponentTests { + public static final String CONFIG_TOPIC = "button/0x847127fffe11dd6a_auto_lock_zigbee2mqtt"; + + @SuppressWarnings("null") + @Test + public void testButton() { + var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """ + { + "dev_cla":"restart", + "name":"Restart", + "entity_category":"config", + "cmd_t":"esphome/single-car-gdo/button/restart/command", + "avty_t":"esphome/single-car-gdo/status", + "uniq_id":"78e36d645710-button-ba0e8e32", + "dev":{ + "ids":"78e36d645710", + "name":"Single Car Garage Door Opener", + "sw":"esphome v2023.10.4 Nov 1 2023, 09:27:02", + "mdl":"esp32dev", + "mf":"espressif"} + } + """); + + assertThat(component.channels.size(), is(1)); + assertThat(component.getName(), is("Restart")); + + assertChannel(component, Button.BUTTON_CHANNEL_ID, "", "esphome/single-car-gdo/button/restart/command", + "Restart", TextValue.class); + + assertThrows(IllegalArgumentException.class, + () -> component.getChannel(Button.BUTTON_CHANNEL_ID).getState().publishValue(new StringType("ON"))); + assertNothingPublished("esphome/single-car-gdo/button/restart/command"); + component.getChannel(Button.BUTTON_CHANNEL_ID).getState().publishValue(new StringType("PRESS")); + assertPublished("esphome/single-car-gdo/button/restart/command", "PRESS"); + } + + @Override + protected Set getConfigTopics() { + return Set.of(CONFIG_TOPIC); + } +}