]> git.basschouten.com Git - openhab-addons.git/blob
416c2cb71f841c13768ab65938a1aa212c3d64ee
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.CoreMatchers.notNullValue;
17 import static org.hamcrest.CoreMatchers.nullValue;
18 import static org.hamcrest.MatcherAssert.assertThat;
19
20 import java.math.BigDecimal;
21 import java.math.MathContext;
22 import java.util.Set;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.junit.jupiter.api.Test;
27 import org.openhab.binding.mqtt.generic.values.ColorValue;
28 import org.openhab.binding.mqtt.generic.values.OnOffValue;
29 import org.openhab.binding.mqtt.generic.values.PercentageValue;
30 import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.HSBType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.PercentType;
35
36 /**
37  * Tests for {@link Light} confirming to the default schema
38  *
39  * @author Anton Kharuzhy - Initial contribution
40  */
41 @NonNullByDefault
42 public class DefaultSchemaLightTests extends AbstractComponentTests {
43     public static final String CONFIG_TOPIC = "light/0x0000000000000000_light_zigbee2mqtt";
44
45     @Test
46     public void testRgb() throws InterruptedException {
47         // @formatter:off
48         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
49                 "{ " +
50                         "  \"availability\": [ " +
51                         "    { " +
52                         "      \"topic\": \"zigbee2mqtt/bridge/state\" " +
53                         "    } " +
54                         "  ], " +
55                         "  \"device\": { " +
56                         "    \"identifiers\": [ " +
57                         "      \"zigbee2mqtt_0x0000000000000000\" " +
58                         "    ], " +
59                         "    \"manufacturer\": \"Lights inc\", " +
60                         "    \"model\": \"light v1\", " +
61                         "    \"name\": \"Light\", " +
62                         "    \"sw_version\": \"Zigbee2MQTT 1.18.2\" " +
63                         "  }, " +
64                         "  \"name\": \"light\", " +
65                         "  \"state_topic\": \"zigbee2mqtt/light/state\", " +
66                         "  \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
67                         "  \"state_value_template\": \"{{ value_json.power }}\", " +
68                         "  \"payload_on\": \"ON_\", " +
69                         "  \"payload_off\": \"OFF_\", " +
70                         "  \"rgb_state_topic\": \"zigbee2mqtt/light/rgb\", " +
71                         "  \"rgb_command_topic\": \"zigbee2mqtt/light/set/rgb\", " +
72                         "  \"rgb_value_template\": \"{{ value_json.rgb }}\", " +
73                         "  \"brightness_state_topic\": \"zigbee2mqtt/light/brightness\", " +
74                         "  \"brightness_command_topic\": \"zigbee2mqtt/light/set/brightness\", " +
75                         "  \"brightness_value_template\": \"{{ value_json.br }}\" " +
76                         "}");
77         // @formatter:on
78
79         assertThat(component.channels.size(), is(1));
80         assertThat(component.getName(), is("light"));
81
82         assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
83
84         @Nullable
85         ComponentChannel onOffChannel = component.onOffChannel;
86         assertThat(onOffChannel, is(notNullValue()));
87         if (onOffChannel != null) {
88             assertChannel(onOffChannel, "zigbee2mqtt/light/state", "zigbee2mqtt/light/set/state", "On/Off State",
89                     OnOffValue.class);
90         }
91         @Nullable
92         ComponentChannel brightnessChannel = component.brightnessChannel;
93         assertThat(brightnessChannel, is(notNullValue()));
94         if (brightnessChannel != null) {
95             assertChannel(brightnessChannel, "zigbee2mqtt/light/brightness", "zigbee2mqtt/light/set/brightness",
96                     "Brightness", PercentageValue.class);
97         }
98
99         publishMessage("zigbee2mqtt/light/state", "{\"power\": \"ON_\"}");
100         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
101         publishMessage("zigbee2mqtt/light/rgb", "{\"rgb\": \"10,20,30\"}");
102         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.fromRGB(10, 20, 30));
103         publishMessage("zigbee2mqtt/light/rgb", "{\"rgb\": \"255,255,255\"}");
104         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
105
106         sendCommand(component, Light.COLOR_CHANNEL_ID, HSBType.BLUE);
107         assertPublished("zigbee2mqtt/light/set/rgb", "0,0,255");
108
109         // Brightness commands should route to the correct topic
110         sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
111         assertPublished("zigbee2mqtt/light/set/brightness", "128");
112
113         // OnOff commands should route to the correct topic
114         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
115         assertPublished("zigbee2mqtt/light/set/state", "OFF_");
116     }
117
118     @Test
119     public void testRgbWithoutBrightness() throws InterruptedException {
120         // @formatter:off
121         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
122                 "{ " +
123                         "  \"name\": \"light\", " +
124                         "  \"state_topic\": \"zigbee2mqtt/light/state\", " +
125                         "  \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
126                         "  \"state_value_template\": \"{{ value_json.power }}\", " +
127                         "  \"payload_on\": \"ON_\", " +
128                         "  \"payload_off\": \"OFF_\", " +
129                         "  \"rgb_state_topic\": \"zigbee2mqtt/light/rgb\", " +
130                         "  \"rgb_command_topic\": \"zigbee2mqtt/light/set/rgb\", " +
131                         "  \"rgb_value_template\": \"{{ value_json.rgb }}\"" +
132                         "}");
133         // @formatter:on
134
135         publishMessage("zigbee2mqtt/light/rgb", "{\"rgb\": \"255,255,255\"}");
136         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
137
138         // Brightness commands should route to the correct topic, converted to RGB
139         sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
140         assertPublished("zigbee2mqtt/light/set/rgb", "127,127,127");
141
142         // OnOff commands should route to the correct topic
143         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
144         assertPublished("zigbee2mqtt/light/set/state", "OFF_");
145     }
146
147     @Test
148     public void testHsb() throws InterruptedException {
149         // @formatter:off
150         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
151                 "{ " +
152                         "  \"name\": \"light\", " +
153                         "  \"state_topic\": \"zigbee2mqtt/light/state\", " +
154                         "  \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
155                         "  \"state_value_template\": \"{{ value_json.power }}\", " +
156                         "  \"payload_on\": \"ON_\", " +
157                         "  \"payload_off\": \"OFF_\", " +
158                         "  \"hs_state_topic\": \"zigbee2mqtt/light/hs\", " +
159                         "  \"hs_command_topic\": \"zigbee2mqtt/light/set/hs\", " +
160                         "  \"hs_value_template\": \"{{ value_json.hs }}\", " +
161                         "  \"brightness_state_topic\": \"zigbee2mqtt/light/brightness\", " +
162                         "  \"brightness_command_topic\": \"zigbee2mqtt/light/set/brightness\", " +
163                         "  \"brightness_value_template\": \"{{ value_json.br }}\" " +
164                         "}");
165         // @formatter:on
166
167         assertThat(component.channels.size(), is(1));
168         assertThat(component.getName(), is("light"));
169
170         assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
171
172         @Nullable
173         ComponentChannel onOffChannel = component.onOffChannel;
174         assertThat(onOffChannel, is(notNullValue()));
175         if (onOffChannel != null) {
176             assertChannel(onOffChannel, "zigbee2mqtt/light/state", "zigbee2mqtt/light/set/state", "On/Off State",
177                     OnOffValue.class);
178         }
179         @Nullable
180         ComponentChannel brightnessChannel = component.brightnessChannel;
181         assertThat(brightnessChannel, is(notNullValue()));
182         if (brightnessChannel != null) {
183             assertChannel(brightnessChannel, "zigbee2mqtt/light/brightness", "zigbee2mqtt/light/set/brightness",
184                     "Brightness", PercentageValue.class);
185         }
186
187         publishMessage("zigbee2mqtt/light/hs", "{\"hs\": \"180,50\"}");
188         publishMessage("zigbee2mqtt/light/brightness", "{\"br\": \"128\"}");
189         assertState(component, Light.COLOR_CHANNEL_ID, new HSBType(new DecimalType(180), new PercentType(50),
190                 new PercentType(new BigDecimal(128 * 100).divide(new BigDecimal(255), MathContext.DECIMAL128))));
191
192         sendCommand(component, Light.COLOR_CHANNEL_ID, HSBType.BLUE);
193         assertPublished("zigbee2mqtt/light/set/brightness", "255");
194         assertPublished("zigbee2mqtt/light/set/hs", "240,100");
195
196         // Brightness commands should route to the correct topic
197         sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
198         assertPublished("zigbee2mqtt/light/set/brightness", "128");
199
200         // OnOff commands should route to the correct topic
201         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
202         assertPublished("zigbee2mqtt/light/set/state", "OFF_");
203     }
204
205     @Test
206     public void testBrightnessAndOnOff() throws InterruptedException {
207         // @formatter:off
208         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
209                 "{ " +
210                         "  \"name\": \"light\", " +
211                         "  \"state_topic\": \"zigbee2mqtt/light/state\", " +
212                         "  \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
213                         "  \"state_value_template\": \"{{ value_json.power }}\", " +
214                         "  \"brightness_state_topic\": \"zigbee2mqtt/light/brightness\", " +
215                         "  \"brightness_command_topic\": \"zigbee2mqtt/light/set/brightness\", " +
216                         "  \"payload_on\": \"ON_\", " +
217                         "  \"payload_off\": \"OFF_\" " +
218                         "}");
219         // @formatter:on
220
221         assertThat(component.channels.size(), is(1));
222         assertThat(component.getName(), is("light"));
223
224         assertChannel(component, Light.BRIGHTNESS_CHANNEL_ID, "zigbee2mqtt/light/brightness",
225                 "zigbee2mqtt/light/set/brightness", "Brightness", PercentageValue.class);
226         @Nullable
227         ComponentChannel onOffChannel = component.onOffChannel;
228         assertThat(onOffChannel, is(notNullValue()));
229         if (onOffChannel != null) {
230             assertChannel(onOffChannel, "zigbee2mqtt/light/state", "zigbee2mqtt/light/set/state", "On/Off State",
231                     OnOffValue.class);
232         }
233
234         publishMessage("zigbee2mqtt/light/brightness", "128");
235         assertState(component, Light.BRIGHTNESS_CHANNEL_ID,
236                 new PercentType(new BigDecimal(128 * 100).divide(new BigDecimal(255), MathContext.DECIMAL128)));
237         publishMessage("zigbee2mqtt/light/brightness", "64");
238         assertState(component, Light.BRIGHTNESS_CHANNEL_ID,
239                 new PercentType(new BigDecimal(64 * 100).divide(new BigDecimal(255), MathContext.DECIMAL128)));
240
241         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.OFF);
242         assertPublished("zigbee2mqtt/light/set/state", "OFF_");
243
244         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.ON);
245         assertPublished("zigbee2mqtt/light/set/state", "ON_");
246     }
247
248     @Test
249     public void testOnOffOnly() throws InterruptedException {
250         // @formatter:off
251         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
252                 "{ " +
253                         "  \"name\": \"light\", " +
254                         "  \"state_topic\": \"zigbee2mqtt/light/state\", " +
255                         "  \"command_topic\": \"zigbee2mqtt/light/set/state\", " +
256                         "  \"state_value_template\": \"{{ value_json.power }}\", " +
257                         "  \"payload_on\": \"ON_\", " +
258                         "  \"payload_off\": \"OFF_\" " +
259                         "}");
260         // @formatter:on
261
262         assertThat(component.channels.size(), is(1));
263         assertThat(component.getName(), is("light"));
264
265         assertChannel(component, Light.ON_OFF_CHANNEL_ID, "zigbee2mqtt/light/state", "zigbee2mqtt/light/set/state",
266                 "On/Off State", OnOffValue.class);
267         assertThat(component.brightnessChannel, is(nullValue()));
268
269         publishMessage("zigbee2mqtt/light/state", "{\"power\": \"ON_\"}");
270         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
271         publishMessage("zigbee2mqtt/light/state", "{\"power\": \"OFF_\"}");
272         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
273
274         sendCommand(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
275         assertPublished("zigbee2mqtt/light/set/state", "OFF_");
276     }
277
278     @Override
279     protected Set<String> getConfigTopics() {
280         return Set.of(CONFIG_TOPIC);
281     }
282 }