2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.homeassistant.internal.component;
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.junit.jupiter.api.Test;
22 import org.openhab.binding.mqtt.generic.values.RollershutterValue;
23 import org.openhab.binding.mqtt.generic.values.TextValue;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.library.types.StopMoveType;
26 import org.openhab.core.library.types.StringType;
27 import org.openhab.core.library.types.UpDownType;
30 * Tests for {@link Cover}
32 * @author Anton Kharuzhy - Initial contribution
35 public class CoverTests extends AbstractComponentTests {
36 public static final String CONFIG_TOPIC = "cover/0x0000000000000000_cover_zigbee2mqtt";
38 @SuppressWarnings("null")
40 public void testStateOnly() throws InterruptedException {
42 var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
47 "topic": "zigbee2mqtt/bridge/state" \
52 "zigbee2mqtt_0x0000000000000000" \
54 "manufacturer": "Covers inc", \
55 "model": "cover v1", \
57 "sw_version": "Zigbee2MQTT 1.18.2" \
60 "payload_open": "OPEN_", \
61 "payload_close": "CLOSE_", \
62 "payload_stop": "STOP_", \
63 "state_topic": "zigbee2mqtt/cover/state", \
64 "command_topic": "zigbee2mqtt/cover/set/state" \
69 assertThat(component.channels.size(), is(2));
70 assertThat(component.getName(), is("cover"));
72 assertChannel(component, Cover.STATE_CHANNEL_ID, "zigbee2mqtt/cover/state", "", "State", TextValue.class);
73 assertChannel(component, Cover.COVER_CHANNEL_ID, "zigbee2mqtt/cover/state", "zigbee2mqtt/cover/set/state",
74 "Cover", RollershutterValue.class);
76 publishMessage("zigbee2mqtt/cover/state", "closed");
77 assertState(component, Cover.COVER_CHANNEL_ID, UpDownType.DOWN);
78 assertState(component, Cover.STATE_CHANNEL_ID, new StringType("closed"));
79 publishMessage("zigbee2mqtt/cover/state", "open");
80 assertState(component, Cover.STATE_CHANNEL_ID, new StringType("open"));
81 assertState(component, Cover.COVER_CHANNEL_ID, UpDownType.UP);
83 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.UP);
84 assertPublished("zigbee2mqtt/cover/set/state", "OPEN_");
85 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.DOWN);
86 assertPublished("zigbee2mqtt/cover/set/state", "CLOSE_");
87 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(StopMoveType.STOP);
88 assertPublished("zigbee2mqtt/cover/set/state", "STOP_");
91 @SuppressWarnings("null")
93 public void testPositionAndState() throws InterruptedException {
95 var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
99 "pos_t":"esphome/single-car-gdo/cover/door/position/state",
100 "set_pos_t":"esphome/single-car-gdo/cover/door/position/command",
102 "stat_t":"esphome/single-car-gdo/cover/door/state",
103 "cmd_t":"esphome/single-car-gdo/cover/door/command",
104 "avty_t":"esphome/single-car-gdo/status",
105 "uniq_id":"78e36d645710-cover-d27845ad",
107 "ids":"78e36d645710",
108 "name":"Single Car Garage Door Opener",
109 "sw":"esphome v2023.10.4 Nov 7 2023, 16:19:39",
116 assertThat(component.channels.size(), is(2));
117 assertThat(component.getName(), is("Door"));
119 assertChannel(component, Cover.STATE_CHANNEL_ID, "esphome/single-car-gdo/cover/door/state", "", "State",
121 assertChannel(component, Cover.COVER_CHANNEL_ID, "esphome/single-car-gdo/cover/door/position/state",
122 "esphome/single-car-gdo/cover/door/position/command", "Cover", RollershutterValue.class);
124 publishMessage("esphome/single-car-gdo/cover/door/state", "closed");
125 assertState(component, Cover.STATE_CHANNEL_ID, new StringType("closed"));
126 publishMessage("esphome/single-car-gdo/cover/door/state", "open");
127 assertState(component, Cover.STATE_CHANNEL_ID, new StringType("open"));
128 publishMessage("esphome/single-car-gdo/cover/door/state", "opening");
129 assertState(component, Cover.STATE_CHANNEL_ID, new StringType("opening"));
131 publishMessage("esphome/single-car-gdo/cover/door/position/state", "100");
132 assertState(component, Cover.COVER_CHANNEL_ID, PercentType.ZERO);
133 publishMessage("esphome/single-car-gdo/cover/door/position/state", "40");
134 assertState(component, Cover.COVER_CHANNEL_ID, new PercentType(60));
135 publishMessage("esphome/single-car-gdo/cover/door/position/state", "0");
136 assertState(component, Cover.COVER_CHANNEL_ID, PercentType.HUNDRED);
138 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(PercentType.ZERO);
139 assertPublished("esphome/single-car-gdo/cover/door/position/command", "100");
140 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(PercentType.HUNDRED);
141 assertPublished("esphome/single-car-gdo/cover/door/position/command", "0");
142 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(StopMoveType.STOP);
143 assertPublished("esphome/single-car-gdo/cover/door/command", "STOP");
144 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.UP);
145 assertPublished("esphome/single-car-gdo/cover/door/command", "OPEN");
146 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.DOWN);
147 assertPublished("esphome/single-car-gdo/cover/door/command", "CLOSE");
148 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(new PercentType(40));
149 assertPublished("esphome/single-car-gdo/cover/door/position/command", "60");
152 @SuppressWarnings("null")
154 public void testPositionOnly() throws InterruptedException {
156 var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
160 "pos_t":"esphome/single-car-gdo/cover/door/position/state",
161 "set_pos_t":"esphome/single-car-gdo/cover/door/position/command",
163 "avty_t":"esphome/single-car-gdo/status",
164 "uniq_id":"78e36d645710-cover-d27845ad",
166 "ids":"78e36d645710",
167 "name":"Single Car Garage Door Opener",
168 "sw":"esphome v2023.10.4 Nov 7 2023, 16:19:39",
175 assertThat(component.channels.size(), is(1));
176 assertThat(component.getName(), is("Door"));
178 assertChannel(component, Cover.COVER_CHANNEL_ID, "esphome/single-car-gdo/cover/door/position/state",
179 "esphome/single-car-gdo/cover/door/position/command", "Cover", RollershutterValue.class);
181 publishMessage("esphome/single-car-gdo/cover/door/position/state", "100");
182 assertState(component, Cover.COVER_CHANNEL_ID, PercentType.ZERO);
183 publishMessage("esphome/single-car-gdo/cover/door/position/state", "40");
184 assertState(component, Cover.COVER_CHANNEL_ID, new PercentType(60));
185 publishMessage("esphome/single-car-gdo/cover/door/position/state", "0");
186 assertState(component, Cover.COVER_CHANNEL_ID, PercentType.HUNDRED);
188 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(PercentType.ZERO);
189 assertPublished("esphome/single-car-gdo/cover/door/position/command", "100");
190 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(PercentType.HUNDRED);
191 assertPublished("esphome/single-car-gdo/cover/door/position/command", "0");
192 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.UP);
193 assertPublished("esphome/single-car-gdo/cover/door/position/command", "100", 2);
194 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(UpDownType.DOWN);
195 assertPublished("esphome/single-car-gdo/cover/door/position/command", "0", 2);
196 component.getChannel(Cover.COVER_CHANNEL_ID).getState().publishValue(new PercentType(40));
197 assertPublished("esphome/single-car-gdo/cover/door/position/command", "60");
201 protected Set<String> getConfigTopics() {
202 return Set.of(CONFIG_TOPIC);