]> git.basschouten.com Git - openhab-addons.git/blob
5d9b0a2e9ced838a446889aaef424b369c1deb22
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.homeassistant.internal.component;
14
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17
18 import java.math.BigDecimal;
19 import java.util.Objects;
20 import java.util.Set;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.junit.jupiter.api.Test;
24 import org.openhab.binding.mqtt.generic.values.OnOffValue;
25 import org.openhab.binding.mqtt.generic.values.PercentageValue;
26 import org.openhab.binding.mqtt.generic.values.TextValue;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.PercentType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.core.types.UnDefType;
31
32 /**
33  * Tests for {@link Fan}
34  *
35  * @author Anton Kharuzhy - Initial contribution
36  */
37 @NonNullByDefault
38 public class FanTests extends AbstractComponentTests {
39     public static final String CONFIG_TOPIC = "fan/0x0000000000000000_fan_zigbee2mqtt";
40
41     @SuppressWarnings("null")
42     @Test
43     public void test() throws InterruptedException {
44         // @formatter:off
45         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
46                 """
47                 { \
48                   "availability": [ \
49                     { \
50                       "topic": "zigbee2mqtt/bridge/state" \
51                     } \
52                   ], \
53                   "device": { \
54                     "identifiers": [ \
55                       "zigbee2mqtt_0x0000000000000000" \
56                     ], \
57                     "manufacturer": "Fans inc", \
58                     "model": "Fan", \
59                     "name": "FanBlower", \
60                     "sw_version": "Zigbee2MQTT 1.18.2" \
61                   }, \
62                   "name": "fan", \
63                   "payload_off": "OFF_", \
64                   "payload_on": "ON_", \
65                   "state_topic": "zigbee2mqtt/fan/state", \
66                   "command_topic": "zigbee2mqtt/fan/set/state" \
67                 }\
68                 """);
69         // @formatter:on
70
71         assertThat(component.channels.size(), is(1));
72         assertThat(component.getName(), is("fan"));
73
74         assertChannel(component, Fan.SWITCH_CHANNEL_ID, "zigbee2mqtt/fan/state", "zigbee2mqtt/fan/set/state",
75                 "On/Off State", OnOffValue.class);
76
77         publishMessage("zigbee2mqtt/fan/state", "ON_");
78         assertState(component, Fan.SWITCH_CHANNEL_ID, OnOffType.ON);
79         publishMessage("zigbee2mqtt/fan/state", "ON_");
80         assertState(component, Fan.SWITCH_CHANNEL_ID, OnOffType.ON);
81         publishMessage("zigbee2mqtt/fan/state", "OFF_");
82         assertState(component, Fan.SWITCH_CHANNEL_ID, OnOffType.OFF);
83         publishMessage("zigbee2mqtt/fan/state", "ON_");
84         assertState(component, Fan.SWITCH_CHANNEL_ID, OnOffType.ON);
85
86         component.getChannel(Fan.SWITCH_CHANNEL_ID).getState().publishValue(OnOffType.OFF);
87         assertPublished("zigbee2mqtt/fan/set/state", "OFF_");
88         component.getChannel(Fan.SWITCH_CHANNEL_ID).getState().publishValue(OnOffType.ON);
89         assertPublished("zigbee2mqtt/fan/set/state", "ON_");
90     }
91
92     @SuppressWarnings("null")
93     @Test
94     public void testCommandTemplate() throws InterruptedException {
95         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
96                         {
97                             "availability": [
98                             {
99                                 "topic": "zigbee2mqtt/bridge/state"
100                             }
101                             ],
102                             "device": {
103                             "identifiers": [
104                                 "zigbee2mqtt_0x0000000000000000"
105                             ],
106                             "manufacturer": "Fans inc",
107                             "model": "Fan",
108                             "name": "FanBlower",
109                             "sw_version": "Zigbee2MQTT 1.18.2"
110                             },
111                             "name": "fan",
112                             "payload_off": "OFF_",
113                             "payload_on": "ON_",
114                             "state_topic": "zigbee2mqtt/fan/state",
115                             "command_topic": "zigbee2mqtt/fan/set/state",
116                             "command_template": "set to {{ value }}"
117                         }
118                 """);
119
120         assertThat(component.channels.size(), is(1));
121
122         component.getChannel(Fan.SWITCH_CHANNEL_ID).getState().publishValue(OnOffType.OFF);
123         assertPublished("zigbee2mqtt/fan/set/state", "set to OFF_");
124     }
125
126     @SuppressWarnings("null")
127     @Test
128     public void testComplex() throws InterruptedException {
129         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
130                 {
131                   "availability": [
132                     {
133                       "topic": "zigbee2mqtt/bridge/state"
134                     }
135                   ],
136                   "device": {
137                     "identifiers": [
138                       "zigbee2mqtt_0x0000000000000000"
139                     ],
140                     "manufacturer": "Fans inc",
141                     "model": "Fan",
142                     "name": "FanBlower",
143                     "sw_version": "Zigbee2MQTT 1.18.2"
144                   },
145                   "name": "Bedroom Fan",
146                   "payload_off": "false",
147                   "payload_on": "true",
148                   "state_topic": "bedroom_fan/on/state",
149                   "command_topic": "bedroom_fan/on/set",
150                   "direction_state_topic": "bedroom_fan/direction/state",
151                   "direction_command_topic": "bedroom_fan/direction/set",
152                   "oscillation_state_topic": "bedroom_fan/oscillation/state",
153                   "oscillation_command_topic": "bedroom_fan/oscillation/set",
154                   "percentage_state_topic": "bedroom_fan/speed/percentage_state",
155                   "percentage_command_topic": "bedroom_fan/speed/percentage",
156                   "preset_mode_state_topic": "bedroom_fan/preset/preset_mode_state",
157                   "preset_mode_command_topic": "bedroom_fan/preset/preset_mode",
158                   "preset_modes": [
159                     "auto",
160                     "smart",
161                     "whoosh",
162                     "eco",
163                     "breeze"
164                   ],
165                   "payload_oscillation_on": "true",
166                   "payload_oscillation_off": "false",
167                   "speed_range_min": 1,
168                   "speed_range_max": 10
169                 }
170                 """);
171
172         assertThat(component.channels.size(), is(4));
173         assertThat(component.getName(), is("Bedroom Fan"));
174
175         assertChannel(component, Fan.SPEED_CHANNEL_ID, "bedroom_fan/speed/percentage_state",
176                 "bedroom_fan/speed/percentage", "Speed", PercentageValue.class);
177         var channel = Objects.requireNonNull(component.getChannel(Fan.SPEED_CHANNEL_ID));
178         assertThat(channel.getStateDescription().getStep(), is(BigDecimal.valueOf(10.0d)));
179         assertChannel(component, Fan.OSCILLATION_CHANNEL_ID, "bedroom_fan/oscillation/state",
180                 "bedroom_fan/oscillation/set", "Oscillation", OnOffValue.class);
181         assertChannel(component, Fan.DIRECTION_CHANNEL_ID, "bedroom_fan/direction/state", "bedroom_fan/direction/set",
182                 "Direction", TextValue.class);
183         assertChannel(component, Fan.PRESET_MODE_CHANNEL_ID, "bedroom_fan/preset/preset_mode_state",
184                 "bedroom_fan/preset/preset_mode", "Preset Mode", TextValue.class);
185
186         publishMessage("bedroom_fan/on/state", "true");
187         assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.HUNDRED);
188         publishMessage("bedroom_fan/on/state", "false");
189         assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
190         publishMessage("bedroom_fan/on/state", "true");
191         publishMessage("bedroom_fan/speed/percentage_state", "50");
192         assertState(component, Fan.SPEED_CHANNEL_ID, new PercentType(50));
193         publishMessage("bedroom_fan/on/state", "false");
194         // Off, even though we got an updated speed
195         assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
196         publishMessage("bedroom_fan/speed/percentage_state", "25");
197         assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
198         publishMessage("bedroom_fan/on/state", "true");
199         // Now that it's on, the channel reflects the proper speed
200         assertState(component, Fan.SPEED_CHANNEL_ID, new PercentType(25));
201
202         publishMessage("bedroom_fan/oscillation/state", "true");
203         assertState(component, Fan.OSCILLATION_CHANNEL_ID, OnOffType.ON);
204         publishMessage("bedroom_fan/oscillation/state", "false");
205         assertState(component, Fan.OSCILLATION_CHANNEL_ID, OnOffType.OFF);
206
207         publishMessage("bedroom_fan/direction/state", "forward");
208         assertState(component, Fan.DIRECTION_CHANNEL_ID, new StringType("forward"));
209         publishMessage("bedroom_fan/direction/state", "backward");
210         assertState(component, Fan.DIRECTION_CHANNEL_ID, new StringType("backward"));
211
212         publishMessage("bedroom_fan/preset/preset_mode_state", "auto");
213         assertState(component, Fan.PRESET_MODE_CHANNEL_ID, new StringType("auto"));
214         publishMessage("bedroom_fan/preset/preset_mode_state", "None");
215         assertState(component, Fan.PRESET_MODE_CHANNEL_ID, UnDefType.NULL);
216
217         component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(OnOffType.OFF);
218         assertPublished("bedroom_fan/on/set", "false");
219         component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(OnOffType.ON);
220         assertPublished("bedroom_fan/on/set", "true");
221         // Setting to a specific speed turns it on first
222         component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(PercentType.HUNDRED);
223         assertPublished("bedroom_fan/on/set", "true");
224         assertPublished("bedroom_fan/speed/percentage", "100");
225
226         component.getChannel(Fan.OSCILLATION_CHANNEL_ID).getState().publishValue(OnOffType.ON);
227         assertPublished("bedroom_fan/oscillation/set", "true");
228
229         component.getChannel(Fan.DIRECTION_CHANNEL_ID).getState().publishValue(new StringType("forward"));
230         assertPublished("bedroom_fan/direction/set", "forward");
231
232         component.getChannel(Fan.PRESET_MODE_CHANNEL_ID).getState().publishValue(new StringType("eco"));
233         assertPublished("bedroom_fan/preset/preset_mode", "eco");
234     }
235
236     @Override
237     protected Set<String> getConfigTopics() {
238         return Set.of(CONFIG_TOPIC);
239     }
240 }