]> git.basschouten.com Git - openhab-addons.git/blob
b41a5078d5ae592cf8a71e9f98e4335d458894bb
[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.util.Set;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.junit.jupiter.api.Test;
22 import org.openhab.binding.mqtt.generic.values.NumberValue;
23 import org.openhab.binding.mqtt.generic.values.OnOffValue;
24 import org.openhab.binding.mqtt.generic.values.TextValue;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.library.unit.ImperialUnits;
30 import org.openhab.core.library.unit.SIUnits;
31
32 /**
33  * Tests for {@link Climate}
34  *
35  * @author Anton Kharuzhy - Initial contribution
36  */
37 @NonNullByDefault
38 public class ClimateTests extends AbstractComponentTests {
39     public static final String CONFIG_TOPIC = "climate/0x847127fffe11dd6a_climate_zigbee2mqtt";
40
41     @SuppressWarnings("null")
42     @Test
43     public void testTS0601Climate() {
44         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
45                 """
46                         {\
47                          "action_template": "{% set values = {'idle':'off','heat':'heating','cool':'cooling','fan only':'fan'} %}{{ values[value_json.running_state] }}",\
48                          "action_topic": "zigbee2mqtt/th1", "availability": [ {\
49                          "topic": "zigbee2mqtt/bridge/state" } ],\
50                          "away_mode_command_topic": "zigbee2mqtt/th1/set/away_mode",\
51                          "away_mode_state_template": "{{ value_json.away_mode }}",\
52                          "away_mode_state_topic": "zigbee2mqtt/th1",\
53                          "current_temperature_template": "{{ value_json.local_temperature }}",\
54                          "current_temperature_topic": "zigbee2mqtt/th1", "device": {\
55                          "identifiers": [ "zigbee2mqtt_0x847127fffe11dd6a" ],  "manufacturer": "TuYa",\
56                          "model": "Radiator valve with thermostat (TS0601_thermostat)",\
57                          "name": "th1", "sw_version": "Zigbee2MQTT 1.18.2" },\
58                          "hold_command_topic": "zigbee2mqtt/th1/set/preset", "hold_modes": [\
59                          "schedule", "manual", "boost", "complex",  "comfort", "eco" ],\
60                          "hold_state_template": "{{ value_json.preset }}",\
61                          "hold_state_topic": "zigbee2mqtt/th1",\
62                          "json_attributes_topic": "zigbee2mqtt/th1", "max_temp": "35",\
63                          "min_temp": "5", "mode_command_topic": "zigbee2mqtt/th1/set/system_mode",\
64                          "mode_state_template": "{{ value_json.system_mode }}",\
65                          "mode_state_topic": "zigbee2mqtt/th1", "modes": [ "heat",\
66                          "auto", "off" ], "name": "th1", "temp_step": 0.5,\
67                          "temperature_command_topic": "zigbee2mqtt/th1/set/current_heating_setpoint",\
68                          "temperature_state_template": "{{ value_json.current_heating_setpoint }}",\
69                          "temperature_state_topic": "zigbee2mqtt/th1", "temperature_unit": "C",\
70                          "unique_id": "0x847127fffe11dd6a_climate_zigbee2mqtt"}\
71                         """);
72
73         assertThat(component.channels.size(), is(6));
74         assertThat(component.getName(), is("th1"));
75
76         assertChannel(component, Climate.ACTION_CH_ID, "zigbee2mqtt/th1", "", "th1", TextValue.class);
77         assertChannel(component, Climate.AWAY_MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/away_mode", "th1",
78                 OnOffValue.class);
79         assertChannel(component, Climate.CURRENT_TEMPERATURE_CH_ID, "zigbee2mqtt/th1", "", "th1", NumberValue.class);
80         assertChannel(component, Climate.HOLD_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/preset", "th1",
81                 TextValue.class);
82         assertChannel(component, Climate.MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/system_mode", "th1",
83                 TextValue.class);
84         assertChannel(component, Climate.TEMPERATURE_CH_ID, "zigbee2mqtt/th1",
85                 "zigbee2mqtt/th1/set/current_heating_setpoint", "th1", NumberValue.class);
86
87         publishMessage("zigbee2mqtt/th1", """
88                 {"running_state": "idle", "away_mode": "ON", \
89                 "local_temperature": "22.2", "preset": "schedule", "system_mode": "heat", \
90                 "current_heating_setpoint": "24"}\
91                 """);
92         assertState(component, Climate.ACTION_CH_ID, new StringType("off"));
93         assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.ON);
94         assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(22.2, SIUnits.CELSIUS));
95         assertState(component, Climate.HOLD_CH_ID, new StringType("schedule"));
96         assertState(component, Climate.MODE_CH_ID, new StringType("heat"));
97         assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(24, SIUnits.CELSIUS));
98
99         component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.OFF);
100         assertPublished("zigbee2mqtt/th1/set/away_mode", "OFF");
101         component.getChannel(Climate.HOLD_CH_ID).getState().publishValue(new StringType("eco"));
102         assertPublished("zigbee2mqtt/th1/set/preset", "eco");
103         component.getChannel(Climate.MODE_CH_ID).getState().publishValue(new StringType("auto"));
104         assertPublished("zigbee2mqtt/th1/set/system_mode", "auto");
105         component.getChannel(Climate.TEMPERATURE_CH_ID).getState().publishValue(new DecimalType(25));
106         assertPublished("zigbee2mqtt/th1/set/current_heating_setpoint", "25");
107     }
108
109     @SuppressWarnings("null")
110     @Test
111     public void testTS0601ClimateNotSendIfOff() {
112         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
113                 """
114                         {\
115                          "action_template": "{% set values = {'idle':'off','heat':'heating','cool':'cooling','fan only':'fan'} %}{{ values[value_json.running_state] }}",\
116                          "action_topic": "zigbee2mqtt/th1", "availability": [ {\
117                          "topic": "zigbee2mqtt/bridge/state" } ],\
118                          "away_mode_command_topic": "zigbee2mqtt/th1/set/away_mode",\
119                          "away_mode_state_template": "{{ value_json.away_mode }}",\
120                          "away_mode_state_topic": "zigbee2mqtt/th1",\
121                          "current_temperature_template": "{{ value_json.local_temperature }}",\
122                          "current_temperature_topic": "zigbee2mqtt/th1", "device": {\
123                          "identifiers": [ "zigbee2mqtt_0x847127fffe11dd6a" ],  "manufacturer": "TuYa",\
124                          "model": "Radiator valve with thermostat (TS0601_thermostat)",\
125                          "name": "th1", "sw_version": "Zigbee2MQTT 1.18.2" },\
126                          "hold_command_topic": "zigbee2mqtt/th1/set/preset", "hold_modes": [\
127                          "schedule", "manual", "boost", "complex",  "comfort", "eco" ],\
128                          "hold_state_template": "{{ value_json.preset }}",\
129                          "hold_state_topic": "zigbee2mqtt/th1",\
130                          "json_attributes_topic": "zigbee2mqtt/th1", "max_temp": "35",\
131                          "min_temp": "5", "mode_command_topic": "zigbee2mqtt/th1/set/system_mode",\
132                          "mode_state_template": "{{ value_json.system_mode }}",\
133                          "mode_state_topic": "zigbee2mqtt/th1", "modes": [ "heat",\
134                          "auto", "off" ], "name": "th1", "temp_step": 0.5,\
135                          "temperature_command_topic": "zigbee2mqtt/th1/set/current_heating_setpoint",\
136                          "temperature_state_template": "{{ value_json.current_heating_setpoint }}",\
137                          "temperature_state_topic": "zigbee2mqtt/th1", "temperature_unit": "C",\
138                          "power_command_topic": "zigbee2mqtt/th1/power",\
139                          "unique_id": "0x847127fffe11dd6a_climate_zigbee2mqtt", "send_if_off": "false"}\
140                         """);
141
142         assertThat(component.channels.size(), is(7));
143         assertThat(component.getName(), is("th1"));
144
145         assertChannel(component, Climate.ACTION_CH_ID, "zigbee2mqtt/th1", "", "th1", TextValue.class);
146         assertChannel(component, Climate.AWAY_MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/away_mode", "th1",
147                 OnOffValue.class);
148         assertChannel(component, Climate.CURRENT_TEMPERATURE_CH_ID, "zigbee2mqtt/th1", "", "th1", NumberValue.class);
149         assertChannel(component, Climate.HOLD_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/preset", "th1",
150                 TextValue.class);
151         assertChannel(component, Climate.MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/set/system_mode", "th1",
152                 TextValue.class);
153         assertChannel(component, Climate.TEMPERATURE_CH_ID, "zigbee2mqtt/th1",
154                 "zigbee2mqtt/th1/set/current_heating_setpoint", "th1", NumberValue.class);
155
156         publishMessage("zigbee2mqtt/th1", """
157                 {"running_state": "idle", "away_mode": "ON", \
158                 "local_temperature": "22.2", "preset": "schedule", "system_mode": "heat", \
159                 "current_heating_setpoint": "24"}\
160                 """);
161         assertState(component, Climate.ACTION_CH_ID, new StringType("off"));
162         assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.ON);
163         assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(22.2, SIUnits.CELSIUS));
164         assertState(component, Climate.HOLD_CH_ID, new StringType("schedule"));
165         assertState(component, Climate.MODE_CH_ID, new StringType("heat"));
166         assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(24, SIUnits.CELSIUS));
167
168         // Climate is in OFF state
169         component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.OFF);
170         assertNotPublished("zigbee2mqtt/th1/set/away_mode", "OFF");
171         component.getChannel(Climate.HOLD_CH_ID).getState().publishValue(new StringType("eco"));
172         assertNotPublished("zigbee2mqtt/th1/set/preset", "eco");
173         component.getChannel(Climate.MODE_CH_ID).getState().publishValue(new StringType("auto"));
174         assertNotPublished("zigbee2mqtt/th1/set/system_mode", "auto");
175         component.getChannel(Climate.TEMPERATURE_CH_ID).getState().publishValue(new DecimalType(25));
176         assertNotPublished("zigbee2mqtt/th1/set/current_heating_setpoint", "25");
177         component.getChannel(Climate.POWER_CH_ID).getState().publishValue(OnOffType.ON);
178         assertPublished("zigbee2mqtt/th1/power", "ON");
179
180         // Enabled
181         publishMessage("zigbee2mqtt/th1", """
182                 {"running_state": "heat", "away_mode": "ON", \
183                 "local_temperature": "22.2", "preset": "schedule", "system_mode": "heat", \
184                 "current_heating_setpoint": "24"}\
185                 """);
186
187         // Climate is in ON state
188         component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.OFF);
189         assertPublished("zigbee2mqtt/th1/set/away_mode", "OFF");
190         component.getChannel(Climate.HOLD_CH_ID).getState().publishValue(new StringType("eco"));
191         assertPublished("zigbee2mqtt/th1/set/preset", "eco");
192         component.getChannel(Climate.MODE_CH_ID).getState().publishValue(new StringType("auto"));
193         assertPublished("zigbee2mqtt/th1/set/system_mode", "auto");
194         component.getChannel(Climate.TEMPERATURE_CH_ID).getState().publishValue(new DecimalType(25));
195         assertPublished("zigbee2mqtt/th1/set/current_heating_setpoint", "25");
196     }
197
198     @SuppressWarnings("null")
199     @Test
200     public void testClimate() {
201         var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
202                 {"action_template": "{{ value_json.action }}", "action_topic": "zigbee2mqtt/th1",\
203                  "aux_command_topic": "zigbee2mqtt/th1/aux",\
204                  "aux_state_template": "{{ value_json.aux }}", "aux_state_topic": "zigbee2mqtt/th1",\
205                  "away_mode_command_topic": "zigbee2mqtt/th1/away_mode",\
206                  "away_mode_state_template": "{{ value_json.away_mode }}",\
207                  "away_mode_state_topic": "zigbee2mqtt/th1",\
208                  "current_temperature_template": "{{ value_json.current_temperature }}",\
209                  "current_temperature_topic": "zigbee2mqtt/th1",\
210                  "fan_mode_command_template": "fan_mode={{ value }}",\
211                  "fan_mode_command_topic": "zigbee2mqtt/th1/fan_mode",\
212                  "fan_mode_state_template": "{{ value_json.fan_mode }}",\
213                  "fan_mode_state_topic": "zigbee2mqtt/th1", "fan_modes": [ "p1",\
214                  "p2" ], "hold_command_template": "hold={{ value }}",\
215                  "hold_command_topic": "zigbee2mqtt/th1/hold",\
216                  "hold_state_template": "{{ value_json.hold }}",\
217                  "hold_state_topic": "zigbee2mqtt/th1", "hold_modes": [ "u1", "u2",\
218                  "u3" ], "json_attributes_template": "{{ value_json.attrs }}",\
219                  "json_attributes_topic": "zigbee2mqtt/th1",\
220                  "mode_command_template": "mode={{ value }}",\
221                  "mode_command_topic": "zigbee2mqtt/th1/mode",\
222                  "mode_state_template": "{{ value_json.mode }}",\
223                  "mode_state_topic": "zigbee2mqtt/th1", "modes": [ "B1", "B2"\
224                  ], "swing_command_template": "swing={{ value }}",\
225                  "swing_command_topic": "zigbee2mqtt/th1/swing",\
226                  "swing_state_template": "{{ value_json.swing }}",\
227                  "swing_state_topic": "zigbee2mqtt/th1", "swing_modes": [ "G1",\
228                  "G2" ], "temperature_command_template": "temperature={{ value }}",\
229                  "temperature_command_topic": "zigbee2mqtt/th1/temperature",\
230                  "temperature_state_template": "{{ value_json.temperature }}",\
231                  "temperature_state_topic": "zigbee2mqtt/th1",\
232                  "temperature_high_command_template": "temperature_high={{ value }}",\
233                  "temperature_high_command_topic": "zigbee2mqtt/th1/temperature_high",\
234                  "temperature_high_state_template": "{{ value_json.temperature_high }}",\
235                  "temperature_high_state_topic": "zigbee2mqtt/th1",\
236                  "temperature_low_command_template": "temperature_low={{ value }}",\
237                  "temperature_low_command_topic": "zigbee2mqtt/th1/temperature_low",\
238                  "temperature_low_state_template": "{{ value_json.temperature_low }}",\
239                  "temperature_low_state_topic": "zigbee2mqtt/th1",\
240                  "power_command_topic": "zigbee2mqtt/th1/power", "initial": "10",\
241                  "max_temp": "40", "min_temp": "0", "temperature_unit": "F",\
242                  "temp_step": "1", "precision": "0.5", "send_if_off": "false" }\
243                 """);
244
245         assertThat(component.channels.size(), is(12));
246         assertThat(component.getName(), is("MQTT HVAC"));
247
248         assertChannel(component, Climate.ACTION_CH_ID, "zigbee2mqtt/th1", "", "MQTT HVAC", TextValue.class);
249         assertChannel(component, Climate.AUX_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/aux", "MQTT HVAC",
250                 OnOffValue.class);
251         assertChannel(component, Climate.AWAY_MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/away_mode", "MQTT HVAC",
252                 OnOffValue.class);
253         assertChannel(component, Climate.CURRENT_TEMPERATURE_CH_ID, "zigbee2mqtt/th1", "", "MQTT HVAC",
254                 NumberValue.class);
255         assertChannel(component, Climate.FAN_MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/fan_mode", "MQTT HVAC",
256                 TextValue.class);
257         assertChannel(component, Climate.HOLD_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/hold", "MQTT HVAC",
258                 TextValue.class);
259         assertChannel(component, Climate.MODE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/mode", "MQTT HVAC",
260                 TextValue.class);
261         assertChannel(component, Climate.SWING_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/swing", "MQTT HVAC",
262                 TextValue.class);
263         assertChannel(component, Climate.TEMPERATURE_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/temperature",
264                 "MQTT HVAC", NumberValue.class);
265         assertChannel(component, Climate.TEMPERATURE_HIGH_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/temperature_high",
266                 "MQTT HVAC", NumberValue.class);
267         assertChannel(component, Climate.TEMPERATURE_LOW_CH_ID, "zigbee2mqtt/th1", "zigbee2mqtt/th1/temperature_low",
268                 "MQTT HVAC", NumberValue.class);
269         assertChannel(component, Climate.POWER_CH_ID, "", "zigbee2mqtt/th1/power", "MQTT HVAC", OnOffValue.class);
270
271         publishMessage("zigbee2mqtt/th1", """
272                 { "action": "fan",  "aux": "ON",  "away_mode": "OFF", \
273                 "current_temperature": "35.5",  "fan_mode": "p2",  "hold": "u2", \
274                 "mode": "B1",  "swing": "G1",  "temperature": "30", \
275                 "temperature_high": "37",  "temperature_low": "20" }\
276                 """);
277
278         assertState(component, Climate.ACTION_CH_ID, new StringType("fan"));
279         assertState(component, Climate.AUX_CH_ID, OnOffType.ON);
280         assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.OFF);
281         assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(35.5, ImperialUnits.FAHRENHEIT));
282         assertState(component, Climate.FAN_MODE_CH_ID, new StringType("p2"));
283         assertState(component, Climate.HOLD_CH_ID, new StringType("u2"));
284         assertState(component, Climate.MODE_CH_ID, new StringType("B1"));
285         assertState(component, Climate.SWING_CH_ID, new StringType("G1"));
286         assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(30, ImperialUnits.FAHRENHEIT));
287         assertState(component, Climate.TEMPERATURE_HIGH_CH_ID, new QuantityType<>(37, ImperialUnits.FAHRENHEIT));
288         assertState(component, Climate.TEMPERATURE_LOW_CH_ID, new QuantityType<>(20, ImperialUnits.FAHRENHEIT));
289
290         component.getChannel(Climate.AUX_CH_ID).getState().publishValue(OnOffType.OFF);
291         assertPublished("zigbee2mqtt/th1/aux", "OFF");
292         component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.ON);
293         assertPublished("zigbee2mqtt/th1/away_mode", "ON");
294         component.getChannel(Climate.FAN_MODE_CH_ID).getState().publishValue(new StringType("p1"));
295         assertPublished("zigbee2mqtt/th1/fan_mode", "fan_mode=p1");
296         component.getChannel(Climate.HOLD_CH_ID).getState().publishValue(new StringType("u3"));
297         assertPublished("zigbee2mqtt/th1/hold", "hold=u3");
298         component.getChannel(Climate.MODE_CH_ID).getState().publishValue(new StringType("B2"));
299         assertPublished("zigbee2mqtt/th1/mode", "mode=B2");
300         component.getChannel(Climate.SWING_CH_ID).getState().publishValue(new StringType("G2"));
301         assertPublished("zigbee2mqtt/th1/swing", "swing=G2");
302         component.getChannel(Climate.TEMPERATURE_CH_ID).getState().publishValue(new DecimalType(30.5));
303         assertPublished("zigbee2mqtt/th1/temperature", "temperature=30.5");
304         component.getChannel(Climate.TEMPERATURE_HIGH_CH_ID).getState().publishValue(new DecimalType(39.5));
305         assertPublished("zigbee2mqtt/th1/temperature_high", "temperature_high=39.5");
306         component.getChannel(Climate.TEMPERATURE_LOW_CH_ID).getState().publishValue(new DecimalType(19.5));
307         assertPublished("zigbee2mqtt/th1/temperature_low", "temperature_low=19.5");
308         component.getChannel(Climate.POWER_CH_ID).getState().publishValue(OnOffType.OFF);
309         assertPublished("zigbee2mqtt/th1/power", "OFF");
310     }
311
312     @Override
313     protected Set<String> getConfigTopics() {
314         return Set.of(CONFIG_TOPIC);
315     }
316 }