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