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