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.
Files changed (72) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +110 -0
  4. package/cli/commands/control/execute.js +23 -0
  5. package/cli/commands/control/index.js +12 -0
  6. package/cli/commands/control/menu.js +193 -0
  7. package/cli/commands/control/params/generic.js +229 -0
  8. package/cli/commands/control/params/index.js +56 -0
  9. package/cli/commands/control/params/light.js +188 -0
  10. package/cli/commands/control/params/thermostat.js +166 -0
  11. package/cli/commands/control/params/timer.js +242 -0
  12. package/cli/commands/control/params/trigger.js +206 -0
  13. package/cli/commands/dump.js +35 -0
  14. package/cli/commands/index.js +34 -0
  15. package/cli/commands/info.js +221 -0
  16. package/cli/commands/list.js +112 -0
  17. package/cli/commands/mqtt.js +187 -0
  18. package/cli/commands/sniffer/device-sniffer.js +217 -0
  19. package/cli/commands/sniffer/fake-app.js +233 -0
  20. package/cli/commands/sniffer/index.js +7 -0
  21. package/cli/commands/sniffer/message-queue.js +65 -0
  22. package/cli/commands/sniffer/sniffer-menu.js +676 -0
  23. package/cli/commands/stats.js +90 -0
  24. package/cli/commands/status/device-status.js +1403 -0
  25. package/cli/commands/status/hub-status.js +72 -0
  26. package/cli/commands/status/index.js +50 -0
  27. package/cli/commands/status/subdevices/hub-smoke-detector.js +82 -0
  28. package/cli/commands/status/subdevices/hub-temp-hum-sensor.js +43 -0
  29. package/cli/commands/status/subdevices/hub-thermostat-valve.js +83 -0
  30. package/cli/commands/status/subdevices/hub-water-leak-sensor.js +27 -0
  31. package/cli/commands/status/subdevices/index.js +23 -0
  32. package/cli/commands/test/index.js +185 -0
  33. package/cli/config/users.js +108 -0
  34. package/cli/control-registry.js +875 -0
  35. package/cli/helpers/client.js +89 -0
  36. package/cli/helpers/meross.js +106 -0
  37. package/cli/menu/index.js +10 -0
  38. package/cli/menu/main.js +648 -0
  39. package/cli/menu/settings.js +789 -0
  40. package/cli/meross-cli.js +547 -0
  41. package/cli/tests/README.md +365 -0
  42. package/cli/tests/test-alarm.js +144 -0
  43. package/cli/tests/test-child-lock.js +248 -0
  44. package/cli/tests/test-config.js +133 -0
  45. package/cli/tests/test-control.js +189 -0
  46. package/cli/tests/test-diffuser.js +505 -0
  47. package/cli/tests/test-dnd.js +246 -0
  48. package/cli/tests/test-electricity.js +209 -0
  49. package/cli/tests/test-encryption.js +281 -0
  50. package/cli/tests/test-garage.js +259 -0
  51. package/cli/tests/test-helper.js +313 -0
  52. package/cli/tests/test-hub-mts100.js +355 -0
  53. package/cli/tests/test-hub-sensors.js +489 -0
  54. package/cli/tests/test-light.js +253 -0
  55. package/cli/tests/test-presence.js +497 -0
  56. package/cli/tests/test-registry.js +419 -0
  57. package/cli/tests/test-roller-shutter.js +628 -0
  58. package/cli/tests/test-runner.js +415 -0
  59. package/cli/tests/test-runtime.js +234 -0
  60. package/cli/tests/test-screen.js +133 -0
  61. package/cli/tests/test-sensor-history.js +146 -0
  62. package/cli/tests/test-smoke-config.js +138 -0
  63. package/cli/tests/test-spray.js +131 -0
  64. package/cli/tests/test-temp-unit.js +133 -0
  65. package/cli/tests/test-template.js +238 -0
  66. package/cli/tests/test-thermostat.js +919 -0
  67. package/cli/tests/test-timer.js +372 -0
  68. package/cli/tests/test-toggle.js +342 -0
  69. package/cli/tests/test-trigger.js +279 -0
  70. package/cli/utils/display.js +86 -0
  71. package/cli/utils/terminal.js +137 -0
  72. package/package.json +53 -0
