From 8d9c65c8c3c6eb28a556c12cabdbb15d92a3440d Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 24 Mar 2025 11:53:59 -0600 Subject: [PATCH] [mqtt.homeassistant] Use default strings for all commands and states (#18383) * [mqtt.homeassistant] Use default strings for all commands Signed-off-by: Cody Cutrer --- .../mqtt/generic/values/TextValue.java | 72 ++++++++++----- .../README.md | 61 +++++++------ .../internal/component/AlarmControlPanel.java | 89 +++++++++++-------- .../internal/component/Button.java | 8 +- .../internal/component/Cover.java | 39 +++++--- .../internal/component/DeviceTrigger.java | 6 +- .../internal/component/Lock.java | 61 ++++++++----- .../internal/component/Vacuum.java | 46 ++++++---- .../internal/component/Valve.java | 59 ++++++------ .../component/AlarmControlPanelTests.java | 6 +- .../internal/component/ButtonTests.java | 38 ++++++++ .../component/DeviceTriggerTests.java | 7 +- .../internal/component/LockTests.java | 22 ++--- 13 files changed, 331 insertions(+), 183 deletions(-) diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java index 6c7f0746ef..1c321b49c4 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java @@ -14,8 +14,9 @@ package org.openhab.binding.mqtt.generic.values; import static java.util.function.Predicate.not; +import java.util.LinkedHashMap; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -38,11 +39,33 @@ import org.openhab.core.types.UnDefType; */ @NonNullByDefault public class TextValue extends Value { - private final @Nullable Set states; - private final @Nullable Set commands; + private final @Nullable Map states; + private final @Nullable Map commands; protected @Nullable String nullValue = null; + /** + * Create a string value with a limited number of allowed states and commands. + * + * @param states Allowed states. The key is the value that is received from MQTT, + * and the value is how matching values will be presented in openHAB. + * @param commands Allowed commands. The key is the value that will be received by + * openHAB, and the value is how matching commands will be sent to MQTT. + */ + public TextValue(Map states, Map commands) { + super(CoreItemFactory.STRING, List.of(StringType.class)); + if (!states.isEmpty()) { + this.states = new LinkedHashMap(states); + } else { + this.states = null; + } + if (!commands.isEmpty()) { + this.commands = new LinkedHashMap(commands); + } else { + this.commands = null; + } + } + /** * Create a string value with a limited number of allowed states and commands. * @@ -53,13 +76,15 @@ public class TextValue extends Value { */ public TextValue(String[] states, String[] commands) { super(CoreItemFactory.STRING, List.of(StringType.class)); - Set s = Stream.of(states).filter(not(String::isBlank)).collect(Collectors.toSet()); + Map s = Stream.of(states).filter(not(String::isBlank)) + .collect(Collectors.toMap(str -> str, str -> str, (a, b) -> a, LinkedHashMap::new)); if (!s.isEmpty()) { this.states = s; } else { this.states = null; } - Set c = Stream.of(commands).filter(not(String::isBlank)).collect(Collectors.toSet()); + Map c = Stream.of(commands).filter(not(String::isBlank)) + .collect(Collectors.toMap(str -> str, str -> str, (a, b) -> a, LinkedHashMap::new)); if (!c.isEmpty()) { this.commands = c; } else { @@ -89,10 +114,13 @@ public class TextValue extends Value { @Override public StringType parseCommand(Command command) throws IllegalArgumentException { - final Set commands = this.commands; + final Map commands = this.commands; String valueStr = command.toString(); - if (commands != null && !commands.contains(valueStr)) { - throw new IllegalArgumentException("Value " + valueStr + " not within range"); + if (commands != null) { + if (!commands.containsKey(valueStr)) { + throw new IllegalArgumentException("Value " + valueStr + " not within range"); + } + return new StringType(commands.get(valueStr)); } return new StringType(valueStr); } @@ -103,13 +131,17 @@ public class TextValue extends Value { return UnDefType.NULL; } - final Set states = this.states; + final Map states = this.states; String valueStr = command.toString(); - if (states != null && !states.contains(valueStr)) { - if (valueStr.isEmpty()) { - return UnDefType.NULL; + if (states != null) { + if (!states.containsKey(valueStr)) { + if (valueStr.isEmpty()) { + return UnDefType.NULL; + } else { + throw new IllegalArgumentException("Value " + valueStr + " not within range"); + } } else { - throw new IllegalArgumentException("Value " + valueStr + " not within range"); + return new StringType(states.get(valueStr)); } } return new StringType(valueStr); @@ -118,18 +150,16 @@ public class TextValue extends Value { /** * @return valid states. Can be null. */ - public @Nullable Set getStates() { + public @Nullable Map getStates() { return states; } @Override public StateDescriptionFragmentBuilder createStateDescription(boolean readOnly) { StateDescriptionFragmentBuilder builder = super.createStateDescription(readOnly); - final Set states = this.states; + final Map states = this.states; if (states != null) { - for (String state : states) { - builder = builder.withOption(new StateOption(state, state)); - } + states.forEach((ohState, mqttState) -> builder.withOption(new StateOption(ohState, ohState))); } return builder; } @@ -137,10 +167,10 @@ public class TextValue extends Value { @Override public CommandDescriptionBuilder createCommandDescription() { CommandDescriptionBuilder builder = super.createCommandDescription(); - final Set commands = this.commands; + final Map commands = this.commands; if (commands != null) { - for (String command : commands) { - builder = builder.withCommandOption(new CommandOption(command, command)); + for (String command : commands.keySet()) { + builder.withCommandOption(new CommandOption(command, command)); } } return builder; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/README.md b/bundles/org.openhab.binding.mqtt.homeassistant/README.md index 797f96521a..906fa1a8e0 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/README.md +++ b/bundles/org.openhab.binding.mqtt.homeassistant/README.md @@ -21,10 +21,10 @@ Note also that just because these tables show that a channel may be read/write, ### [Alarm Control Panel](https://www.home-assistant.io/integrations/alarm_control_panel.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|--------|-----|------------------------------------------------------------------------------------------------------------------------------------------| -| state | String | R/W | The current state of the alarm system, and the ability to change its state. Inspect the state and command descriptions for valid values. | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +| Channel ID | Type | R/W | Description | +|-----------------|--------|-----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| state | String | R/W | The current state of the alarm system, and the ability to change its state. Inspect the state and command descriptions for values supported by your device. Possible values are ARM_AWAY, ARM_CUSTOM_BYPASS, ARM_HOME, ARM_NIGHT, ARM_VACATION, DISARM, TRIGGER for commands; armed_away, armed_custom_bypass, armed_home, armed_night, armed_vacation, arming, disarmed, disarming, pending, triggered for states. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ### [Binary Sensor](https://www.home-assistant.io/integrations/binary_sensor.mqtt/) @@ -35,10 +35,10 @@ Note also that just because these tables show that a channel may be read/write, ### [Button](https://www.home-assistant.io/integrations/button.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|--------|-----|------------------------------------------------------------------------------| -| button | String | WO | Inspect the state description for the proper string to send (usually PRESS). | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +| Channel ID | Type | R/W | Description | +|-----------------|--------|-----|-----------------------------------------------------| +| button | String | WO | Send PRESS to activate the button. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ### [Camera](https://www.home-assistant.io/integrations/camera.mqtt/)
@@ -138,11 +138,11 @@ If a device has multiple device triggers for the same subtype (the particular bu ### [Lock](https://www.home-assistant.io/integrations/lock.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|--------|-----|-----------------------------------------------------------------------------------------------------------------------------------------------| -| lock | Switch | R/W | Lock/unlocked state. | -| state | String | R/W | Additional states may be supported such as jammed, or opening the door directly. Inspect the state and command descriptions for availability. | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +| Channel ID | Type | R/W | Description | +|-----------------|--------|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| lock | Switch | R/W | Lock/unlocked state. | +| state | String | R/W | Additional states may be supported such as jammed, or opening the door directly. Inspect the state and command descriptions for values supported by your device. Possible values are LOCK, UNLOCK, OPEN for commands; JAMMED, LOCKED, LOCKING, UNLOCKED, UNLOCKING for states. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ### [Number](https://www.home-assistant.io/integrations/number.mqtt/) @@ -153,10 +153,12 @@ If a device has multiple device triggers for the same subtype (the particular bu ### [Scene](https://www.home-assistant.io/integrations/scene.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|--------|-----|-----------------------------------------------------------------------------------------------------------| -| scene | String | WO | Triggers a scene on the device. Inspect the state description for the proper string to send (usually ON). | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +If a device has multiple scenes, they will only show up as a single channel. You send the name of a given scene to activate it. + +| Channel ID | Type | R/W | Description | +|-----------------|--------|-----|------------------------------------------------------------------------------------------------| +| scene | String | WO | Triggers a scene on the device. Inspect the command description for the proper string to send. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ### [Select](https://www.home-assistant.io/integrations/select.mqtt/) @@ -203,21 +205,22 @@ The `json-attributes` channel for this component will always appear as part of c ### [Vacuum](https://www.home-assistant.io/integrations/vacuum.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|--------|-----|--------------------------------------------------------------------------------------------------| -| command | String | WO | Send a command to the vacuum. Inspect the state description for allowed values. | -| fan-speed | String | R/W | Set the fan speed. Inspect the state description fro allowed values. | -| custom-command | String | WO | Send an arbitrary command to the vacuum. This may be a raw command, or JSON. | -| battery-level | Dimmer | RO | The vaccum's battery level. | -| state | String | RO | The state of the vacuum. One of `cleaning`, `docked`, `paused`, `idle`, `returning`, or `error`. | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +| Channel ID | Type | R/W | Description | +|-----------------|--------|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| command | String | WO | Send a command to the vacuum. Inspect the command description for values supported by your device. Possible values are clean_spot, locate, pause, return_to_base, start, and stop. | +| fan-speed | String | R/W | Set the fan speed. Inspect the state description fro allowed values. | +| custom-command | String | WO | Send an arbitrary command to the vacuum. This may be a raw command, or JSON. | +| battery-level | Dimmer | RO | The vaccum's battery level. | +| state | String | RO | The state of the vacuum. One of `cleaning`, `docked`, `paused`, `idle`, `returning`, or `error`. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ### [Valve](https://www.home-assistant.io/integrations/valve.mqtt/) -| Channel ID | Type | R/W | Description | -|-----------------|---------------|-----|---------------------------------------------------------------------------------------------| -| valve | Switch/Dimmer | R/W | If the valve is on (open), or not. For a valve with position (a Dimmer), 100% is full open. | -| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | +| Channel ID | Type | R/W | Description | +|-----------------|---------------|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| valve | Switch/Dimmer | R/W | If the valve is on (open), or not. For a valve with position (a Dimmer), 100% is full open. | +| state | String | RO | Additional states may be supported, such as currently processing actions, or stopping the valve where it currently is. Inspect the state and command descriptions for values supported by your device. Possible values are CLOSE, OPEN, STOP for commands; open, opening, closed, closing for states. | +| json-attributes | String | RO | Additional attributes, as a serialized JSON string. | ## Supported Devices diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanel.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanel.java index 38be53cb2d..297e7db44a 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanel.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanel.java @@ -12,8 +12,9 @@ */ package org.openhab.binding.mqtt.homeassistant.internal.component; -import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -46,6 +47,14 @@ public class AlarmControlPanel extends AbstractComponent supportedFeatures = List.of(FEATURE_ARM_HOME, FEATURE_ARM_AWAY, FEATURE_ARM_NIGHT, @@ -95,37 +104,43 @@ public class AlarmControlPanel extends AbstractComponent stateEnum = new ArrayList(List.of(STATE_DISARMED, STATE_TRIGGERED, STATE_ARMING, STATE_DISARMING, - STATE_PENDING, STATE_TRIGGERED)); - List commandEnum = new ArrayList(List.of(channelConfiguration.payloadDisarm)); - if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_HOME)) { - stateEnum.add(STATE_ARMED_HOME); - commandEnum.add(channelConfiguration.payloadArmHome); - } - if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_AWAY)) { - stateEnum.add(STATE_ARMED_AWAY); - commandEnum.add(channelConfiguration.payloadArmAway); - } - if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_NIGHT)) { - stateEnum.add(STATE_ARMED_NIGHT); - commandEnum.add(channelConfiguration.payloadArmNight); - } - if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_VACATION)) { - stateEnum.add(STATE_ARMED_VACATION); - commandEnum.add(channelConfiguration.payloadArmVacation); - } - if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_CUSTOM_BYPASS)) { - stateEnum.add(STATE_ARMED_CUSTOM_BYPASS); - commandEnum.add(channelConfiguration.payloadArmCustomBypass); - } - if (channelConfiguration.supportedFeatures.contains(FEATURE_TRIGGER)) { - commandEnum.add(channelConfiguration.payloadTrigger); - } + Map stateEnum = new LinkedHashMap<>(); + stateEnum.put(STATE_DISARMED, STATE_DISARMED); + stateEnum.put(STATE_TRIGGERED, STATE_TRIGGERED); + stateEnum.put(STATE_ARMING, STATE_ARMING); + stateEnum.put(STATE_DISARMING, STATE_DISARMING); + stateEnum.put(STATE_PENDING, STATE_PENDING); String commandTopic = channelConfiguration.commandTopic; - TextValue value = (commandTopic != null) - ? new TextValue(stateEnum.toArray(new String[0]), commandEnum.toArray(new String[0])) - : new TextValue(stateEnum.toArray(new String[0])); + Map commandEnum = new LinkedHashMap<>(); + if (commandTopic != null) { + commandEnum.put(PAYLOAD_DISARM, channelConfiguration.payloadDisarm); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_HOME)) { + stateEnum.put(STATE_ARMED_HOME, STATE_ARMED_HOME); + commandEnum.put(PAYLOAD_ARM_HOME, channelConfiguration.payloadArmHome); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_AWAY)) { + stateEnum.put(STATE_ARMED_AWAY, STATE_ARMED_AWAY); + commandEnum.put(PAYLOAD_ARM_AWAY, channelConfiguration.payloadArmAway); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_NIGHT)) { + stateEnum.put(STATE_ARMED_NIGHT, STATE_ARMED_NIGHT); + commandEnum.put(PAYLOAD_ARM_NIGHT, channelConfiguration.payloadArmNight); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_VACATION)) { + stateEnum.put(STATE_ARMED_VACATION, STATE_ARMED_VACATION); + commandEnum.put(PAYLOAD_ARM_VACATION, channelConfiguration.payloadArmVacation); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_ARM_CUSTOM_BYPASS)) { + stateEnum.put(STATE_ARMED_CUSTOM_BYPASS, STATE_ARMED_CUSTOM_BYPASS); + commandEnum.put(PAYLOAD_ARM_CUSTOM_BYPASS, channelConfiguration.payloadArmCustomBypass); + } + if (channelConfiguration.supportedFeatures.contains(FEATURE_TRIGGER)) { + commandEnum.put(PAYLOAD_TRIGGER, channelConfiguration.payloadTrigger); + } + + TextValue value = new TextValue(stateEnum, commandEnum); var builder = buildChannel(STATE_CHANNEL_ID, ComponentChannelType.STRING, value, getName(), componentConfiguration.getUpdateListener()) .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate()); 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 index c17ed3d88f..4bf5cbb955 100644 --- 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 @@ -12,6 +12,8 @@ */ package org.openhab.binding.mqtt.homeassistant.internal.component; +import java.util.Map; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.values.TextValue; @@ -30,6 +32,8 @@ import com.google.gson.annotations.SerializedName; public class Button extends AbstractComponent { public static final String BUTTON_CHANNEL_ID = "button"; + public static final String PAYLOAD_PRESS = "PRESS"; + /** * Configuration class for MQTT component */ @@ -44,13 +48,13 @@ public class Button extends AbstractComponent { protected @Nullable String commandTopic; @SerializedName("payload_press") - protected String payloadPress = "PRESS"; + protected String payloadPress = PAYLOAD_PRESS; } public Button(ComponentFactory.ComponentConfiguration componentConfiguration) { super(componentConfiguration, ChannelConfiguration.class); - TextValue value = new TextValue(new String[] { channelConfiguration.payloadPress }); + TextValue value = new TextValue(Map.of(), Map.of(PAYLOAD_PRESS, channelConfiguration.payloadPress)); buildChannel(BUTTON_CHANNEL_ID, ComponentChannelType.STRING, value, getName(), componentConfiguration.getUpdateListener()) diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Cover.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Cover.java index 3742ab51ba..c7cece0309 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Cover.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Cover.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.mqtt.homeassistant.internal.component; +import java.util.LinkedHashMap; +import java.util.Map; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.values.RollershutterValue; @@ -41,6 +44,16 @@ public class Cover extends AbstractComponent { public static final String COVER_CHANNEL_ID = "cover"; public static final String STATE_CHANNEL_ID = "state"; + public static final String PAYLOAD_OPEN = "OPEN"; + public static final String PAYLOAD_CLOSE = "CLOSE"; + public static final String PAYLOAD_STOP = "STOP"; + + public static final String STATE_CLOSED = "closed"; + public static final String STATE_CLOSING = "closing"; + public static final String STATE_OPEN = "open"; + public static final String STATE_OPENING = "opening"; + public static final String STATE_STOPPED = "stopped"; + /** * Configuration class for MQTT component */ @@ -56,11 +69,11 @@ public class Cover extends AbstractComponent { @SerializedName("command_topic") protected @Nullable String commandTopic; @SerializedName("payload_open") - protected String payloadOpen = "OPEN"; + protected String payloadOpen = PAYLOAD_OPEN; @SerializedName("payload_close") - protected String payloadClose = "CLOSE"; + protected String payloadClose = PAYLOAD_CLOSE; @SerializedName("payload_stop") - protected String payloadStop = "STOP"; + protected String payloadStop = PAYLOAD_STOP; @SerializedName("position_closed") protected int positionClosed = 0; @SerializedName("position_open") @@ -74,15 +87,15 @@ public class Cover extends AbstractComponent { @SerializedName("set_position_topic") protected @Nullable String setPositionTopic; @SerializedName("state_closed") - protected String stateClosed = "closed"; + protected String stateClosed = STATE_CLOSED; @SerializedName("state_closing") - protected String stateClosing = "closing"; + protected String stateClosing = STATE_CLOSING; @SerializedName("state_open") - protected String stateOpen = "open"; + protected String stateOpen = STATE_OPEN; @SerializedName("state_opening") - protected String stateOpening = "opening"; + protected String stateOpening = STATE_OPENING; @SerializedName("state_stopped") - protected String stateStopped = "stopped"; + protected String stateStopped = STATE_STOPPED; } @Nullable @@ -102,9 +115,13 @@ public class Cover extends AbstractComponent { // State can indicate additional information than just // the current position, so expose it as a separate channel if (stateTopic != null) { - TextValue value = new TextValue(new String[] { channelConfiguration.stateClosed, - channelConfiguration.stateClosing, channelConfiguration.stateOpen, - channelConfiguration.stateOpening, channelConfiguration.stateStopped }); + Map states = new LinkedHashMap<>(); + states.put(channelConfiguration.stateClosed, STATE_CLOSED); + states.put(channelConfiguration.stateClosing, STATE_CLOSING); + states.put(channelConfiguration.stateOpen, STATE_OPEN); + states.put(channelConfiguration.stateOpening, STATE_OPENING); + states.put(channelConfiguration.stateStopped, STATE_STOPPED); + TextValue value = new TextValue(states, Map.of()); buildChannel(STATE_CHANNEL_ID, ComponentChannelType.STRING, value, "State", componentConfiguration.getUpdateListener()).stateTopic(stateTopic).isAdvanced(true).build(); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTrigger.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTrigger.java index 6c4de70323..04b6e91703 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTrigger.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTrigger.java @@ -12,8 +12,8 @@ */ package org.openhab.binding.mqtt.homeassistant.internal.component; +import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -121,7 +121,7 @@ public class DeviceTrigger extends AbstractComponent payloads = value.getStates(); + Map payloads = value.getStates(); // Append payload to allowed values String otherPayload = newTrigger.getChannelConfiguration().payload; @@ -129,7 +129,7 @@ public class DeviceTrigger extends AbstractComponent { public static final String LOCK_CHANNEL_ID = "lock"; public static final String STATE_CHANNEL_ID = "state"; + public static final String PAYLOAD_LOCK = "LOCK"; + public static final String PAYLOAD_UNLOCK = "UNLOCK"; + public static final String PAYLOAD_OPEN = "OPEN"; + + public static final String STATE_JAMMED = "JAMMED"; + public static final String STATE_LOCKED = "LOCKED"; + public static final String STATE_LOCKING = "LOCKING"; + public static final String STATE_UNLOCKED = "UNLOCKED"; + public static final String STATE_UNLOCKING = "UNLOCKING"; + /** * Configuration class for MQTT component */ @@ -52,21 +65,21 @@ public class Lock extends AbstractComponent { @SerializedName("state_topic") protected String stateTopic = ""; @SerializedName("payload_lock") - protected String payloadLock = "LOCK"; + protected String payloadLock = PAYLOAD_LOCK; @SerializedName("payload_unlock") - protected String payloadUnlock = "UNLOCK"; + protected String payloadUnlock = PAYLOAD_UNLOCK; @SerializedName("payload_open") protected @Nullable String payloadOpen; @SerializedName("state_jammed") - protected String stateJammed = "JAMMED"; + protected String stateJammed = STATE_JAMMED; @SerializedName("state_locked") - protected String stateLocked = "LOCKED"; + protected String stateLocked = STATE_LOCKED; @SerializedName("state_locking") - protected String stateLocking = "LOCKING"; + protected String stateLocking = STATE_LOCKING; @SerializedName("state_unlocked") - protected String stateUnlocked = "UNLOCKED"; + protected String stateUnlocked = STATE_UNLOCKED; @SerializedName("state_unlocking") - protected String stateUnlocking = "UNLOCKING"; + protected String stateUnlocking = STATE_UNLOCKING; } private boolean optimistic = false; @@ -95,16 +108,21 @@ public class Lock extends AbstractComponent { return true; }).build(); - String[] commands; - if (channelConfiguration.payloadOpen == null) { - commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, }; - } else { - commands = new String[] { channelConfiguration.payloadLock, channelConfiguration.payloadUnlock, - channelConfiguration.payloadOpen }; + Map commands = new LinkedHashMap<>(); + commands.put(PAYLOAD_LOCK, channelConfiguration.payloadLock); + commands.put(PAYLOAD_UNLOCK, channelConfiguration.payloadUnlock); + String payloadOpen = channelConfiguration.payloadOpen; + if (payloadOpen != null) { + commands.put(PAYLOAD_OPEN, payloadOpen); } - stateValue = new TextValue(new String[] { channelConfiguration.stateJammed, channelConfiguration.stateLocked, - channelConfiguration.stateLocking, channelConfiguration.stateUnlocked, - channelConfiguration.stateUnlocking }, commands); + Map states = new LinkedHashMap<>(); + states.put(channelConfiguration.stateLocked, STATE_LOCKED); + states.put(channelConfiguration.stateUnlocked, STATE_UNLOCKED); + states.put(channelConfiguration.stateLocking, STATE_LOCKING); + states.put(channelConfiguration.stateUnlocking, STATE_UNLOCKING); + states.put(channelConfiguration.stateJammed, STATE_JAMMED); + stateValue = new TextValue(states, commands); + buildChannel(STATE_CHANNEL_ID, ComponentChannelType.STRING, stateValue, "State", componentConfiguration.getUpdateListener()) .stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate()) @@ -112,10 +130,11 @@ public class Lock extends AbstractComponent { channelConfiguration.getQos()) .isAdvanced(true).withAutoUpdatePolicy(AutoUpdatePolicy.VETO).commandFilter(command -> { if (command instanceof StringType stringCommand) { - if (stringCommand.toString().equals(channelConfiguration.payloadLock)) { + if (stringCommand.toString().equals(PAYLOAD_LOCK)) { autoUpdate(true); - } else if (stringCommand.toString().equals(channelConfiguration.payloadUnlock) - || stringCommand.toString().equals(channelConfiguration.payloadOpen)) { + } else if (stringCommand.toString().equals(PAYLOAD_UNLOCK) + || (channelConfiguration.payloadOpen != null + && stringCommand.toString().equals(PAYLOAD_OPEN))) { autoUpdate(false); } } @@ -135,12 +154,12 @@ public class Lock extends AbstractComponent { final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener(); if (locking) { - stateValue.update(new StringType(channelConfiguration.stateLocked)); + stateValue.update(new StringType(STATE_LOCKED)); updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState()); lockValue.update(OnOffType.ON); updateListener.updateChannelState(lockChannelUID, OnOffType.ON); } else { - stateValue.update(new StringType(channelConfiguration.stateUnlocked)); + stateValue.update(new StringType(STATE_UNLOCKED)); updateListener.updateChannelState(stateChannelUID, stateValue.getChannelState()); lockValue.update(OnOffType.OFF); updateListener.updateChannelState(lockChannelUID, OnOffType.OFF); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Vacuum.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Vacuum.java index 35c20ad559..b2f0e401a6 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Vacuum.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Vacuum.java @@ -13,8 +13,9 @@ package org.openhab.binding.mqtt.homeassistant.internal.component; import java.math.BigDecimal; -import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -49,6 +50,13 @@ public class Vacuum extends AbstractComponent { public static final String FEATURE_FAN_SPEED = "fan_speed"; public static final String FEATURE_SEND_COMMAND = "send_command"; + public static final String PAYLOAD_CLEAN_SPOT = "clean_spot"; + public static final String PAYLOAD_LOCATE = "locate"; + public static final String PAYLOAD_PAUSE = "pause"; + public static final String PAYLOAD_RETURN_TO_BASE = "return_to_base"; + public static final String PAYLOAD_START = "start"; + public static final String PAYLOAD_STOP = "stop"; + public static final String STATE_CLEANING = "cleaning"; public static final String STATE_DOCKED = "docked"; public static final String STATE_PAUSED = "paused"; @@ -87,17 +95,17 @@ public class Vacuum extends AbstractComponent { protected @Nullable String fanSpeedTopic; @SerializedName("payload_clean_spot") - protected String payloadCleanSpot = "clean_spot"; + protected String payloadCleanSpot = PAYLOAD_CLEAN_SPOT; @SerializedName("payload_locate") - protected String payloadLocate = "locate"; + protected String payloadLocate = PAYLOAD_LOCATE; @SerializedName("payload_pause") - protected String payloadPause = "pause"; + protected String payloadPause = PAYLOAD_PAUSE; @SerializedName("payload_return_to_base") - protected String payloadReturnToBase = "return_to_base"; + protected String payloadReturnToBase = PAYLOAD_RETURN_TO_BASE; @SerializedName("payload_start") - protected String payloadStart = "start"; + protected String payloadStart = PAYLOAD_START; @SerializedName("payload_stop") - protected String payloadStop = "stop"; + protected String payloadStop = PAYLOAD_STOP; @SerializedName("send_command_topic") protected @Nullable String sendCommandTopic; @@ -124,15 +132,18 @@ public class Vacuum extends AbstractComponent { final var supportedFeatures = channelConfiguration.supportedFeatures; - final List commands = new ArrayList<>(); - addPayloadToList(supportedFeatures, FEATURE_CLEAN_SPOT, channelConfiguration.payloadCleanSpot, commands); - addPayloadToList(supportedFeatures, FEATURE_LOCATE, channelConfiguration.payloadLocate, commands); - addPayloadToList(supportedFeatures, FEATURE_RETURN_HOME, channelConfiguration.payloadReturnToBase, commands); - addPayloadToList(supportedFeatures, FEATURE_START, channelConfiguration.payloadStart, commands); - addPayloadToList(supportedFeatures, FEATURE_STOP, channelConfiguration.payloadStop, commands); - addPayloadToList(supportedFeatures, FEATURE_PAUSE, channelConfiguration.payloadPause, commands); + final Map commands = new LinkedHashMap<>(); + addPayloadToList(supportedFeatures, FEATURE_CLEAN_SPOT, PAYLOAD_CLEAN_SPOT, + channelConfiguration.payloadCleanSpot, commands); + addPayloadToList(supportedFeatures, FEATURE_LOCATE, PAYLOAD_LOCATE, channelConfiguration.payloadLocate, + commands); + addPayloadToList(supportedFeatures, FEATURE_RETURN_HOME, PAYLOAD_RETURN_TO_BASE, + channelConfiguration.payloadReturnToBase, commands); + addPayloadToList(supportedFeatures, FEATURE_START, PAYLOAD_START, channelConfiguration.payloadStart, commands); + addPayloadToList(supportedFeatures, FEATURE_STOP, PAYLOAD_STOP, channelConfiguration.payloadStop, commands); + addPayloadToList(supportedFeatures, FEATURE_PAUSE, PAYLOAD_PAUSE, channelConfiguration.payloadPause, commands); - buildOptionalChannel(COMMAND_CH_ID, ComponentChannelType.STRING, new TextValue(commands.toArray(new String[0])), + buildOptionalChannel(COMMAND_CH_ID, ComponentChannelType.STRING, new TextValue(Map.of(), commands), updateListener, null, channelConfiguration.commandTopic, null, null, "Command"); final var fanSpeedList = channelConfiguration.fanSpeedList; @@ -188,9 +199,10 @@ public class Vacuum extends AbstractComponent { return null; } - private void addPayloadToList(List supportedFeatures, String feature, String payload, List list) { + private void addPayloadToList(List supportedFeatures, String feature, String command, String payload, + Map commands) { if (supportedFeatures.contains(feature) && !payload.isEmpty()) { - list.add(payload); + commands.put(command, payload); } } } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Valve.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Valve.java index e312a4db7c..005d829312 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Valve.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Valve.java @@ -14,14 +14,11 @@ package org.openhab.binding.mqtt.homeassistant.internal.component; import java.io.IOException; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -61,6 +58,15 @@ public class Valve extends AbstractComponent impleme private static final String POSITION_KEY = "position"; private static final String STATE_KEY = "state"; + public static final String PAYLOAD_OPEN = "OPEN"; + public static final String PAYLOAD_CLOSE = "CLOSE"; + public static final String PAYLOAD_STOP = "STOP"; + + public static final String STATE_OPEN = "open"; + public static final String STATE_OPENING = "opening"; + public static final String STATE_CLOSED = "closed"; + public static final String STATE_CLOSING = "closing"; + private static final String FORMAT_INTEGER = "%.0f"; private final Logger logger = LoggerFactory.getLogger(Valve.class); @@ -83,9 +89,9 @@ public class Valve extends AbstractComponent impleme protected String commandTopic = ""; @SerializedName("payload_close") - protected @Nullable String payloadClose = "CLOSE"; + protected @Nullable String payloadClose = PAYLOAD_CLOSE; @SerializedName("payload_open") - protected @Nullable String payloadOpen = "OPEN"; + protected @Nullable String payloadOpen = PAYLOAD_OPEN; @SerializedName("payload_stop") protected @Nullable String payloadStop; @SerializedName("position_closed") @@ -95,13 +101,13 @@ public class Valve extends AbstractComponent impleme @SerializedName("reports_position") protected boolean reportsPosition = false; @SerializedName("state_closed") - protected @Nullable String stateClosed = "closed"; + protected @Nullable String stateClosed = STATE_CLOSED; @SerializedName("state_closing") - protected @Nullable String stateClosing = "closing"; + protected @Nullable String stateClosing = STATE_CLOSING; @SerializedName("state_open") - protected @Nullable String stateOpen = "open"; + protected @Nullable String stateOpen = STATE_OPEN; @SerializedName("state_opening") - protected @Nullable String stateOpening = "opening"; + protected @Nullable String stateOpening = STATE_OPENING; } private final OnOffValue onOffValue; @@ -137,17 +143,17 @@ public class Valve extends AbstractComponent impleme .withAutoUpdatePolicy(autoUpdatePolicy).build(); } - List commandValues = new ArrayList<>(); - addCommandValue(commandValues, channelConfiguration.payloadOpen); - addCommandValue(commandValues, channelConfiguration.payloadClose); - addCommandValue(commandValues, channelConfiguration.payloadStop); + Map commandValues = new HashMap<>(); + addCommandValue(commandValues, PAYLOAD_OPEN, channelConfiguration.payloadOpen); + addCommandValue(commandValues, PAYLOAD_CLOSE, channelConfiguration.payloadClose); + addCommandValue(commandValues, PAYLOAD_STOP, channelConfiguration.payloadStop); - List stateValues = new ArrayList<>(); - addCommandValue(stateValues, channelConfiguration.stateOpen); - addCommandValue(stateValues, channelConfiguration.stateOpening); - addCommandValue(stateValues, channelConfiguration.stateClosed); - addCommandValue(stateValues, channelConfiguration.stateClosing); - stateValue = new TextValue(stateValues.toArray(new String[0]), commandValues.toArray(new String[0])); + Map stateValues = new HashMap<>(); + addCommandValue(stateValues, channelConfiguration.stateOpen, STATE_OPEN); + addCommandValue(stateValues, channelConfiguration.stateOpening, STATE_OPENING); + addCommandValue(stateValues, channelConfiguration.stateClosed, STATE_CLOSED); + addCommandValue(stateValues, channelConfiguration.stateClosing, STATE_CLOSING); + stateValue = new TextValue(stateValues, commandValues); final var rawStateChannel = buildChannel(RAW_STATE_CHANNEL_ID, ComponentChannelType.STRING, new TextValue(), "State", this).stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate()) @@ -178,9 +184,9 @@ public class Valve extends AbstractComponent impleme finalizeChannels(); } - private void addCommandValue(List commandValues, @Nullable String command) { - if (command != null) { - commandValues.add(command); + private void addCommandValue(Map commandValues, @Nullable String key, @Nullable String value) { + if (key != null && value != null) { + commandValues.put(key, value); } } @@ -192,7 +198,7 @@ public class Valve extends AbstractComponent impleme // then the state gets inferred from the position if it's fully open or fully closed. @Override public void updateChannelState(ChannelUID channel, State state) { - Set states = stateValue.getStates(); + Map states = stateValue.getStates(); @Nullable String statePayload = state.toString(); @@ -215,9 +221,12 @@ public class Valve extends AbstractComponent impleme // We have both state and position; no need to guess anything if (channelConfiguration.reportsPosition) { if (statePayload != null) { - if (states != null && !states.contains(statePayload)) { + if (states != null && !states.containsKey(statePayload)) { logger.warn("Invalid state '{}' for {}", statePayload, getHaID().toShortTopic()); } else { + if (states != null) { + statePayload = Objects.requireNonNull(states.get(statePayload)); + } stateValue.update(new StringType(statePayload)); channelStateUpdateListener.updateChannelState(buildChannelUID(STATE_CHANNEL_ID), stateValue.getChannelState()); @@ -241,7 +250,7 @@ public class Valve extends AbstractComponent impleme } statePayload = Objects.requireNonNull(statePayload); - if (states != null && states.contains(statePayload)) { + if (states != null && states.containsKey(statePayload)) { if (channelConfiguration.reportsPosition) { if (statePayload.equals(channelConfiguration.stateClosed)) { positionValue.update(PercentType.ZERO); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanelTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanelTests.java index 19df750601..51d1d70fe1 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanelTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanelTests.java @@ -78,11 +78,11 @@ public class AlarmControlPanelTests extends AbstractComponentTests { publishMessage("zigbee2mqtt/alarm/state", "armed_away"); assertState(component, AlarmControlPanel.STATE_CHANNEL_ID, new StringType("armed_away")); - component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("DISARM_")); + component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("DISARM")); assertPublished("zigbee2mqtt/alarm/set/state", "DISARM_"); - component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("ARM_AWAY_")); + component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("ARM_AWAY")); assertPublished("zigbee2mqtt/alarm/set/state", "ARM_AWAY_"); - component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("ARM_HOME_")); + component.getChannel(AlarmControlPanel.STATE_CHANNEL_ID).getState().publishValue(new StringType("ARM_HOME")); assertPublished("zigbee2mqtt/alarm/set/state", "ARM_HOME_"); } 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 index f8c1531987..5c4876ae1f 100644 --- 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 @@ -71,6 +71,44 @@ public class ButtonTests extends AbstractComponentTests { assertPublished("esphome/single-car-gdo/button/restart/command", "PRESS"); } + @SuppressWarnings("null") + @Test + public void testButtonWithCustomPayload() { + 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", + "payload_press": "restart", + "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); + assertThat(Objects.requireNonNull(component.getChannel(Button.BUTTON_CHANNEL_ID)).getChannel() + .getAutoUpdatePolicy(), is(AutoUpdatePolicy.VETO)); + + linkAllChannels(component); + + 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", "restart"); + } + @Override protected Set getConfigTopics() { return Set.of(CONFIG_TOPIC); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTriggerTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTriggerTests.java index 8e24db09a2..001a88af1e 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTriggerTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTriggerTests.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -119,11 +120,11 @@ public class DeviceTriggerTests extends AbstractComponentTests { ComponentChannel channel = Objects.requireNonNull(component1.getChannel("turn_on")); TextValue value = (TextValue) channel.getState().getCache(); - Set payloads = value.getStates(); + Map payloads = value.getStates(); assertNotNull(payloads); assertThat(payloads.size(), is(2)); - assertThat(payloads.contains("press"), is(true)); - assertThat(payloads.contains("release"), is(true)); + assertThat(payloads.containsKey("press"), is(true)); + assertThat(payloads.containsKey("release"), is(true)); Configuration channelConfig = channel.getChannel().getConfiguration(); Object config = channelConfig.get("config"); assertNotNull(config); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/LockTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/LockTests.java index 9c9a6aae78..ecdf4af9a7 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/LockTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/LockTests.java @@ -75,10 +75,10 @@ public class LockTests extends AbstractComponentTests { linkAllChannels(component); publishMessage("zigbee2mqtt/lock/state", "LOCKED_"); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.ON); publishMessage("zigbee2mqtt/lock/state", "UNLOCKED_"); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.OFF); publishMessage("zigbee2mqtt/lock/state", "JAMMED"); assertState(component, Lock.STATE_CHANNEL_ID, new StringType("JAMMED")); @@ -88,26 +88,26 @@ public class LockTests extends AbstractComponentTests { component.getChannel(Lock.LOCK_CHANNEL_ID).getState().publishValue(OnOffType.OFF); assertPublished("zigbee2mqtt/lock/set/state", "UNLOCK_"); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.OFF); component.getChannel(Lock.LOCK_CHANNEL_ID).getState().publishValue(OnOffType.ON); assertPublished("zigbee2mqtt/lock/set/state", "LOCK_"); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.ON); - component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("UNLOCK_")); + component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("UNLOCK")); assertPublished("zigbee2mqtt/lock/set/state", "UNLOCK_", 2); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("UNLOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.OFF); - component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("LOCK_")); + component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("LOCK")); assertPublished("zigbee2mqtt/lock/set/state", "LOCK_", 2); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED_")); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.ON); assertThrows(IllegalArgumentException.class, - () -> component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("LOCK"))); + () -> component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("LOCK_"))); assertThrows(IllegalArgumentException.class, - () -> component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("OPEN"))); - assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED_")); + () -> component.getChannel(Lock.STATE_CHANNEL_ID).getState().publishValue(new StringType("OPEN_"))); + assertState(component, Lock.STATE_CHANNEL_ID, new StringType("LOCKED")); assertState(component, Lock.LOCK_CHANNEL_ID, OnOffType.ON); }