]> git.basschouten.com Git - openhab-addons.git/blob
43455b077f4594edecbf626ba7611bf234ea3b8a
[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.math.MathContext;
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.ColorValue;
25 import org.openhab.binding.mqtt.generic.values.OnOffValue;
26 import org.openhab.binding.mqtt.generic.values.PercentageValue;
27 import org.openhab.core.library.types.HSBType;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.library.types.PercentType;
30
31 /**
32  * Tests for {@link Light} conforming to the JSON schema
33  *
34  * @author Cody Cutrer - Initial contribution
35  */
36 @NonNullByDefault
37 public class JSONSchemaLightTests extends AbstractComponentTests {
38     public static final String CONFIG_TOPIC = "light/0x0000000000000000_light_zigbee2mqtt";
39
40     @Test
41     public void testRgb() throws InterruptedException {
42         // @formatter:off
43         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
44                 """
45                 { \
46                   "availability": [ \
47                     { \
48                       "topic": "zigbee2mqtt/bridge/state" \
49                     } \
50                   ], \
51                   "device": { \
52                     "identifiers": [ \
53                       "zigbee2mqtt_0x0000000000000000" \
54                     ], \
55                     "manufacturer": "Lights inc", \
56                     "model": "light v1", \
57                     "name": "Light", \
58                     "sw_version": "Zigbee2MQTT 1.18.2" \
59                   }, \
60                   "name": "light", \
61                   "schema": "json", \
62                   "state_topic": "zigbee2mqtt/light/state", \
63                   "command_topic": "zigbee2mqtt/light/set/state", \
64                   "brightness": true, \
65                   "color_mode": true, \
66                   "supported_color_modes": ["onoff", "brightness", "rgb"]\
67                 }\
68                 """);
69         // @formatter:on
70
71         assertThat(component.channels.size(), is(1));
72         assertThat(component.getName(), is("light"));
73
74         assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
75
76         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"ON\" }");
77         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
78         publishMessage("zigbee2mqtt/light/state", "{ \"color\": {\"r\": 10, \"g\": 20, \"b\": 30 } }");
79         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.fromRGB(10, 20, 30));
80         publishMessage("zigbee2mqtt/light/state", "{ \"brightness\": 255 }");
81         assertState(component, Light.COLOR_CHANNEL_ID, new HSBType("210,67,100"));
82
83         sendCommand(component, Light.COLOR_CHANNEL_ID, HSBType.BLUE);
84         assertPublished("zigbee2mqtt/light/set/state",
85                 "{\"state\":\"ON\",\"brightness\":255,\"color\":{\"r\":0,\"g\":0,\"b\":255}}");
86
87         // OnOff commands should route to the correct topic
88         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
89         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"OFF\"}");
90
91         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.ON);
92         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\"}");
93
94         sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
95         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":127}");
96     }
97
98     @Test
99     public void testRgbNewStyle() throws InterruptedException {
100         // @formatter:off
101         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
102                 """
103                 { \
104                   "availability": [ \
105                     { \
106                       "topic": "zigbee2mqtt/bridge/state" \
107                     } \
108                   ], \
109                   "device": { \
110                     "identifiers": [ \
111                       "zigbee2mqtt_0x0000000000000000" \
112                     ], \
113                     "manufacturer": "Lights inc", \
114                     "model": "light v1", \
115                     "name": "Light", \
116                     "sw_version": "Zigbee2MQTT 1.18.2" \
117                   }, \
118                   "name": "light", \
119                   "schema": "json", \
120                   "state_topic": "zigbee2mqtt/light/state", \
121                   "command_topic": "zigbee2mqtt/light/set/state", \
122                   "brightness": true, \
123                   "supported_color_modes": ["rgb"]\
124                 }\
125                 """);
126         // @formatter:on
127
128         assertThat(component.channels.size(), is(1));
129         assertThat(component.getName(), is("light"));
130
131         assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
132
133         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"ON\" }");
134         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
135         publishMessage("zigbee2mqtt/light/state", "{ \"color\": {\"r\": 10, \"g\": 20, \"b\": 30 } }");
136         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.fromRGB(10, 20, 30));
137         publishMessage("zigbee2mqtt/light/state", "{ \"brightness\": 255 }");
138         assertState(component, Light.COLOR_CHANNEL_ID, new HSBType("210,67,100"));
139
140         sendCommand(component, Light.COLOR_CHANNEL_ID, HSBType.BLUE);
141         assertPublished("zigbee2mqtt/light/set/state",
142                 "{\"state\":\"ON\",\"brightness\":255,\"color\":{\"r\":0,\"g\":0,\"b\":255}}");
143
144         // OnOff commands should route to the correct topic
145         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
146         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"OFF\"}");
147
148         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.ON);
149         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\"}");
150
151         sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
152         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":127}");
153     }
154
155     @Test
156     public void testBrightnessAndOnOff() throws InterruptedException {
157         // @formatter:off
158         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
159                 """
160                 { \
161                   "name": "light", \
162                   "schema": "json", \
163                   "state_topic": "zigbee2mqtt/light/state", \
164                   "command_topic": "zigbee2mqtt/light/set/state", \
165                   "brightness": true\
166                 }\
167                 """);
168         // @formatter:on
169
170         assertThat(component.channels.size(), is(1));
171         assertThat(component.getName(), is("light"));
172
173         assertChannel(component, Light.BRIGHTNESS_CHANNEL_ID, "", "dummy", "Brightness", PercentageValue.class);
174
175         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"ON\", \"brightness\": 128 }");
176         assertState(component, Light.BRIGHTNESS_CHANNEL_ID,
177                 new PercentType(new BigDecimal(128 * 100).divide(new BigDecimal(255), MathContext.DECIMAL128)));
178
179         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"OFF\", \"brightness\": 128 }");
180         assertState(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.ZERO);
181
182         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.HUNDRED);
183         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":255}");
184
185         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.OFF);
186         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"OFF\"}");
187     }
188
189     @Test
190     public void testOnOffOnly() throws InterruptedException {
191         // @formatter:off
192         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
193                 """
194                 { \
195                   "name": "light", \
196                   "schema": "json", \
197                   "state_topic": "zigbee2mqtt/light/state", \
198                   "command_topic": "zigbee2mqtt/light/set/state"\
199                 }\
200                 """);
201         // @formatter:on
202
203         assertThat(component.channels.size(), is(1));
204         assertThat(component.getName(), is("light"));
205
206         assertChannel(component, Light.ON_OFF_CHANNEL_ID, "", "dummy", "On/Off State", OnOffValue.class);
207
208         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"ON\" }");
209         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
210         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"OFF\" }");
211         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
212
213         sendCommand(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
214         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"OFF\"}");
215         sendCommand(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
216         assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\"}");
217     }
218
219     @Override
220     protected Set<String> getConfigTopics() {
221         return Set.of(CONFIG_TOPIC);
222     }
223 }