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,365 @@
1
+ # Meross Cloud Tests
2
+
3
+ This directory contains test files for the Meross Cloud library. The test system uses a custom lightweight test runner with no external test framework dependencies.
4
+
5
+ ## Test System Architecture
6
+
7
+ The test system is designed for simplicity, maintainability, and excellent error reporting:
8
+
9
+ - **Custom Test Runner**: Lightweight async execution engine (no Mocha dependency)
10
+ - **Explicit Dependency Injection**: All dependencies passed via context object (no globals)
11
+ - **Structured Results**: Detailed test results with device context and error information
12
+ - **Better Error Reporting**: Rich error messages with device names and stack traces
13
+
14
+ ## Test Files
15
+
16
+ - `test-runner.js` - Custom lightweight test runner for CLI integration
17
+ - `test-registry.js` - Centralized test metadata and auto-discovery
18
+ - `test-helper.js` - Pure utility functions for device discovery and connection management
19
+ - `test-template.js` - Template/example showing new test file structure
20
+ - `test-toggle.js` - Tests for toggle/switch devices
21
+ - `test-light.js` - Tests for light/RGB devices
22
+ - `test-thermostat.js` - Tests for thermostat devices
23
+ - `test-roller-shutter.js` - Tests for roller shutter devices
24
+ - `test-garage.js` - Tests for garage door opener devices
25
+ - `test-spray.js` - Tests for spray/humidifier devices
26
+ - `test-diffuser.js` - Tests for diffuser devices
27
+ - `test-electricity.js` - Tests for electricity/consumption monitoring devices
28
+ - `test-dnd.js` - Tests for Do-Not-Disturb (DND) mode functionality
29
+ - `test-child-lock.js` - Tests for physical lock/child lock functionality
30
+ - `test-timer.js` - Tests for timer functionality (TimerX namespace)
31
+ - `test-trigger.js` - Tests for trigger functionality (TriggerX namespace)
32
+ - `test-alarm.js` - Tests for alarm functionality
33
+ - `test-runtime.js` - Tests for runtime information
34
+ - `test-encryption.js` - Tests for encryption support
35
+ - `test-hub-sensors.js` - Tests for hub sensors (temperature/humidity, water leak, smoke, battery)
36
+ - `test-hub-mts100.js` - Tests for hub MTS100 thermostat valves
37
+ - `test-screen.js` - Tests for screen brightness
38
+ - `test-sensor-history.js` - Tests for sensor history
39
+ - `test-smoke-config.js` - Tests for smoke sensor configuration
40
+ - `test-temp-unit.js` - Tests for temperature unit configuration
41
+ - `test-config.js` - Tests for config features (over temp)
42
+ - `test-control.js` - Tests for control features (multiple commands, upgrade, over temp)
43
+ - `test-presence.js` - Tests for presence sensor devices
44
+
45
+ ## Running Tests
46
+
47
+ Tests are only available via the CLI tool. They cannot be run directly with npm or mocha.
48
+
49
+ ### Prerequisites
50
+
51
+ Set environment variables for authentication:
52
+ ```bash
53
+ export MEROSS_EMAIL="your-email@example.com"
54
+ export MEROSS_PASSWORD="your-password"
55
+ ```
56
+
57
+ Optional: Use cached credentials (base64 encoded token data):
58
+ ```bash
59
+ export MEROSS_TOKEN_DATA="base64-encoded-credentials"
60
+ ```
61
+
62
+ ### Run Tests via CLI
63
+
64
+ Start the CLI in interactive mode:
65
+ ```bash
66
+ npm run cli
67
+ # or
68
+ meross-cli interactive
69
+ ```
70
+
71
+ Then run tests:
72
+ ```bash
73
+ meross> test electricity
74
+ meross> test toggle
75
+ meross> test light
76
+ # etc.
77
+ ```
78
+
79
+ Or run directly from command line:
80
+ ```bash
81
+ meross-cli test electricity --email your@email.com --password yourpass
82
+ ```
83
+
84
+ ### Available Test Types
85
+
86
+ - `toggle` (or `switch`) - Toggle/switch devices
87
+ - `light` (or `rgb`, `lamp`) - Light/RGB devices
88
+ - `thermostat` - Thermostat devices
89
+ - `roller-shutter` (or `shutter`, `blind`) - Roller shutter devices
90
+ - `garage` - Garage door opener devices
91
+ - `spray` - Spray/humidifier devices
92
+ - `diffuser` - Diffuser devices
93
+ - `electricity` - Electricity/consumption monitoring devices
94
+ - `dnd` - Do-Not-Disturb mode
95
+ - `child-lock` - Physical lock/child lock
96
+ - `timer` - Timer functionality
97
+ - `trigger` - Trigger functionality
98
+ - `alarm` - Alarm functionality
99
+ - `runtime` - Runtime information
100
+ - `encryption` - Encryption support
101
+ - `hub-sensors` (or `hub`) - Hub sensors (temperature/humidity, water leak, smoke, battery)
102
+ - `hub-mts100` (or `mts100`) - Hub MTS100 thermostat valves
103
+ - `screen` - Screen brightness
104
+ - `sensor-history` - Sensor history
105
+ - `smoke-config` - Smoke sensor configuration
106
+ - `temp-unit` - Temperature unit configuration
107
+ - `config` - Config features (over temp)
108
+ - `control` - Control features (multiple commands, upgrade, over temp)
109
+ - `presence` - Presence sensor devices
110
+
111
+ ## Writing New Tests
112
+
113
+ ### Test File Structure
114
+
115
+ Each test file follows this structure:
116
+
117
+ ```javascript
118
+ 'use strict';
119
+
120
+ /**
121
+ * Test Description
122
+ * Brief description of what this test covers
123
+ */
124
+
125
+ const { findDevicesByAbility, getDeviceName, OnlineStatus } = require('./test-helper');
126
+
127
+ // Export metadata object
128
+ const metadata = {
129
+ name: 'test-name',
130
+ description: 'Brief description of the test',
131
+ requiredAbilities: ['Appliance.Control.Namespace'],
132
+ aliases: ['alias1', 'alias2'], // Optional
133
+ minDevices: 1
134
+ };
135
+
136
+ // Export runTests function
137
+ async function runTests(context) {
138
+ const { manager, devices, options = {} } = context;
139
+ const timeout = options.timeout || 30000;
140
+ const results = [];
141
+
142
+ // If no devices provided, discover them
143
+ let testDevices = devices || [];
144
+ if (testDevices.length === 0) {
145
+ testDevices = await findDevicesByAbility(manager, 'Appliance.Control.Namespace', OnlineStatus.ONLINE);
146
+ }
147
+
148
+ if (testDevices.length === 0) {
149
+ results.push({
150
+ name: 'should perform test action',
151
+ passed: false,
152
+ skipped: true,
153
+ error: 'No suitable devices found',
154
+ device: null
155
+ });
156
+ return results;
157
+ }
158
+
159
+ const testDevice = testDevices[0];
160
+ const deviceName = getDeviceName(testDevice);
161
+
162
+ // Test 1: Perform some action
163
+ try {
164
+ const result = await testDevice.someMethod();
165
+
166
+ if (!result) {
167
+ results.push({
168
+ name: 'should perform test action',
169
+ passed: false,
170
+ skipped: false,
171
+ error: 'Method returned null or undefined',
172
+ device: deviceName
173
+ });
174
+ } else {
175
+ results.push({
176
+ name: 'should perform test action',
177
+ passed: true,
178
+ skipped: false,
179
+ error: null,
180
+ device: deviceName,
181
+ details: { result: result }
182
+ });
183
+ }
184
+ } catch (error) {
185
+ results.push({
186
+ name: 'should perform test action',
187
+ passed: false,
188
+ skipped: false,
189
+ error: error.message,
190
+ device: deviceName
191
+ });
192
+ }
193
+
194
+ return results;
195
+ }
196
+
197
+ module.exports = {
198
+ metadata,
199
+ runTests
200
+ };
201
+ ```
202
+
203
+ ### Key Points
204
+
205
+ 1. **Metadata Export**: Every test file must export a `metadata` object with:
206
+ - `name`: Test type identifier
207
+ - `description`: Human-readable description
208
+ - `requiredAbilities`: Array of ability namespaces required
209
+ - `aliases`: Optional array of alternative names
210
+ - `minDevices`: Minimum number of devices required (default: 1)
211
+
212
+ 2. **runTests Function**: Must accept a `context` object with:
213
+ - `manager`: MerossManager instance
214
+ - `devices`: Array of pre-selected devices (optional, will auto-discover if not provided)
215
+ - `options`: Test options (timeout, verbose, etc.)
216
+
217
+ 3. **Return Value**: Must return an array of test result objects:
218
+ ```javascript
219
+ {
220
+ name: 'Test name',
221
+ passed: true/false,
222
+ skipped: true/false,
223
+ error: 'Error message or null',
224
+ device: 'Device name or null',
225
+ details: { /* optional additional info */ }
226
+ }
227
+ ```
228
+
229
+ 4. **No Globals**: All dependencies are passed explicitly via the context object.
230
+
231
+ 5. **Error Handling**: Always wrap test logic in try-catch blocks and return structured results.
232
+
233
+ ### Test Helper Functions
234
+
235
+ The `test-helper.js` module provides pure utility functions:
236
+
237
+ - `waitForDevices(manager, timeout)` - Wait for devices to be discovered
238
+ - `findDevicesByAbility(manager, namespace, onlineStatus)` - Find devices by ability namespace
239
+ - `findDevicesByType(manager, deviceType, onlineStatus)` - Find devices by device type
240
+ - `waitForDeviceConnection(device, timeout)` - Wait for device to connect
241
+ - `waitForPushNotification(device, namespace, timeout)` - Wait for push notification
242
+ - `getDeviceName(device)` - Get device display name
243
+ - `deviceHasAbility(device, namespace)` - Check if device has specific ability
244
+ - `getDeviceOnlineStatus(device)` - Get device online status
245
+
246
+ All functions accept explicit parameters - no globals or environment variables.
247
+
248
+ ### Example: Simple Test
249
+
250
+ ```javascript
251
+ 'use strict';
252
+
253
+ const { findDevicesByAbility, getDeviceName, OnlineStatus } = require('./test-helper');
254
+
255
+ const metadata = {
256
+ name: 'simple-test',
257
+ description: 'Tests simple device functionality',
258
+ requiredAbilities: ['Appliance.Control.Simple'],
259
+ minDevices: 1
260
+ };
261
+
262
+ async function runTests(context) {
263
+ const { manager, devices } = context;
264
+ const results = [];
265
+
266
+ let testDevices = devices || [];
267
+ if (testDevices.length === 0) {
268
+ testDevices = await findDevicesByAbility(manager, 'Appliance.Control.Simple', OnlineStatus.ONLINE);
269
+ }
270
+
271
+ if (testDevices.length === 0) {
272
+ results.push({
273
+ name: 'should find devices',
274
+ passed: false,
275
+ skipped: true,
276
+ error: 'No devices found',
277
+ device: null
278
+ });
279
+ return results;
280
+ }
281
+
282
+ const device = testDevices[0];
283
+ const deviceName = getDeviceName(device);
284
+
285
+ try {
286
+ const state = await device.getState();
287
+ results.push({
288
+ name: 'should get device state',
289
+ passed: !!state,
290
+ skipped: false,
291
+ error: state ? null : 'State is null',
292
+ device: deviceName
293
+ });
294
+ } catch (error) {
295
+ results.push({
296
+ name: 'should get device state',
297
+ passed: false,
298
+ skipped: false,
299
+ error: error.message,
300
+ device: deviceName
301
+ });
302
+ }
303
+
304
+ return results;
305
+ }
306
+
307
+ module.exports = { metadata, runTests };
308
+ ```
309
+
310
+ ## Test Results Format
311
+
312
+ Test results are returned in a structured format:
313
+
314
+ ```javascript
315
+ {
316
+ success: true/false, // Overall success status
317
+ passed: 5, // Number of passed tests
318
+ failed: 1, // Number of failed tests
319
+ skipped: 2, // Number of skipped tests
320
+ duration: 12345, // Duration in milliseconds
321
+ error: 'Error message or null', // Overall error (if any)
322
+ stack: 'Stack trace', // Stack trace (if error)
323
+ tests: [ // Array of individual test results
324
+ {
325
+ name: 'Test name',
326
+ passed: true/false,
327
+ skipped: true/false,
328
+ error: 'Error message or null',
329
+ device: 'Device name',
330
+ details: { /* optional */ }
331
+ }
332
+ ]
333
+ }
334
+ ```
335
+
336
+ ## Debugging
337
+
338
+ The test runner provides detailed error reporting:
339
+
340
+ - **Device Context**: Each test result includes the device name
341
+ - **Error Messages**: Clear error messages with context
342
+ - **Stack Traces**: Full stack traces for debugging
343
+ - **Progress Indicators**: Shows which device is being tested
344
+ - **Structured Output**: Easy to parse and analyze
345
+
346
+ ## Notes
347
+
348
+ - Tests require actual Meross devices to be available in your account
349
+ - Tests will skip if no suitable devices are found
350
+ - Some tests may take longer due to device operations (e.g., garage doors, roller shutters)
351
+ - Push notification tests may require special setup
352
+ - All tests use explicit dependency injection - no globals or environment variables
353
+
354
+ ## Migration from Old System
355
+
356
+ The old test system used Mocha/Chai and global variables. The new system:
357
+
358
+ - ✅ No Mocha dependency
359
+ - ✅ No global variables
360
+ - ✅ Explicit dependency injection
361
+ - ✅ Better error reporting
362
+ - ✅ Structured results
363
+ - ✅ Easier to debug
364
+
365
+ See `test-template.js` for a complete example of the new structure.
@@ -0,0 +1,144 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Alarm Device Tests
5
+ * Tests alarm functionality and notifications
6
+ */
7
+
8
+ const { findDevicesByAbility, waitForDeviceConnection, getDeviceName, OnlineStatus } = require('./test-helper');
9
+
10
+ const metadata = {
11
+ name: 'alarm',
12
+ description: 'Tests alarm functionality and notifications',
13
+ requiredAbilities: ['Appliance.Control.Alarm'],
14
+ minDevices: 1
15
+ };
16
+
17
+ async function runTests(context) {
18
+ const { manager, devices, options = {} } = context;
19
+ const timeout = options.timeout || 30000;
20
+ const results = [];
21
+
22
+ // If no devices provided, discover them
23
+ let testDevices = devices || [];
24
+ if (testDevices.length === 0) {
25
+ testDevices = await findDevicesByAbility(manager, 'Appliance.Control.Alarm', OnlineStatus.ONLINE);
26
+ }
27
+
28
+ // Wait for devices to be connected
29
+ for (const device of testDevices) {
30
+ await waitForDeviceConnection(device, timeout);
31
+ await new Promise(resolve => setTimeout(resolve, 2000));
32
+ }
33
+
34
+ if (testDevices.length === 0) {
35
+ results.push({
36
+ name: 'should get alarm status',
37
+ passed: false,
38
+ skipped: true,
39
+ error: 'No Alarm device has been found to run this test on',
40
+ device: null
41
+ });
42
+ return results;
43
+ }
44
+
45
+ const testDevice = testDevices[0];
46
+ const deviceName = getDeviceName(testDevice);
47
+
48
+ // Test 1: Get alarm status
49
+ try {
50
+ const response = await testDevice.getAlarmStatus({ channel: 0 });
51
+
52
+ if (!response) {
53
+ results.push({
54
+ name: 'should get alarm status',
55
+ passed: false,
56
+ skipped: false,
57
+ error: 'getAlarmStatus returned null or undefined',
58
+ device: deviceName
59
+ });
60
+ } else if (!Array.isArray(response.alarm)) {
61
+ results.push({
62
+ name: 'should get alarm status',
63
+ passed: false,
64
+ skipped: false,
65
+ error: 'Response alarm is not an array',
66
+ device: deviceName
67
+ });
68
+ } else {
69
+ results.push({
70
+ name: 'should get alarm status',
71
+ passed: true,
72
+ skipped: false,
73
+ error: null,
74
+ device: deviceName,
75
+ details: { alarm: response.alarm }
76
+ });
77
+ }
78
+ } catch (error) {
79
+ results.push({
80
+ name: 'should get alarm status',
81
+ passed: false,
82
+ skipped: false,
83
+ error: error.message,
84
+ device: deviceName
85
+ });
86
+ }
87
+
88
+ // Test 2: Store alarm events from push notifications
89
+ try {
90
+ // Get initial alarm events (should be empty or existing events)
91
+ const initialEvents = testDevice.getLastAlarmEvents();
92
+
93
+ if (!Array.isArray(initialEvents)) {
94
+ results.push({
95
+ name: 'should store alarm events from push notifications',
96
+ passed: false,
97
+ skipped: false,
98
+ error: 'getLastAlarmEvents did not return an array',
99
+ device: deviceName
100
+ });
101
+ } else {
102
+ // Get current alarm status
103
+ await testDevice.getAlarmStatus({ channel: 0 });
104
+ await new Promise(resolve => setTimeout(resolve, 1000));
105
+
106
+ // Check that getLastAlarmEvents returns an array (even if empty)
107
+ const events = testDevice.getLastAlarmEvents();
108
+
109
+ if (!Array.isArray(events)) {
110
+ results.push({
111
+ name: 'should store alarm events from push notifications',
112
+ passed: false,
113
+ skipped: false,
114
+ error: 'getLastAlarmEvents did not return an array after getAlarmStatus',
115
+ device: deviceName
116
+ });
117
+ } else {
118
+ results.push({
119
+ name: 'should store alarm events from push notifications',
120
+ passed: true,
121
+ skipped: false,
122
+ error: null,
123
+ device: deviceName,
124
+ details: { eventCount: events.length }
125
+ });
126
+ }
127
+ }
128
+ } catch (error) {
129
+ results.push({
130
+ name: 'should store alarm events from push notifications',
131
+ passed: false,
132
+ skipped: false,
133
+ error: error.message,
134
+ device: deviceName
135
+ });
136
+ }
137
+
138
+ return results;
139
+ }
140
+
141
+ module.exports = {
142
+ metadata,
143
+ runTests
144
+ };