meross-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/cli/commands/control/execute.js +23 -0
- package/cli/commands/control/index.js +12 -0
- package/cli/commands/control/menu.js +193 -0
- package/cli/commands/control/params/generic.js +229 -0
- package/cli/commands/control/params/index.js +56 -0
- package/cli/commands/control/params/light.js +188 -0
- package/cli/commands/control/params/thermostat.js +166 -0
- package/cli/commands/control/params/timer.js +242 -0
- package/cli/commands/control/params/trigger.js +206 -0
- package/cli/commands/dump.js +35 -0
- package/cli/commands/index.js +34 -0
- package/cli/commands/info.js +221 -0
- package/cli/commands/list.js +112 -0
- package/cli/commands/mqtt.js +187 -0
- package/cli/commands/sniffer/device-sniffer.js +217 -0
- package/cli/commands/sniffer/fake-app.js +233 -0
- package/cli/commands/sniffer/index.js +7 -0
- package/cli/commands/sniffer/message-queue.js +65 -0
- package/cli/commands/sniffer/sniffer-menu.js +676 -0
- package/cli/commands/stats.js +90 -0
- package/cli/commands/status/device-status.js +1403 -0
- package/cli/commands/status/hub-status.js +72 -0
- package/cli/commands/status/index.js +50 -0
- package/cli/commands/status/subdevices/hub-smoke-detector.js +82 -0
- package/cli/commands/status/subdevices/hub-temp-hum-sensor.js +43 -0
- package/cli/commands/status/subdevices/hub-thermostat-valve.js +83 -0
- package/cli/commands/status/subdevices/hub-water-leak-sensor.js +27 -0
- package/cli/commands/status/subdevices/index.js +23 -0
- package/cli/commands/test/index.js +185 -0
- package/cli/config/users.js +108 -0
- package/cli/control-registry.js +875 -0
- package/cli/helpers/client.js +89 -0
- package/cli/helpers/meross.js +106 -0
- package/cli/menu/index.js +10 -0
- package/cli/menu/main.js +648 -0
- package/cli/menu/settings.js +789 -0
- package/cli/meross-cli.js +547 -0
- package/cli/tests/README.md +365 -0
- package/cli/tests/test-alarm.js +144 -0
- package/cli/tests/test-child-lock.js +248 -0
- package/cli/tests/test-config.js +133 -0
- package/cli/tests/test-control.js +189 -0
- package/cli/tests/test-diffuser.js +505 -0
- package/cli/tests/test-dnd.js +246 -0
- package/cli/tests/test-electricity.js +209 -0
- package/cli/tests/test-encryption.js +281 -0
- package/cli/tests/test-garage.js +259 -0
- package/cli/tests/test-helper.js +313 -0
- package/cli/tests/test-hub-mts100.js +355 -0
- package/cli/tests/test-hub-sensors.js +489 -0
- package/cli/tests/test-light.js +253 -0
- package/cli/tests/test-presence.js +497 -0
- package/cli/tests/test-registry.js +419 -0
- package/cli/tests/test-roller-shutter.js +628 -0
- package/cli/tests/test-runner.js +415 -0
- package/cli/tests/test-runtime.js +234 -0
- package/cli/tests/test-screen.js +133 -0
- package/cli/tests/test-sensor-history.js +146 -0
- package/cli/tests/test-smoke-config.js +138 -0
- package/cli/tests/test-spray.js +131 -0
- package/cli/tests/test-temp-unit.js +133 -0
- package/cli/tests/test-template.js +238 -0
- package/cli/tests/test-thermostat.js +919 -0
- package/cli/tests/test-timer.js +372 -0
- package/cli/tests/test-toggle.js +342 -0
- package/cli/tests/test-trigger.js +279 -0
- package/cli/utils/display.js +86 -0
- package/cli/utils/terminal.js +137 -0
- package/package.json +53 -0
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thermostat Device Tests
|
|
5
|
+
* Tests thermostat mode control, temperature settings, and additional features
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { findDevicesByAbility, waitForDeviceConnection, getDeviceName, OnlineStatus } = require('./test-helper');
|
|
9
|
+
const { ThermostatMode } = require('meross-iot');
|
|
10
|
+
|
|
11
|
+
const metadata = {
|
|
12
|
+
name: 'thermostat',
|
|
13
|
+
description: 'Tests thermostat mode control, temperature settings, and additional features',
|
|
14
|
+
requiredAbilities: ['Appliance.Control.Thermostat.Mode'],
|
|
15
|
+
minDevices: 1
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function runTests(context) {
|
|
19
|
+
const { manager, devices, options = {} } = context;
|
|
20
|
+
const timeout = options.timeout || 30000;
|
|
21
|
+
const results = [];
|
|
22
|
+
|
|
23
|
+
// If no devices provided, discover them
|
|
24
|
+
let testDevices = devices || [];
|
|
25
|
+
if (testDevices.length === 0) {
|
|
26
|
+
testDevices = await findDevicesByAbility(manager, 'Appliance.Control.Thermostat.Mode', OnlineStatus.ONLINE);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Wait for devices to be connected
|
|
30
|
+
for (const device of testDevices) {
|
|
31
|
+
await waitForDeviceConnection(device, timeout);
|
|
32
|
+
await device.getThermostatMode();
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (testDevices.length === 0) {
|
|
37
|
+
results.push({
|
|
38
|
+
name: 'should turn thermostat on and off',
|
|
39
|
+
passed: false,
|
|
40
|
+
skipped: true,
|
|
41
|
+
error: 'No thermostat device has been found to run this test',
|
|
42
|
+
device: null
|
|
43
|
+
});
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const testDevice = testDevices[0];
|
|
48
|
+
const deviceName = getDeviceName(testDevice);
|
|
49
|
+
|
|
50
|
+
// Test 1: Turn thermostat on and off
|
|
51
|
+
try {
|
|
52
|
+
await testDevice.getThermostatMode();
|
|
53
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
54
|
+
|
|
55
|
+
if (!state) {
|
|
56
|
+
results.push({
|
|
57
|
+
name: 'should turn thermostat on and off',
|
|
58
|
+
passed: false,
|
|
59
|
+
skipped: false,
|
|
60
|
+
error: 'Could not get cached thermostat state',
|
|
61
|
+
device: deviceName
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
const toggledState = !state.isOn;
|
|
65
|
+
|
|
66
|
+
// Set the new state
|
|
67
|
+
await testDevice.setThermostatMode({
|
|
68
|
+
channel: 0,
|
|
69
|
+
onoff: toggledState ? 1 : 0,
|
|
70
|
+
partialUpdate: true
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
74
|
+
|
|
75
|
+
// Refresh state to get updated on/off status
|
|
76
|
+
await testDevice.getThermostatMode();
|
|
77
|
+
|
|
78
|
+
// Verify the state
|
|
79
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
80
|
+
|
|
81
|
+
if (!newState || newState.isOn !== toggledState) {
|
|
82
|
+
results.push({
|
|
83
|
+
name: 'should turn thermostat on and off',
|
|
84
|
+
passed: false,
|
|
85
|
+
skipped: false,
|
|
86
|
+
error: `State mismatch. Expected ${toggledState}, got ${newState?.isOn}`,
|
|
87
|
+
device: deviceName
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
results.push({
|
|
91
|
+
name: 'should turn thermostat on and off',
|
|
92
|
+
passed: true,
|
|
93
|
+
skipped: false,
|
|
94
|
+
error: null,
|
|
95
|
+
device: deviceName
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
results.push({
|
|
101
|
+
name: 'should turn thermostat on and off',
|
|
102
|
+
passed: false,
|
|
103
|
+
skipped: false,
|
|
104
|
+
error: error.message,
|
|
105
|
+
device: deviceName
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Test 2: Read ambient temperature
|
|
110
|
+
try {
|
|
111
|
+
await testDevice.getThermostatMode();
|
|
112
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
113
|
+
|
|
114
|
+
if (!state) {
|
|
115
|
+
results.push({
|
|
116
|
+
name: 'should read ambient temperature',
|
|
117
|
+
passed: false,
|
|
118
|
+
skipped: false,
|
|
119
|
+
error: 'Could not get cached thermostat state',
|
|
120
|
+
device: deviceName
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
const temperature = state.currentTemperatureCelsius;
|
|
124
|
+
|
|
125
|
+
if (typeof temperature !== 'number') {
|
|
126
|
+
results.push({
|
|
127
|
+
name: 'should read ambient temperature',
|
|
128
|
+
passed: false,
|
|
129
|
+
skipped: false,
|
|
130
|
+
error: `Temperature is not a number: ${temperature}`,
|
|
131
|
+
device: deviceName
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
results.push({
|
|
135
|
+
name: 'should read ambient temperature',
|
|
136
|
+
passed: true,
|
|
137
|
+
skipped: false,
|
|
138
|
+
error: null,
|
|
139
|
+
device: deviceName,
|
|
140
|
+
details: { temperature: temperature }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
results.push({
|
|
146
|
+
name: 'should read ambient temperature',
|
|
147
|
+
passed: false,
|
|
148
|
+
skipped: false,
|
|
149
|
+
error: error.message,
|
|
150
|
+
device: deviceName
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Test 3: Change thermostat mode
|
|
155
|
+
try {
|
|
156
|
+
await testDevice.getThermostatMode();
|
|
157
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
158
|
+
|
|
159
|
+
if (!state || state.mode === undefined) {
|
|
160
|
+
results.push({
|
|
161
|
+
name: 'should change thermostat mode',
|
|
162
|
+
passed: false,
|
|
163
|
+
skipped: true,
|
|
164
|
+
error: 'Could not get current mode or mode is undefined',
|
|
165
|
+
device: deviceName
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
// Get available modes (using enum values)
|
|
169
|
+
const currentMode = state.mode;
|
|
170
|
+
const modes = [
|
|
171
|
+
ThermostatMode.HEAT,
|
|
172
|
+
ThermostatMode.COOL,
|
|
173
|
+
ThermostatMode.ECONOMY,
|
|
174
|
+
ThermostatMode.AUTO,
|
|
175
|
+
ThermostatMode.MANUAL
|
|
176
|
+
].filter(m => m !== currentMode);
|
|
177
|
+
|
|
178
|
+
if (modes.length === 0) {
|
|
179
|
+
results.push({
|
|
180
|
+
name: 'should change thermostat mode',
|
|
181
|
+
passed: false,
|
|
182
|
+
skipped: true,
|
|
183
|
+
error: 'No alternative mode available',
|
|
184
|
+
device: deviceName
|
|
185
|
+
});
|
|
186
|
+
} else {
|
|
187
|
+
const targetMode = modes[Math.floor(Math.random() * modes.length)];
|
|
188
|
+
|
|
189
|
+
await testDevice.setThermostatMode({
|
|
190
|
+
channel: 0,
|
|
191
|
+
mode: targetMode
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
195
|
+
|
|
196
|
+
// Refresh state to get updated mode
|
|
197
|
+
await testDevice.getThermostatMode();
|
|
198
|
+
|
|
199
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
200
|
+
|
|
201
|
+
if (!newState || newState.mode !== targetMode) {
|
|
202
|
+
results.push({
|
|
203
|
+
name: 'should change thermostat mode',
|
|
204
|
+
passed: false,
|
|
205
|
+
skipped: false,
|
|
206
|
+
error: `Mode mismatch. Expected ${targetMode}, got ${newState?.mode}`,
|
|
207
|
+
device: deviceName
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
results.push({
|
|
211
|
+
name: 'should change thermostat mode',
|
|
212
|
+
passed: true,
|
|
213
|
+
skipped: false,
|
|
214
|
+
error: null,
|
|
215
|
+
device: deviceName
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
results.push({
|
|
222
|
+
name: 'should change thermostat mode',
|
|
223
|
+
passed: false,
|
|
224
|
+
skipped: false,
|
|
225
|
+
error: error.message,
|
|
226
|
+
device: deviceName
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Test 4: Set heat temperature
|
|
231
|
+
try {
|
|
232
|
+
await testDevice.getThermostatMode();
|
|
233
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
234
|
+
|
|
235
|
+
if (!state) {
|
|
236
|
+
results.push({
|
|
237
|
+
name: 'should set heat temperature',
|
|
238
|
+
passed: false,
|
|
239
|
+
skipped: true,
|
|
240
|
+
error: 'Could not get cached thermostat state',
|
|
241
|
+
device: deviceName
|
|
242
|
+
});
|
|
243
|
+
} else if (state.minTemperatureCelsius === undefined || state.maxTemperatureCelsius === undefined) {
|
|
244
|
+
results.push({
|
|
245
|
+
name: 'should set heat temperature',
|
|
246
|
+
passed: false,
|
|
247
|
+
skipped: true,
|
|
248
|
+
error: 'Device does not support temperature range',
|
|
249
|
+
device: deviceName
|
|
250
|
+
});
|
|
251
|
+
} else {
|
|
252
|
+
const minTemp = state.minTemperatureCelsius;
|
|
253
|
+
const maxTemp = state.maxTemperatureCelsius;
|
|
254
|
+
const targetTemp = minTemp + Math.random() * (maxTemp - minTemp);
|
|
255
|
+
const alignedTemp = Math.round(targetTemp * 2) / 2; // Round to 0.5
|
|
256
|
+
|
|
257
|
+
// Set heat temperature
|
|
258
|
+
await testDevice.setThermostatMode({
|
|
259
|
+
channel: 0,
|
|
260
|
+
heatTemperature: alignedTemp,
|
|
261
|
+
partialUpdate: true
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
265
|
+
|
|
266
|
+
// Refresh state to get updated temperature
|
|
267
|
+
await testDevice.getThermostatMode();
|
|
268
|
+
|
|
269
|
+
// Verify heat temperature was set
|
|
270
|
+
const stateAfterTemp = testDevice.getCachedThermostatState(0);
|
|
271
|
+
|
|
272
|
+
if (!stateAfterTemp || stateAfterTemp.heatTemperatureCelsius !== alignedTemp) {
|
|
273
|
+
results.push({
|
|
274
|
+
name: 'should set heat temperature',
|
|
275
|
+
passed: false,
|
|
276
|
+
skipped: false,
|
|
277
|
+
error: `Temperature mismatch. Expected ${alignedTemp}, got ${stateAfterTemp?.heatTemperatureCelsius}`,
|
|
278
|
+
device: deviceName
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
// Set heat mode
|
|
282
|
+
await testDevice.setThermostatMode({
|
|
283
|
+
channel: 0,
|
|
284
|
+
mode: ThermostatMode.HEAT
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
288
|
+
|
|
289
|
+
// Refresh state to get updated mode
|
|
290
|
+
await testDevice.getThermostatMode();
|
|
291
|
+
|
|
292
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
293
|
+
|
|
294
|
+
if (!newState || newState.mode !== ThermostatMode.HEAT) {
|
|
295
|
+
results.push({
|
|
296
|
+
name: 'should set heat temperature',
|
|
297
|
+
passed: false,
|
|
298
|
+
skipped: false,
|
|
299
|
+
error: `Mode mismatch. Expected ${ThermostatMode.HEAT}, got ${newState?.mode}`,
|
|
300
|
+
device: deviceName
|
|
301
|
+
});
|
|
302
|
+
} else {
|
|
303
|
+
results.push({
|
|
304
|
+
name: 'should set heat temperature',
|
|
305
|
+
passed: true,
|
|
306
|
+
skipped: false,
|
|
307
|
+
error: null,
|
|
308
|
+
device: deviceName
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
} catch (error) {
|
|
314
|
+
results.push({
|
|
315
|
+
name: 'should set heat temperature',
|
|
316
|
+
passed: false,
|
|
317
|
+
skipped: false,
|
|
318
|
+
error: error.message,
|
|
319
|
+
device: deviceName
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Test 5: Set eco temperature
|
|
324
|
+
try {
|
|
325
|
+
await testDevice.getThermostatMode();
|
|
326
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
327
|
+
|
|
328
|
+
if (!state || state.minTemperatureCelsius === undefined || state.maxTemperatureCelsius === undefined) {
|
|
329
|
+
results.push({
|
|
330
|
+
name: 'should set eco temperature',
|
|
331
|
+
passed: false,
|
|
332
|
+
skipped: true,
|
|
333
|
+
error: 'Device does not support temperature range',
|
|
334
|
+
device: deviceName
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
const minTemp = state.minTemperatureCelsius;
|
|
338
|
+
const maxTemp = state.maxTemperatureCelsius;
|
|
339
|
+
const targetTemp = minTemp + Math.random() * (maxTemp - minTemp);
|
|
340
|
+
const alignedTemp = Math.round(targetTemp * 2) / 2;
|
|
341
|
+
|
|
342
|
+
// Set eco temperature
|
|
343
|
+
await testDevice.setThermostatMode({
|
|
344
|
+
channel: 0,
|
|
345
|
+
ecoTemperature: alignedTemp,
|
|
346
|
+
partialUpdate: true
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
350
|
+
|
|
351
|
+
await testDevice.getThermostatMode();
|
|
352
|
+
|
|
353
|
+
const stateAfterTemp = testDevice.getCachedThermostatState(0);
|
|
354
|
+
|
|
355
|
+
if (!stateAfterTemp || stateAfterTemp.ecoTemperatureCelsius !== alignedTemp) {
|
|
356
|
+
results.push({
|
|
357
|
+
name: 'should set eco temperature',
|
|
358
|
+
passed: false,
|
|
359
|
+
skipped: false,
|
|
360
|
+
error: `Temperature mismatch. Expected ${alignedTemp}, got ${stateAfterTemp?.ecoTemperatureCelsius}`,
|
|
361
|
+
device: deviceName
|
|
362
|
+
});
|
|
363
|
+
} else {
|
|
364
|
+
// Set eco mode
|
|
365
|
+
await testDevice.setThermostatMode({
|
|
366
|
+
channel: 0,
|
|
367
|
+
mode: ThermostatMode.ECONOMY
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
371
|
+
|
|
372
|
+
await testDevice.getThermostatMode();
|
|
373
|
+
|
|
374
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
375
|
+
|
|
376
|
+
if (!newState || newState.mode !== ThermostatMode.ECONOMY) {
|
|
377
|
+
results.push({
|
|
378
|
+
name: 'should set eco temperature',
|
|
379
|
+
passed: false,
|
|
380
|
+
skipped: false,
|
|
381
|
+
error: `Mode mismatch. Expected ${ThermostatMode.ECONOMY}, got ${newState?.mode}`,
|
|
382
|
+
device: deviceName
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
results.push({
|
|
386
|
+
name: 'should set eco temperature',
|
|
387
|
+
passed: true,
|
|
388
|
+
skipped: false,
|
|
389
|
+
error: null,
|
|
390
|
+
device: deviceName
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
} catch (error) {
|
|
396
|
+
results.push({
|
|
397
|
+
name: 'should set eco temperature',
|
|
398
|
+
passed: false,
|
|
399
|
+
skipped: false,
|
|
400
|
+
error: error.message,
|
|
401
|
+
device: deviceName
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Test 6: Set cool temperature
|
|
406
|
+
try {
|
|
407
|
+
await testDevice.getThermostatMode();
|
|
408
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
409
|
+
|
|
410
|
+
if (!state || state.minTemperatureCelsius === undefined || state.maxTemperatureCelsius === undefined) {
|
|
411
|
+
results.push({
|
|
412
|
+
name: 'should set cool temperature',
|
|
413
|
+
passed: false,
|
|
414
|
+
skipped: true,
|
|
415
|
+
error: 'Device does not support temperature range',
|
|
416
|
+
device: deviceName
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
const minTemp = state.minTemperatureCelsius;
|
|
420
|
+
const maxTemp = state.maxTemperatureCelsius;
|
|
421
|
+
const targetTemp = minTemp + Math.random() * (maxTemp - minTemp);
|
|
422
|
+
const alignedTemp = Math.round(targetTemp * 2) / 2;
|
|
423
|
+
|
|
424
|
+
await testDevice.setThermostatMode({
|
|
425
|
+
channel: 0,
|
|
426
|
+
coolTemperature: alignedTemp,
|
|
427
|
+
partialUpdate: true
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
431
|
+
|
|
432
|
+
await testDevice.getThermostatMode();
|
|
433
|
+
|
|
434
|
+
const stateAfterTemp = testDevice.getCachedThermostatState(0);
|
|
435
|
+
|
|
436
|
+
if (!stateAfterTemp || stateAfterTemp.coolTemperatureCelsius !== alignedTemp) {
|
|
437
|
+
results.push({
|
|
438
|
+
name: 'should set cool temperature',
|
|
439
|
+
passed: false,
|
|
440
|
+
skipped: false,
|
|
441
|
+
error: `Temperature mismatch. Expected ${alignedTemp}, got ${stateAfterTemp?.coolTemperatureCelsius}`,
|
|
442
|
+
device: deviceName
|
|
443
|
+
});
|
|
444
|
+
} else {
|
|
445
|
+
await testDevice.setThermostatMode({
|
|
446
|
+
channel: 0,
|
|
447
|
+
mode: ThermostatMode.COOL
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
451
|
+
|
|
452
|
+
await testDevice.getThermostatMode();
|
|
453
|
+
|
|
454
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
455
|
+
|
|
456
|
+
if (!newState || newState.mode !== ThermostatMode.COOL) {
|
|
457
|
+
results.push({
|
|
458
|
+
name: 'should set cool temperature',
|
|
459
|
+
passed: false,
|
|
460
|
+
skipped: false,
|
|
461
|
+
error: `Mode mismatch. Expected ${ThermostatMode.COOL}, got ${newState?.mode}`,
|
|
462
|
+
device: deviceName
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
results.push({
|
|
466
|
+
name: 'should set cool temperature',
|
|
467
|
+
passed: true,
|
|
468
|
+
skipped: false,
|
|
469
|
+
error: null,
|
|
470
|
+
device: deviceName
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
} catch (error) {
|
|
476
|
+
results.push({
|
|
477
|
+
name: 'should set cool temperature',
|
|
478
|
+
passed: false,
|
|
479
|
+
skipped: false,
|
|
480
|
+
error: error.message,
|
|
481
|
+
device: deviceName
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Test 7: Set manual temperature
|
|
486
|
+
try {
|
|
487
|
+
await testDevice.getThermostatMode();
|
|
488
|
+
const state = testDevice.getCachedThermostatState(0);
|
|
489
|
+
|
|
490
|
+
if (!state || state.minTemperatureCelsius === undefined || state.maxTemperatureCelsius === undefined) {
|
|
491
|
+
results.push({
|
|
492
|
+
name: 'should set manual temperature',
|
|
493
|
+
passed: false,
|
|
494
|
+
skipped: true,
|
|
495
|
+
error: 'Device does not support temperature range',
|
|
496
|
+
device: deviceName
|
|
497
|
+
});
|
|
498
|
+
} else {
|
|
499
|
+
const minTemp = state.minTemperatureCelsius;
|
|
500
|
+
const maxTemp = state.maxTemperatureCelsius;
|
|
501
|
+
const targetTemp = minTemp + Math.random() * (maxTemp - minTemp);
|
|
502
|
+
const alignedTemp = Math.round(targetTemp * 2) / 2;
|
|
503
|
+
|
|
504
|
+
await testDevice.setThermostatMode({
|
|
505
|
+
channel: 0,
|
|
506
|
+
manualTemperature: alignedTemp,
|
|
507
|
+
partialUpdate: true
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
511
|
+
|
|
512
|
+
await testDevice.getThermostatMode();
|
|
513
|
+
|
|
514
|
+
const stateAfterTemp = testDevice.getCachedThermostatState(0);
|
|
515
|
+
|
|
516
|
+
if (!stateAfterTemp || stateAfterTemp.manualTemperatureCelsius !== alignedTemp) {
|
|
517
|
+
results.push({
|
|
518
|
+
name: 'should set manual temperature',
|
|
519
|
+
passed: false,
|
|
520
|
+
skipped: false,
|
|
521
|
+
error: `Temperature mismatch. Expected ${alignedTemp}, got ${stateAfterTemp?.manualTemperatureCelsius}`,
|
|
522
|
+
device: deviceName
|
|
523
|
+
});
|
|
524
|
+
} else {
|
|
525
|
+
await testDevice.setThermostatMode({
|
|
526
|
+
channel: 0,
|
|
527
|
+
mode: ThermostatMode.MANUAL
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
531
|
+
|
|
532
|
+
await testDevice.getThermostatMode();
|
|
533
|
+
|
|
534
|
+
const newState = testDevice.getCachedThermostatState(0);
|
|
535
|
+
|
|
536
|
+
if (!newState || newState.mode !== ThermostatMode.MANUAL) {
|
|
537
|
+
results.push({
|
|
538
|
+
name: 'should set manual temperature',
|
|
539
|
+
passed: false,
|
|
540
|
+
skipped: false,
|
|
541
|
+
error: `Mode mismatch. Expected ${ThermostatMode.MANUAL}, got ${newState?.mode}`,
|
|
542
|
+
device: deviceName
|
|
543
|
+
});
|
|
544
|
+
} else {
|
|
545
|
+
results.push({
|
|
546
|
+
name: 'should set manual temperature',
|
|
547
|
+
passed: true,
|
|
548
|
+
skipped: false,
|
|
549
|
+
error: null,
|
|
550
|
+
device: deviceName
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} catch (error) {
|
|
556
|
+
results.push({
|
|
557
|
+
name: 'should set manual temperature',
|
|
558
|
+
passed: false,
|
|
559
|
+
skipped: false,
|
|
560
|
+
error: error.message,
|
|
561
|
+
device: deviceName
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Test 8: Turn thermostat on and off multiple times
|
|
566
|
+
try {
|
|
567
|
+
await testDevice.getThermostatMode();
|
|
568
|
+
|
|
569
|
+
// Turn off
|
|
570
|
+
await testDevice.setThermostatMode({
|
|
571
|
+
channel: 0,
|
|
572
|
+
onoff: 0,
|
|
573
|
+
partialUpdate: true
|
|
574
|
+
});
|
|
575
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
576
|
+
await testDevice.getThermostatMode();
|
|
577
|
+
|
|
578
|
+
const state1 = testDevice.getCachedThermostatState(0);
|
|
579
|
+
if (!state1 || state1.isOn !== false) {
|
|
580
|
+
results.push({
|
|
581
|
+
name: 'should turn thermostat on and off multiple times',
|
|
582
|
+
passed: false,
|
|
583
|
+
skipped: false,
|
|
584
|
+
error: 'Failed to turn off. Expected false, got ' + state1?.isOn,
|
|
585
|
+
device: deviceName
|
|
586
|
+
});
|
|
587
|
+
return results;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Turn on
|
|
591
|
+
await testDevice.setThermostatMode({
|
|
592
|
+
channel: 0,
|
|
593
|
+
onoff: 1,
|
|
594
|
+
partialUpdate: true
|
|
595
|
+
});
|
|
596
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
597
|
+
await testDevice.getThermostatMode();
|
|
598
|
+
|
|
599
|
+
const state2 = testDevice.getCachedThermostatState(0);
|
|
600
|
+
if (!state2 || state2.isOn !== true) {
|
|
601
|
+
results.push({
|
|
602
|
+
name: 'should turn thermostat on and off multiple times',
|
|
603
|
+
passed: false,
|
|
604
|
+
skipped: false,
|
|
605
|
+
error: 'Failed to turn on. Expected true, got ' + state2?.isOn,
|
|
606
|
+
device: deviceName
|
|
607
|
+
});
|
|
608
|
+
return results;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Turn off again
|
|
612
|
+
await testDevice.setThermostatMode({
|
|
613
|
+
channel: 0,
|
|
614
|
+
onoff: 0,
|
|
615
|
+
partialUpdate: true
|
|
616
|
+
});
|
|
617
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
618
|
+
await testDevice.getThermostatMode();
|
|
619
|
+
|
|
620
|
+
const state3 = testDevice.getCachedThermostatState(0);
|
|
621
|
+
if (!state3 || state3.isOn !== false) {
|
|
622
|
+
results.push({
|
|
623
|
+
name: 'should turn thermostat on and off multiple times',
|
|
624
|
+
passed: false,
|
|
625
|
+
skipped: false,
|
|
626
|
+
error: 'Failed to turn off again. Expected false, got ' + state3?.isOn,
|
|
627
|
+
device: deviceName
|
|
628
|
+
});
|
|
629
|
+
} else {
|
|
630
|
+
results.push({
|
|
631
|
+
name: 'should turn thermostat on and off multiple times',
|
|
632
|
+
passed: true,
|
|
633
|
+
skipped: false,
|
|
634
|
+
error: null,
|
|
635
|
+
device: deviceName
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
} catch (error) {
|
|
639
|
+
results.push({
|
|
640
|
+
name: 'should turn thermostat on and off multiple times',
|
|
641
|
+
passed: false,
|
|
642
|
+
skipped: false,
|
|
643
|
+
error: error.message,
|
|
644
|
+
device: deviceName
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Test 9: Get thermostat window opened status
|
|
649
|
+
try {
|
|
650
|
+
if (typeof testDevice.getThermostatWindowOpened !== 'function' ||
|
|
651
|
+
typeof testDevice.setThermostatWindowOpened !== 'function') {
|
|
652
|
+
results.push({
|
|
653
|
+
name: 'should get and set thermostat window opened status',
|
|
654
|
+
passed: false,
|
|
655
|
+
skipped: true,
|
|
656
|
+
error: 'Device does not support window opened feature',
|
|
657
|
+
device: deviceName
|
|
658
|
+
});
|
|
659
|
+
} else {
|
|
660
|
+
const windowStatus = await testDevice.getThermostatWindowOpened();
|
|
661
|
+
|
|
662
|
+
if (!windowStatus) {
|
|
663
|
+
results.push({
|
|
664
|
+
name: 'should get and set thermostat window opened status',
|
|
665
|
+
passed: false,
|
|
666
|
+
skipped: false,
|
|
667
|
+
error: 'getThermostatWindowOpened returned null or undefined',
|
|
668
|
+
device: deviceName
|
|
669
|
+
});
|
|
670
|
+
} else {
|
|
671
|
+
// Note: We don't actually toggle the window status as it's a physical state
|
|
672
|
+
// that shouldn't be changed during testing. We just verify the method works.
|
|
673
|
+
results.push({
|
|
674
|
+
name: 'should get and set thermostat window opened status',
|
|
675
|
+
passed: true,
|
|
676
|
+
skipped: false,
|
|
677
|
+
error: null,
|
|
678
|
+
device: deviceName,
|
|
679
|
+
details: { windowStatus: windowStatus }
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
} catch (error) {
|
|
684
|
+
results.push({
|
|
685
|
+
name: 'should get and set thermostat window opened status',
|
|
686
|
+
passed: false,
|
|
687
|
+
skipped: false,
|
|
688
|
+
error: error.message,
|
|
689
|
+
device: deviceName
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Test 10: Get thermostat mode B
|
|
694
|
+
try {
|
|
695
|
+
if (typeof testDevice.getThermostatModeB !== 'function') {
|
|
696
|
+
results.push({
|
|
697
|
+
name: 'should get thermostat mode B',
|
|
698
|
+
passed: false,
|
|
699
|
+
skipped: true,
|
|
700
|
+
error: 'Device does not support getThermostatModeB',
|
|
701
|
+
device: deviceName
|
|
702
|
+
});
|
|
703
|
+
} else {
|
|
704
|
+
const response = await testDevice.getThermostatModeB({ channel: 0 });
|
|
705
|
+
|
|
706
|
+
if (!response) {
|
|
707
|
+
results.push({
|
|
708
|
+
name: 'should get thermostat mode B',
|
|
709
|
+
passed: false,
|
|
710
|
+
skipped: false,
|
|
711
|
+
error: 'getThermostatModeB returned null or undefined',
|
|
712
|
+
device: deviceName
|
|
713
|
+
});
|
|
714
|
+
} else {
|
|
715
|
+
results.push({
|
|
716
|
+
name: 'should get thermostat mode B',
|
|
717
|
+
passed: true,
|
|
718
|
+
skipped: false,
|
|
719
|
+
error: null,
|
|
720
|
+
device: deviceName,
|
|
721
|
+
details: { modeB: response }
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
} catch (error) {
|
|
726
|
+
if (error.message && error.message.includes('does not support')) {
|
|
727
|
+
results.push({
|
|
728
|
+
name: 'should get thermostat mode B',
|
|
729
|
+
passed: false,
|
|
730
|
+
skipped: true,
|
|
731
|
+
error: `Device does not support ModeB: ${error.message}`,
|
|
732
|
+
device: deviceName
|
|
733
|
+
});
|
|
734
|
+
} else {
|
|
735
|
+
results.push({
|
|
736
|
+
name: 'should get thermostat mode B',
|
|
737
|
+
passed: false,
|
|
738
|
+
skipped: false,
|
|
739
|
+
error: error.message,
|
|
740
|
+
device: deviceName
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Test 11: Get thermostat schedule
|
|
746
|
+
try {
|
|
747
|
+
if (typeof testDevice.getThermostatSchedule !== 'function') {
|
|
748
|
+
results.push({
|
|
749
|
+
name: 'should get thermostat schedule',
|
|
750
|
+
passed: false,
|
|
751
|
+
skipped: true,
|
|
752
|
+
error: 'Device does not support getThermostatSchedule',
|
|
753
|
+
device: deviceName
|
|
754
|
+
});
|
|
755
|
+
} else {
|
|
756
|
+
const response = await testDevice.getThermostatSchedule({ channel: 0 });
|
|
757
|
+
|
|
758
|
+
if (!response) {
|
|
759
|
+
results.push({
|
|
760
|
+
name: 'should get thermostat schedule',
|
|
761
|
+
passed: false,
|
|
762
|
+
skipped: false,
|
|
763
|
+
error: 'getThermostatSchedule returned null or undefined',
|
|
764
|
+
device: deviceName
|
|
765
|
+
});
|
|
766
|
+
} else {
|
|
767
|
+
results.push({
|
|
768
|
+
name: 'should get thermostat schedule',
|
|
769
|
+
passed: true,
|
|
770
|
+
skipped: false,
|
|
771
|
+
error: null,
|
|
772
|
+
device: deviceName,
|
|
773
|
+
details: { schedule: response }
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
} catch (error) {
|
|
778
|
+
results.push({
|
|
779
|
+
name: 'should get thermostat schedule',
|
|
780
|
+
passed: false,
|
|
781
|
+
skipped: false,
|
|
782
|
+
error: error.message,
|
|
783
|
+
device: deviceName
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Test 12: Get thermostat hold action
|
|
788
|
+
try {
|
|
789
|
+
if (typeof testDevice.getThermostatHoldAction !== 'function') {
|
|
790
|
+
results.push({
|
|
791
|
+
name: 'should get thermostat hold action',
|
|
792
|
+
passed: false,
|
|
793
|
+
skipped: true,
|
|
794
|
+
error: 'Device does not support getThermostatHoldAction',
|
|
795
|
+
device: deviceName
|
|
796
|
+
});
|
|
797
|
+
} else {
|
|
798
|
+
const response = await testDevice.getThermostatHoldAction({ channel: 0 });
|
|
799
|
+
|
|
800
|
+
if (!response) {
|
|
801
|
+
results.push({
|
|
802
|
+
name: 'should get thermostat hold action',
|
|
803
|
+
passed: false,
|
|
804
|
+
skipped: false,
|
|
805
|
+
error: 'getThermostatHoldAction returned null or undefined',
|
|
806
|
+
device: deviceName
|
|
807
|
+
});
|
|
808
|
+
} else {
|
|
809
|
+
results.push({
|
|
810
|
+
name: 'should get thermostat hold action',
|
|
811
|
+
passed: true,
|
|
812
|
+
skipped: false,
|
|
813
|
+
error: null,
|
|
814
|
+
device: deviceName,
|
|
815
|
+
details: { holdAction: response }
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
} catch (error) {
|
|
820
|
+
results.push({
|
|
821
|
+
name: 'should get thermostat hold action',
|
|
822
|
+
passed: false,
|
|
823
|
+
skipped: false,
|
|
824
|
+
error: error.message,
|
|
825
|
+
device: deviceName
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Test 13: Get thermostat calibration
|
|
830
|
+
try {
|
|
831
|
+
if (typeof testDevice.getThermostatCalibration !== 'function') {
|
|
832
|
+
results.push({
|
|
833
|
+
name: 'should get thermostat calibration',
|
|
834
|
+
passed: false,
|
|
835
|
+
skipped: true,
|
|
836
|
+
error: 'Device does not support getThermostatCalibration',
|
|
837
|
+
device: deviceName
|
|
838
|
+
});
|
|
839
|
+
} else {
|
|
840
|
+
const response = await testDevice.getThermostatCalibration({ channel: 0 });
|
|
841
|
+
|
|
842
|
+
if (!response) {
|
|
843
|
+
results.push({
|
|
844
|
+
name: 'should get thermostat calibration',
|
|
845
|
+
passed: false,
|
|
846
|
+
skipped: false,
|
|
847
|
+
error: 'getThermostatCalibration returned null or undefined',
|
|
848
|
+
device: deviceName
|
|
849
|
+
});
|
|
850
|
+
} else {
|
|
851
|
+
results.push({
|
|
852
|
+
name: 'should get thermostat calibration',
|
|
853
|
+
passed: true,
|
|
854
|
+
skipped: false,
|
|
855
|
+
error: null,
|
|
856
|
+
device: deviceName,
|
|
857
|
+
details: { calibration: response }
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
} catch (error) {
|
|
862
|
+
results.push({
|
|
863
|
+
name: 'should get thermostat calibration',
|
|
864
|
+
passed: false,
|
|
865
|
+
skipped: false,
|
|
866
|
+
error: error.message,
|
|
867
|
+
device: deviceName
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Test 14: Get thermostat sensor
|
|
872
|
+
try {
|
|
873
|
+
if (typeof testDevice.getThermostatSensor !== 'function') {
|
|
874
|
+
results.push({
|
|
875
|
+
name: 'should get thermostat sensor',
|
|
876
|
+
passed: false,
|
|
877
|
+
skipped: true,
|
|
878
|
+
error: 'Device does not support getThermostatSensor',
|
|
879
|
+
device: deviceName
|
|
880
|
+
});
|
|
881
|
+
} else {
|
|
882
|
+
const response = await testDevice.getThermostatSensor({ channel: 0 });
|
|
883
|
+
|
|
884
|
+
if (!response) {
|
|
885
|
+
results.push({
|
|
886
|
+
name: 'should get thermostat sensor',
|
|
887
|
+
passed: false,
|
|
888
|
+
skipped: false,
|
|
889
|
+
error: 'getThermostatSensor returned null or undefined',
|
|
890
|
+
device: deviceName
|
|
891
|
+
});
|
|
892
|
+
} else {
|
|
893
|
+
results.push({
|
|
894
|
+
name: 'should get thermostat sensor',
|
|
895
|
+
passed: true,
|
|
896
|
+
skipped: false,
|
|
897
|
+
error: null,
|
|
898
|
+
device: deviceName,
|
|
899
|
+
details: { sensor: response }
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
} catch (error) {
|
|
904
|
+
results.push({
|
|
905
|
+
name: 'should get thermostat sensor',
|
|
906
|
+
passed: false,
|
|
907
|
+
skipped: false,
|
|
908
|
+
error: error.message,
|
|
909
|
+
device: deviceName
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
return results;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
module.exports = {
|
|
917
|
+
metadata,
|
|
918
|
+
runTests
|
|
919
|
+
};
|