2 * Copyright (c) 2010-2024 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;
18 import java.math.BigDecimal;
19 import java.math.MathContext;
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;
32 * Tests for {@link Light} conforming to the JSON schema
34 * @author Cody Cutrer - Initial contribution
37 public class JSONSchemaLightTests extends AbstractComponentTests {
38 public static final String CONFIG_TOPIC = "light/0x0000000000000000_light_zigbee2mqtt";
41 public void testRgb() throws InterruptedException {
43 var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
48 "topic": "zigbee2mqtt/bridge/state" \
53 "zigbee2mqtt_0x0000000000000000" \
55 "manufacturer": "Lights inc", \
56 "model": "light v1", \
58 "sw_version": "Zigbee2MQTT 1.18.2" \
62 "state_topic": "zigbee2mqtt/light/state", \
63 "command_topic": "zigbee2mqtt/light/set/state", \
66 "supported_color_modes": ["onoff", "brightness", "rgb"]\
71 assertThat(component.channels.size(), is(1));
72 assertThat(component.getName(), is("light"));
74 assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
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"));
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}}");
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\"}");
91 sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.ON);
92 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\"}");
94 sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
95 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":127}");
99 public void testRgbNewStyle() throws InterruptedException {
101 var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
106 "topic": "zigbee2mqtt/bridge/state" \
111 "zigbee2mqtt_0x0000000000000000" \
113 "manufacturer": "Lights inc", \
114 "model": "light v1", \
116 "sw_version": "Zigbee2MQTT 1.18.2" \
120 "state_topic": "zigbee2mqtt/light/state", \
121 "command_topic": "zigbee2mqtt/light/set/state", \
122 "brightness": true, \
123 "supported_color_modes": ["rgb"]\
128 assertThat(component.channels.size(), is(1));
129 assertThat(component.getName(), is("light"));
131 assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
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"));
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}}");
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\"}");
148 sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.ON);
149 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\"}");
151 sendCommand(component, Light.COLOR_CHANNEL_ID, new PercentType(50));
152 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":127}");
156 public void testBrightnessAndOnOff() throws InterruptedException {
158 var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
163 "state_topic": "zigbee2mqtt/light/state", \
164 "command_topic": "zigbee2mqtt/light/set/state", \
170 assertThat(component.channels.size(), is(1));
171 assertThat(component.getName(), is("light"));
173 assertChannel(component, Light.BRIGHTNESS_CHANNEL_ID, "", "dummy", "Brightness", PercentageValue.class);
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)));
179 publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"OFF\", \"brightness\": 128 }");
180 assertState(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.ZERO);
182 sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.HUNDRED);
183 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"ON\",\"brightness\":255}");
185 sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.OFF);
186 assertPublished("zigbee2mqtt/light/set/state", "{\"state\":\"OFF\"}");
190 public void testOnOffOnly() throws InterruptedException {
192 var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
197 "state_topic": "zigbee2mqtt/light/state", \
198 "command_topic": "zigbee2mqtt/light/set/state"\
203 assertThat(component.channels.size(), is(1));
204 assertThat(component.getName(), is("light"));
206 assertChannel(component, Light.ON_OFF_CHANNEL_ID, "", "dummy", "On/Off State", OnOffValue.class);
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);
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\"}");
220 protected Set<String> getConfigTopics() {
221 return Set.of(CONFIG_TOPIC);