Skip to content

Commit

Permalink
[mqtt.homeassistant] bring AlarmControlPanel in line with current doc…
Browse files Browse the repository at this point in the history
…umentation (openhab#17607)

* [mqtt.homeassistant] bring AlarmControlPanel in line with current documentation

Signed-off-by: Cody Cutrer <[email protected]>
  • Loading branch information
ccutrer authored Oct 25, 2024
1 parent a1f448e commit df36e79
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/
package org.openhab.binding.mqtt.homeassistant.internal.component;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.TextValue;
Expand All @@ -31,10 +34,29 @@
*/
@NonNullByDefault
public class AlarmControlPanel extends AbstractComponent<AlarmControlPanel.ChannelConfiguration> {
public static final String STATE_CHANNEL_ID = "alarm"; // Randomly chosen channel "ID"
public static final String SWITCH_DISARM_CHANNEL_ID = "disarm"; // Randomly chosen channel "ID"
public static final String SWITCH_ARM_HOME_CHANNEL_ID = "armhome"; // Randomly chosen channel "ID"
public static final String SWITCH_ARM_AWAY_CHANNEL_ID = "armaway"; // Randomly chosen channel "ID"
public static final String STATE_CHANNEL_ID = "state";
public static final String STATE_CHANNEL_ID_DEPRECATED = "alarm";
public static final String SWITCH_DISARM_CHANNEL_ID = "disarm";
public static final String SWITCH_ARM_HOME_CHANNEL_ID = "armhome";
public static final String SWITCH_ARM_AWAY_CHANNEL_ID = "armaway";

public static final String FEATURE_ARM_HOME = "arm_home";
public static final String FEATURE_ARM_AWAY = "arm_away";
public static final String FEATURE_ARM_NIGHT = "arm_night";
public static final String FEATURE_ARM_VACATION = "arm_vacation";
public static final String FEATURE_ARM_CUSTOM_BYPASS = "arm_custom_bypass";
public static final String FEATURE_TRIGGER = "trigger";

public static final String STATE_ARMED_AWAY = "armed_away";
public static final String STATE_ARMED_CUSTOM_BYPASS = "armed_custom_bypass";
public static final String STATE_ARMED_HOME = "armed_home";
public static final String STATE_ARMED_NIGHT = "armed_night";
public static final String STATE_ARMED_VACATION = "armed_vacation";
public static final String STATE_ARMING = "arming";
public static final String STATE_DISARMED = "disarmed";
public static final String STATE_DISARMING = "disarming";
public static final String STATE_PENDING = "pending";
public static final String STATE_TRIGGERED = "triggered";

/**
* Configuration class for MQTT component
Expand All @@ -48,40 +70,73 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {

@SerializedName("state_topic")
protected String stateTopic = "";
@SerializedName("state_disarmed")
protected String stateDisarmed = "disarmed";
@SerializedName("state_armed_home")
protected String stateArmedHome = "armed_home";
@SerializedName("state_armed_away")
protected String stateArmedAway = "armed_away";
@SerializedName("state_pending")
protected String statePending = "pending";
@SerializedName("state_triggered")
protected String stateTriggered = "triggered";

@SerializedName("command_topic")
protected @Nullable String commandTopic;
@SerializedName("payload_disarm")
protected String payloadDisarm = "DISARM";
@SerializedName("payload_arm_home")
protected String payloadArmHome = "ARM_HOME";
@SerializedName("payload_arm_away")
protected String payloadArmAway = "ARM_AWAY";
@SerializedName("payload_arm_home")
protected String payloadArmHome = "ARM_HOME";
@SerializedName("payload_arm_night")
protected String payloadArmNight = "ARM_NIGHT";
@SerializedName("payload_arm_vacation")
protected String payloadArmVacation = "ARM_VACATION";
@SerializedName("payload_arm_custom_bypass")
protected String payloadArmCustomBypass = "ARM_CUSTOM_BYPASS";
@SerializedName("payload_disarm")
protected String payloadDisarm = "DISARM";
@SerializedName("payload_trigger")
protected String payloadTrigger = "TRIGGER";

@SerializedName("supported_features")
protected List<String> supportedFeatures = List.of(FEATURE_ARM_HOME, FEATURE_ARM_AWAY, FEATURE_ARM_NIGHT,
FEATURE_ARM_VACATION, FEATURE_ARM_CUSTOM_BYPASS, FEATURE_TRIGGER);
}

public AlarmControlPanel(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);

final String[] stateEnum = { channelConfiguration.stateDisarmed, channelConfiguration.stateArmedHome,
channelConfiguration.stateArmedAway, channelConfiguration.statePending,
channelConfiguration.stateTriggered };
buildChannel(STATE_CHANNEL_ID, ComponentChannelType.STRING, new TextValue(stateEnum), getName(),
componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.build();
List<String> stateEnum = new ArrayList(List.of(STATE_DISARMED, STATE_TRIGGERED, STATE_ARMING, STATE_DISARMING,
STATE_PENDING, STATE_TRIGGERED));
List<String> 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);
}

String commandTopic = channelConfiguration.commandTopic;
if (commandTopic != null) {
TextValue value = (newStyleChannels && commandTopic != null)
? new TextValue(stateEnum.toArray(new String[0]), commandEnum.toArray(new String[0]))
: new TextValue(stateEnum.toArray(new String[0]));
var builder = buildChannel(newStyleChannels ? STATE_CHANNEL_ID : STATE_CHANNEL_ID_DEPRECATED,
ComponentChannelType.STRING, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate());

if (newStyleChannels && commandTopic != null) {
builder.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos());
}
builder.build();

if (!newStyleChannels && commandTopic != null) {
buildChannel(SWITCH_DISARM_CHANNEL_ID, ComponentChannelType.STRING,
new TextValue(new String[] { channelConfiguration.payloadDisarm }), getName(),
componentConfiguration.getUpdateListener())
Expand All @@ -97,6 +152,7 @@ public AlarmControlPanel(ComponentFactory.ComponentConfiguration componentConfig
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
}

finalizeChannels();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* 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 AlarmControlPanel}
*
* @author Anton Kharuzhy - Initial contribution
*/
@NonNullByDefault
public class AlarmControlPanelDeprecatedTests extends AbstractComponentTests {
public static final String CONFIG_TOPIC = "alarm_control_panel/0x0000000000000000_alarm_control_panel_zigbee2mqtt";

@SuppressWarnings("null")
@Test
public void testAlarmControlPanel() {
// @formatter:off
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
"""
{ \
"availability": [ \
{ \
"topic": "zigbee2mqtt/bridge/state" \
} \
], \
"code": "12345", \
"command_topic": "zigbee2mqtt/alarm/set/state", \
"device": { \
"identifiers": [ \
"zigbee2mqtt_0x0000000000000000" \
], \
"manufacturer": "BestAlarmEver", \
"model": "Heavy duty super duper alarm", \
"name": "Alarm", \
"sw_version": "Zigbee2MQTT 1.18.2" \
}, \
"name": "alarm", \
"payload_arm_away": "ARM_AWAY_", \
"payload_arm_home": "ARM_HOME_", \
"payload_arm_night": "ARM_NIGHT_", \
"payload_arm_custom_bypass": "ARM_CUSTOM_BYPASS_", \
"payload_disarm": "DISARM_", \
"state_topic": "zigbee2mqtt/alarm/state" \
} \
""");
// @formatter:on

assertThat(component.channels.size(), is(4));
assertThat(component.getName(), is("alarm"));

assertChannel(component, AlarmControlPanel.STATE_CHANNEL_ID_DEPRECATED, "zigbee2mqtt/alarm/state", "", "alarm",
TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_DISARM_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state", "alarm",
TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_ARM_AWAY_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state",
"alarm", TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_ARM_HOME_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state",
"alarm", TextValue.class);

publishMessage("zigbee2mqtt/alarm/state", "armed_home");
assertState(component, AlarmControlPanel.STATE_CHANNEL_ID_DEPRECATED, new StringType("armed_home"));
publishMessage("zigbee2mqtt/alarm/state", "armed_away");
assertState(component, AlarmControlPanel.STATE_CHANNEL_ID_DEPRECATED, new StringType("armed_away"));

component.getChannel(AlarmControlPanel.SWITCH_DISARM_CHANNEL_ID).getState()
.publishValue(new StringType("DISARM_"));
assertPublished("zigbee2mqtt/alarm/set/state", "DISARM_");
component.getChannel(AlarmControlPanel.SWITCH_ARM_AWAY_CHANNEL_ID).getState()
.publishValue(new StringType("ARM_AWAY_"));
assertPublished("zigbee2mqtt/alarm/set/state", "ARM_AWAY_");
component.getChannel(AlarmControlPanel.SWITCH_ARM_HOME_CHANNEL_ID).getState()
.publishValue(new StringType("ARM_HOME_"));
assertPublished("zigbee2mqtt/alarm/set/state", "ARM_HOME_");
}

@Override
protected Set<String> getConfigTopics() {
return Set.of(CONFIG_TOPIC);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,36 +65,32 @@ public void testAlarmControlPanel() {
""");
// @formatter:on

assertThat(component.channels.size(), is(4));
assertThat(component.channels.size(), is(1));
assertThat(component.getName(), is("alarm"));

assertChannel(component, AlarmControlPanel.STATE_CHANNEL_ID, "zigbee2mqtt/alarm/state", "", "alarm",
TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_DISARM_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state", "alarm",
TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_ARM_AWAY_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state",
"alarm", TextValue.class);
assertChannel(component, AlarmControlPanel.SWITCH_ARM_HOME_CHANNEL_ID, "", "zigbee2mqtt/alarm/set/state",
"alarm", TextValue.class);
assertChannel(component, AlarmControlPanel.STATE_CHANNEL_ID, "zigbee2mqtt/alarm/state",
"zigbee2mqtt/alarm/set/state", "alarm", TextValue.class);

publishMessage("zigbee2mqtt/alarm/state", "armed_home");
assertState(component, AlarmControlPanel.STATE_CHANNEL_ID, new StringType("armed_home"));
publishMessage("zigbee2mqtt/alarm/state", "armed_away");
assertState(component, AlarmControlPanel.STATE_CHANNEL_ID, new StringType("armed_away"));

component.getChannel(AlarmControlPanel.SWITCH_DISARM_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.SWITCH_ARM_AWAY_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.SWITCH_ARM_HOME_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_");
}

@Override
protected Set<String> getConfigTopics() {
return Set.of(CONFIG_TOPIC);
}

@Override
protected boolean useNewStyleChannels() {
return true;
}
}

0 comments on commit df36e79

Please sign in to comment.