@@ -0,0 +1,505 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Diffuser Device Tests
5
+ * Tests light RGB control, brightness, and spray mode for diffuser devices
6
+ */
7
+
8
+ const { findDevicesByAbility, waitForDeviceConnection, getDeviceName, OnlineStatus } = require('./test-helper');
9
+ const { DiffuserLightMode, DiffuserSprayMode } = require('meross-iot');
10
+
11
+ const metadata = {
12
+ name: 'diffuser',
13
+ description: 'Tests light RGB control, brightness, and spray mode for diffuser devices',
14
+ requiredAbilities: ['Appliance.Control.Diffuser.Light', 'Appliance.Control.Diffuser.Spray'],
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 lightDevices = [];
25
+ let sprayDevices = [];
26
+
27
+ if (devices && devices.length > 0) {
28
+ // Use provided devices, filter by capabilities
29
+ lightDevices = devices.filter(d =>
30
+ d._abilities && d._abilities['Appliance.Control.Diffuser.Light']
31
+ );
32
+ sprayDevices = devices.filter(d =>
33
+ d._abilities && d._abilities['Appliance.Control.Diffuser.Spray']
34
+ );
35
+ } else {
36
+ // Find diffuser light devices
37
+ lightDevices = await findDevicesByAbility(manager, 'Appliance.Control.Diffuser.Light', OnlineStatus.ONLINE);
38
+
39
+ // Find diffuser spray devices
40
+ sprayDevices = await findDevicesByAbility(manager, 'Appliance.Control.Diffuser.Spray', OnlineStatus.ONLINE);
41
+ }
42
+
43
+ // Wait for devices to be connected
44
+ for (const device of lightDevices) {
45
+ await waitForDeviceConnection(device, timeout);
46
+ await device.getDiffuserLightState();
47
+ await new Promise(resolve => setTimeout(resolve, 1000));
48
+ }
49
+
50
+ for (const device of sprayDevices) {
51
+ await waitForDeviceConnection(device, timeout);
52
+ await device.getDiffuserSprayState();
53
+ await new Promise(resolve => setTimeout(resolve, 1000));
54
+ }
55
+
56
+ // Test 1: Set RGB color on diffuser light
57
+ if (lightDevices.length < 1) {
58
+ results.push({
59
+ name: 'should set RGB color on diffuser light',
60
+ passed: false,
61
+ skipped: true,
62
+ error: 'Could not find any DiffuserLight device within the given set of devices',
63
+ device: null
64
+ });
65
+ } else {
66
+ const light = lightDevices[0];
67
+ const deviceName = getDeviceName(light);
68
+
69
+ try {
70
+ await light.getDiffuserLightState();
71
+
72
+ // Set mode to FIXED_RGB
73
+ await light.setDiffuserLight({
74
+ channel: 0,
75
+ mode: DiffuserLightMode.FIXED_RGB
76
+ });
77
+
78
+ // Set a random color
79
+ const r = Math.floor(Math.random() * 256);
80
+ const g = Math.floor(Math.random() * 256);
81
+ const b = Math.floor(Math.random() * 256);
82
+ await light.setDiffuserLight({
83
+ channel: 0,
84
+ mode: DiffuserLightMode.FIXED_RGB,
85
+ rgb: (r << 16) | (g << 8) | b,
86
+ onoff: 1
87
+ });
88
+
89
+ await new Promise(resolve => setTimeout(resolve, 1000));
90
+
91
+ // Check the color property returns the set color
92
+ const color = light.getDiffuserLightRgbColor(0);
93
+
94
+ if (!color || !Array.isArray(color) || color.length !== 3) {
95
+ results.push({
96
+ name: 'should set RGB color on diffuser light',
97
+ passed: false,
98
+ skipped: false,
99
+ error: `Invalid color returned: ${JSON.stringify(color)}`,
100
+ device: deviceName
101
+ });
102
+ } else if (color[0] !== r || color[1] !== g || color[2] !== b) {
103
+ results.push({
104
+ name: 'should set RGB color on diffuser light',
105
+ passed: false,
106
+ skipped: false,
107
+ error: `Color mismatch. Expected [${r}, ${g}, ${b}], got [${color[0]}, ${color[1]}, ${color[2]}]`,
108
+ device: deviceName
109
+ });
110
+ } else {
111
+ results.push({
112
+ name: 'should set RGB color on diffuser light',
113
+ passed: true,
114
+ skipped: false,
115
+ error: null,
116
+ device: deviceName,
117
+ details: { rgb: [r, g, b] }
118
+ });
119
+ }
120
+ } catch (error) {
121
+ results.push({
122
+ name: 'should set RGB color on diffuser light',
123
+ passed: false,
124
+ skipped: false,
125
+ error: error.message,
126
+ device: deviceName
127
+ });
128
+ }
129
+ }
130
+
131
+ // Test 2: Turn diffuser light on and off
132
+ if (lightDevices.length < 1) {
133
+ results.push({
134
+ name: 'should turn diffuser light on and off',
135
+ passed: false,
136
+ skipped: true,
137
+ error: 'Could not find any DiffuserLight device within the given set of devices',
138
+ device: null
139
+ });
140
+ } else {
141
+ const light = lightDevices[0];
142
+ const deviceName = getDeviceName(light);
143
+
144
+ try {
145
+ await light.getDiffuserLightState();
146
+
147
+ // Set mode to FIXED RGB
148
+ await light.setDiffuserLight({
149
+ channel: 0,
150
+ mode: DiffuserLightMode.FIXED_RGB
151
+ });
152
+
153
+ // Turn device off
154
+ await light.setDiffuserLight({
155
+ channel: 0,
156
+ onoff: 0
157
+ });
158
+ await new Promise(resolve => setTimeout(resolve, 1000));
159
+
160
+ const isOff = light.getDiffuserLightIsOn(0);
161
+ if (isOff !== false) {
162
+ results.push({
163
+ name: 'should turn diffuser light on and off',
164
+ passed: false,
165
+ skipped: false,
166
+ error: `Device should be off but isOn returned ${isOff}`,
167
+ device: deviceName
168
+ });
169
+ return results;
170
+ }
171
+
172
+ // Change the color without turning it on
173
+ await light.setDiffuserLight({
174
+ channel: 0,
175
+ rgb: 0xFF0000 // Red
176
+ });
177
+ await new Promise(resolve => setTimeout(resolve, 1000));
178
+
179
+ // Make sure device is still off but color changed
180
+ const stillOff = light.getDiffuserLightIsOn(0);
181
+ const color = light.getDiffuserLightRgbColor(0);
182
+
183
+ if (stillOff !== false) {
184
+ results.push({
185
+ name: 'should turn diffuser light on and off',
186
+ passed: false,
187
+ skipped: false,
188
+ error: 'Device should still be off after color change',
189
+ device: deviceName
190
+ });
191
+ return results;
192
+ }
193
+
194
+ if (color && Array.isArray(color) && color.length === 3) {
195
+ if (color[0] !== 255 || color[1] !== 0 || color[2] !== 0) {
196
+ results.push({
197
+ name: 'should turn diffuser light on and off',
198
+ passed: false,
199
+ skipped: false,
200
+ error: `Color mismatch. Expected [255, 0, 0], got [${color[0]}, ${color[1]}, ${color[2]}]`,
201
+ device: deviceName
202
+ });
203
+ return results;
204
+ }
205
+ }
206
+
207
+ // Turn device on
208
+ await light.setDiffuserLight({
209
+ channel: 0,
210
+ onoff: 1
211
+ });
212
+ await new Promise(resolve => setTimeout(resolve, 1000));
213
+
214
+ const isOn = light.getDiffuserLightIsOn(0);
215
+ if (isOn !== true) {
216
+ results.push({
217
+ name: 'should turn diffuser light on and off',
218
+ passed: false,
219
+ skipped: false,
220
+ error: `Device should be on but isOn returned ${isOn}`,
221
+ device: deviceName
222
+ });
223
+ return results;
224
+ }
225
+
226
+ // Turn off device without changing color
227
+ await light.setDiffuserLight({
228
+ channel: 0,
229
+ onoff: 0
230
+ });
231
+ await new Promise(resolve => setTimeout(resolve, 1000));
232
+
233
+ const finalIsOff = light.getDiffuserLightIsOn(0);
234
+ if (finalIsOff !== false) {
235
+ results.push({
236
+ name: 'should turn diffuser light on and off',
237
+ passed: false,
238
+ skipped: false,
239
+ error: 'Device should be off but isOn returned true',
240
+ device: deviceName
241
+ });
242
+ return results;
243
+ }
244
+
245
+ results.push({
246
+ name: 'should turn diffuser light on and off',
247
+ passed: true,
248
+ skipped: false,
249
+ error: null,
250
+ device: deviceName
251
+ });
252
+
253
+ } catch (error) {
254
+ results.push({
255
+ name: 'should turn diffuser light on and off',
256
+ passed: false,
257
+ skipped: false,
258
+ error: error.message,
259
+ device: deviceName
260
+ });
261
+ }
262
+ }
263
+
264
+ // Test 3: Change diffuser light mode
265
+ if (lightDevices.length < 1) {
266
+ results.push({
267
+ name: 'should change diffuser light mode',
268
+ passed: false,
269
+ skipped: true,
270
+ error: 'Could not find any DiffuserLight device within the given set of devices',
271
+ device: null
272
+ });
273
+ } else {
274
+ const light = lightDevices[0];
275
+ const deviceName = getDeviceName(light);
276
+
277
+ try {
278
+ await light.getDiffuserLightState();
279
+
280
+ await light.setDiffuserLight({
281
+ channel: 0,
282
+ mode: DiffuserLightMode.FIXED_LUMINANCE,
283
+ onoff: 1
284
+ });
285
+ await new Promise(resolve => setTimeout(resolve, 1000));
286
+
287
+ const mode1 = light.getDiffuserLightMode(0);
288
+ if (mode1 !== DiffuserLightMode.FIXED_LUMINANCE) {
289
+ results.push({
290
+ name: 'should change diffuser light mode',
291
+ passed: false,
292
+ skipped: false,
293
+ error: `Failed to set FIXED_LUMINANCE. Expected ${DiffuserLightMode.FIXED_LUMINANCE}, got ${mode1}`,
294
+ device: deviceName
295
+ });
296
+ return results;
297
+ }
298
+
299
+ await light.setDiffuserLight({
300
+ channel: 0,
301
+ mode: DiffuserLightMode.ROTATING_COLORS
302
+ });
303
+ await new Promise(resolve => setTimeout(resolve, 1000));
304
+
305
+ const mode2 = light.getDiffuserLightMode(0);
306
+ if (mode2 !== DiffuserLightMode.ROTATING_COLORS) {
307
+ results.push({
308
+ name: 'should change diffuser light mode',
309
+ passed: false,
310
+ skipped: false,
311
+ error: `Failed to set ROTATING_COLORS. Expected ${DiffuserLightMode.ROTATING_COLORS}, got ${mode2}`,
312
+ device: deviceName
313
+ });
314
+ return results;
315
+ }
316
+
317
+ await light.setDiffuserLight({
318
+ channel: 0,
319
+ mode: DiffuserLightMode.FIXED_RGB
320
+ });
321
+ await new Promise(resolve => setTimeout(resolve, 1000));
322
+
323
+ const mode3 = light.getDiffuserLightMode(0);
324
+ if (mode3 !== DiffuserLightMode.FIXED_RGB) {
325
+ results.push({
326
+ name: 'should change diffuser light mode',
327
+ passed: false,
328
+ skipped: false,
329
+ error: `Failed to set FIXED_RGB. Expected ${DiffuserLightMode.FIXED_RGB}, got ${mode3}`,
330
+ device: deviceName
331
+ });
332
+ return results;
333
+ }
334
+
335
+ results.push({
336
+ name: 'should change diffuser light mode',
337
+ passed: true,
338
+ skipped: false,
339
+ error: null,
340
+ device: deviceName
341
+ });
342
+
343
+ } catch (error) {
344
+ results.push({
345
+ name: 'should change diffuser light mode',
346
+ passed: false,
347
+ skipped: false,
348
+ error: error.message,
349
+ device: deviceName
350
+ });
351
+ }
352
+ }
353
+
354
+ // Test 4: Change diffuser light brightness
355
+ if (lightDevices.length < 1) {
356
+ results.push({
357
+ name: 'should change diffuser light brightness',
358
+ passed: false,
359
+ skipped: true,
360
+ error: 'Could not find any DiffuserLight device within the given set of devices',
361
+ device: null
362
+ });
363
+ } else {
364
+ const light = lightDevices[0];
365
+ const deviceName = getDeviceName(light);
366
+
367
+ try {
368
+ await light.getDiffuserLightState();
369
+
370
+ await light.setDiffuserLight({
371
+ channel: 0,
372
+ onoff: 1
373
+ });
374
+
375
+ // Test brightness from 0 to 100 in steps of 10
376
+ let allPassed = true;
377
+ for (let i = 0; i <= 100; i += 10) {
378
+ await light.setDiffuserLight({
379
+ channel: 0,
380
+ luminance: i
381
+ });
382
+ await new Promise(resolve => setTimeout(resolve, 500));
383
+
384
+ const brightness = light.getDiffuserLightBrightness(0);
385
+ if (brightness !== i) {
386
+ allPassed = false;
387
+ break;
388
+ }
389
+ }
390
+
391
+ if (!allPassed) {
392
+ results.push({
393
+ name: 'should change diffuser light brightness',
394
+ passed: false,
395
+ skipped: false,
396
+ error: 'Brightness values did not match expected values',
397
+ device: deviceName
398
+ });
399
+ } else {
400
+ results.push({
401
+ name: 'should change diffuser light brightness',
402
+ passed: true,
403
+ skipped: false,
404
+ error: null,
405
+ device: deviceName
406
+ });
407
+ }
408
+ } catch (error) {
409
+ results.push({
410
+ name: 'should change diffuser light brightness',
411
+ passed: false,
412
+ skipped: false,
413
+ error: error.message,
414
+ device: deviceName
415
+ });
416
+ }
417
+ }
418
+
419
+ // Test 5: Change diffuser spray mode
420
+ if (sprayDevices.length < 1) {
421
+ results.push({
422
+ name: 'should change diffuser spray mode',
423
+ passed: false,
424
+ skipped: true,
425
+ error: 'Could not find any DiffuserSpray device within the given set of devices',
426
+ device: null
427
+ });
428
+ } else {
429
+ const spray = sprayDevices[0];
430
+ const deviceName = getDeviceName(spray);
431
+
432
+ try {
433
+ await spray.getDiffuserSprayState();
434
+
435
+ await spray.setDiffuserSpray(0, DiffuserSprayMode.LIGHT);
436
+ await new Promise(resolve => setTimeout(resolve, 1000));
437
+
438
+ const mode1 = spray.getDiffuserSprayMode(0);
439
+ if (mode1 !== DiffuserSprayMode.LIGHT) {
440
+ results.push({
441
+ name: 'should change diffuser spray mode',
442
+ passed: false,
443
+ skipped: false,
444
+ error: `Failed to set LIGHT. Expected ${DiffuserSprayMode.LIGHT}, got ${mode1}`,
445
+ device: deviceName
446
+ });
447
+ return results;
448
+ }
449
+
450
+ await spray.setDiffuserSpray(0, DiffuserSprayMode.STRONG);
451
+ await new Promise(resolve => setTimeout(resolve, 1000));
452
+
453
+ const mode2 = spray.getDiffuserSprayMode(0);
454
+ if (mode2 !== DiffuserSprayMode.STRONG) {
455
+ results.push({
456
+ name: 'should change diffuser spray mode',
457
+ passed: false,
458
+ skipped: false,
459
+ error: `Failed to set STRONG. Expected ${DiffuserSprayMode.STRONG}, got ${mode2}`,
460
+ device: deviceName
461
+ });
462
+ return results;
463
+ }
464
+
465
+ await spray.setDiffuserSpray(0, DiffuserSprayMode.OFF);
466
+ await new Promise(resolve => setTimeout(resolve, 1000));
467
+
468
+ const mode3 = spray.getDiffuserSprayMode(0);
469
+ if (mode3 !== DiffuserSprayMode.OFF) {
470
+ results.push({
471
+ name: 'should change diffuser spray mode',
472
+ passed: false,
473
+ skipped: false,
474
+ error: `Failed to set OFF. Expected ${DiffuserSprayMode.OFF}, got ${mode3}`,
475
+ device: deviceName
476
+ });
477
+ return results;
478
+ }
479
+
480
+ results.push({
481
+ name: 'should change diffuser spray mode',
482
+ passed: true,
483
+ skipped: false,
484
+ error: null,
485
+ device: deviceName
486
+ });
487
+
488
+ } catch (error) {
489
+ results.push({
490
+ name: 'should change diffuser spray mode',
491
+ passed: false,
492
+ skipped: false,
493
+ error: error.message,
494
+ device: deviceName
495
+ });
496
+ }
497
+ }
498
+
499
+ return results;
500
+ }
501
+
502
+ module.exports = {
503
+ metadata,
504
+ runTests
505
+ };