]> git.basschouten.com Git - openhab-addons.git/blob
e7e133bfdacde4c8a99fcd7fe7c99198309f07db
[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.NumberValue;
26 import org.openhab.binding.mqtt.generic.values.OnOffValue;
27 import org.openhab.binding.mqtt.generic.values.PercentageValue;
28 import org.openhab.core.library.types.HSBType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.PercentType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.types.UnDefType;
34
35 /**
36  * Tests for {@link Light} conforming to the template schema
37  *
38  * @author Cody Cutrer - Initial contribution
39  */
40 @NonNullByDefault
41 public class TemplateSchemaLightTests extends AbstractComponentTests {
42     public static final String CONFIG_TOPIC = "light/0x0000000000000000_light_zigbee2mqtt";
43
44     @Test
45     public void testRgb() throws InterruptedException {
46         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
47                 {
48                     "availability": [
49                     {
50                         "topic": "zigbee2mqtt/bridge/state"
51                     }
52                     ],
53                     "device": {
54                     "identifiers": [
55                         "zigbee2mqtt_0x0000000000000000"
56                     ],
57                     "manufacturer": "Lights inc",
58                     "model": "light v1",
59                     "name": "Light",
60                     "sw_version": "Zigbee2MQTT 1.18.2"
61                     },
62                     "name": "light",
63                     "schema": "template",
64                     "state_topic": "zigbee2mqtt/light/state",
65                     "command_topic": "zigbee2mqtt/light/set/state",
66                     "command_on_template": "{{state}},{{red}},{{green}},{{blue}}",
67                     "command_off_template": "off",
68                     "state_template": "{{value_json.state}}",
69                     "red_template": "{{value_json.r}}",
70                     "green_template": "{{value_json.g}}",
71                     "blue_template": "{{value_json.b}}",
72                     "brightness_template": "{{value_json.brightness}}"
73                 }
74                 """);
75
76         assertThat(component.channels.size(), is(1));
77         assertThat(component.getName(), is("light"));
78
79         assertChannel(component, Light.COLOR_CHANNEL_ID, "", "dummy", "Color", ColorValue.class);
80
81         publishMessage("zigbee2mqtt/light/state", """
82                 { "state": "on", "r": 255, "g": 255, "b": 255, "brightness": 255 }
83                 """);
84         assertState(component, Light.COLOR_CHANNEL_ID, HSBType.WHITE);
85
86         sendCommand(component, Light.COLOR_CHANNEL_ID, HSBType.BLUE);
87         assertPublished("zigbee2mqtt/light/set/state", "on,0,0,255");
88
89         // OnOff commands should route to the correct topic
90         sendCommand(component, Light.COLOR_CHANNEL_ID, OnOffType.OFF);
91         assertPublished("zigbee2mqtt/light/set/state", "off");
92     }
93
94     @Test
95     public void testBrightnessAndOnOff() throws InterruptedException {
96         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
97                 {
98                     "name": "light",
99                     "schema": "template",
100                     "state_topic": "zigbee2mqtt/light/state",
101                     "command_topic": "zigbee2mqtt/light/set/state",
102                     "command_on_template": "{{state}},{{brightness}}",
103                     "command_off_template": "off",
104                     "state_template": "{{value_json.state}}",
105                     "brightness_template": "{{value_json.brightness}}"
106                 }
107                 """);
108
109         assertThat(component.channels.size(), is(1));
110         assertThat(component.getName(), is("light"));
111
112         assertChannel(component, Light.BRIGHTNESS_CHANNEL_ID, "", "dummy", "Brightness", PercentageValue.class);
113
114         publishMessage("zigbee2mqtt/light/state", "{ \"state\": \"on\", \"brightness\": 128 }");
115         assertState(component, Light.BRIGHTNESS_CHANNEL_ID,
116                 new PercentType(new BigDecimal(128 * 100).divide(new BigDecimal(255), MathContext.DECIMAL128)));
117
118         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.HUNDRED);
119         assertPublished("zigbee2mqtt/light/set/state", "on,255");
120
121         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.OFF);
122         assertPublished("zigbee2mqtt/light/set/state", "off");
123     }
124
125     @Test
126     public void testBrightnessAndCCT() throws InterruptedException {
127         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
128                 """
129                         {
130                             "schema": "template",
131                             "name": "Bulb-white",
132                             "command_topic": "shellies/bulb/color/0/set",
133                             "state_topic": "shellies/bulb/color/0/status",
134                             "availability_topic": "shellies/bulb/online",
135                             "command_on_template": "{\\"turn\\": \\"on\\", \\"mode\\": \\"white\\"{%- if brightness is defined -%}, \\"brightness\\": {{brightness | float | multiply(0.39215686) | round(0)}}{%- endif -%}{%- if color_temp is defined -%}, \\"temp\\": {{ (1000000 / color_temp | float) | round(0) }}{%- endif -%}}",
136                             "command_off_template": "{\\"turn\\":\\"off\\", \\"mode\\": \\"white\\"}",
137                             "state_template": "{% if value_json.ison and value_json.mode == 'white' %}on{% else %}off{% endif %}",
138                             "brightness_template": "{{ value_json.brightness | float | multiply(2.55) | round(0) }}",
139                             "color_temp_template": "{{ (1000000 / value_json.temp | float) | round(0) }}",
140                             "payload_available": "true",
141                             "payload_not_available": "false",
142                             "max_mireds": 334,
143                             "min_mireds": 153,
144                             "qos": 1,
145                             "retain": false,
146                             "optimistic": false
147                         }
148                         """);
149
150         assertThat(component.channels.size(), is(2));
151         assertThat(component.getName(), is("Bulb-white"));
152
153         assertChannel(component, Light.BRIGHTNESS_CHANNEL_ID, "", "dummy", "Brightness", PercentageValue.class);
154         assertChannel(component, Light.COLOR_TEMP_CHANNEL_ID, "", "dummy", "Color Temperature", NumberValue.class);
155
156         publishMessage("shellies/bulb/color/0/status", "{ \"state\": \"on\", \"brightness\": 100 }");
157         assertState(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.HUNDRED);
158         assertState(component, Light.COLOR_TEMP_CHANNEL_ID, UnDefType.NULL);
159
160         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, PercentType.HUNDRED);
161         assertPublished("shellies/bulb/color/0/set", "{\"turn\": \"on\", \"mode\": \"white\", \"brightness\": 100}");
162
163         sendCommand(component, Light.BRIGHTNESS_CHANNEL_ID, OnOffType.OFF);
164         assertPublished("shellies/bulb/color/0/set", "{\"turn\":\"off\", \"mode\": \"white\"}");
165
166         sendCommand(component, Light.COLOR_TEMP_CHANNEL_ID, new QuantityType(200, Units.MIRED));
167         assertPublished("shellies/bulb/color/0/set", "{\"turn\": \"on\", \"mode\": \"white\", \"temp\": 5000}");
168     }
169
170     @Test
171     public void testOnOffOnly() throws InterruptedException {
172         var component = (Light) discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
173                 {
174                     "name": "light",
175                     "schema": "template",
176                     "state_topic": "zigbee2mqtt/light/state",
177                     "command_topic": "zigbee2mqtt/light/set/state",
178                     "state_template": "{{ value_json.power }}",
179                     "command_on_template": "{{state}}",
180                     "command_off_template": "off"
181                 }
182                 """);
183
184         assertThat(component.channels.size(), is(1));
185         assertThat(component.getName(), is("light"));
186
187         assertChannel(component, Light.ON_OFF_CHANNEL_ID, "", "dummy", "On/Off State", OnOffValue.class);
188
189         publishMessage("zigbee2mqtt/light/state", "{\"power\": \"on\"}");
190         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
191         publishMessage("zigbee2mqtt/light/state", "{\"power\": \"off\"}");
192         assertState(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
193
194         sendCommand(component, Light.ON_OFF_CHANNEL_ID, OnOffType.OFF);
195         assertPublished("zigbee2mqtt/light/set/state", "off");
196         sendCommand(component, Light.ON_OFF_CHANNEL_ID, OnOffType.ON);
197         assertPublished("zigbee2mqtt/light/set/state", "on");
198     }
199
200     @Override
201     protected Set<String> getConfigTopics() {
202         return Set.of(CONFIG_TOPIC);
203     }
204 }