meross-cli 0.4.0 → 0.5.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 +11 -0
- package/README.md +28 -2
- package/cli/commands/control/execute.js +36 -6
- package/cli/commands/control/params/index.js +16 -12
- package/cli/commands/control/params/light.js +55 -25
- package/cli/commands/control/params/thermostat.js +24 -22
- package/cli/commands/control/params/timer.js +18 -15
- package/cli/commands/control/params/trigger.js +24 -13
- package/cli/commands/info.js +38 -14
- package/cli/commands/sniffer/sniffer-menu.js +2 -2
- package/cli/commands/status/device-status.js +418 -1292
- package/cli/commands/status/hub-status.js +14 -6
- package/cli/control-registry.js +211 -406
- package/cli/meross-cli.js +1 -1
- package/cli/tests/README.md +2 -0
- package/cli/tests/test-alarm.js +22 -2
- package/cli/tests/test-child-lock.js +40 -10
- package/cli/tests/test-config.js +22 -2
- package/cli/tests/test-control.js +8 -8
- package/cli/tests/test-diffuser.js +7 -7
- package/cli/tests/test-dnd.js +87 -66
- package/cli/tests/test-electricity.js +37 -33
- package/cli/tests/test-encryption.js +13 -13
- package/cli/tests/test-garage.js +12 -14
- package/cli/tests/test-helper.js +1 -1
- package/cli/tests/test-hub-sensors.js +3 -3
- package/cli/tests/test-light.js +497 -105
- package/cli/tests/test-presence.js +10 -55
- package/cli/tests/test-registry.js +7 -1
- package/cli/tests/test-roller-shutter.js +78 -90
- package/cli/tests/test-screen.js +1 -1
- package/cli/tests/test-sensor-history.js +6 -2
- package/cli/tests/test-smoke-config.js +24 -4
- package/cli/tests/test-spray.js +11 -11
- package/cli/tests/test-system.js +375 -0
- package/cli/tests/test-temp-unit.js +22 -2
- package/cli/tests/test-template.js +61 -73
- package/cli/tests/test-thermostat.js +126 -89
- package/cli/tests/test-timer.js +8 -51
- package/cli/tests/test-toggle.js +49 -173
- package/cli/tests/test-trigger.js +7 -50
- package/package.json +2 -2
package/cli/tests/test-toggle.js
CHANGED
|
@@ -87,22 +87,19 @@ async function runTests(context) {
|
|
|
87
87
|
|
|
88
88
|
// Test 2: Get toggle state
|
|
89
89
|
try {
|
|
90
|
-
if (
|
|
91
|
-
const toggleState = await testDevice.
|
|
90
|
+
if (testDevice.toggle) {
|
|
91
|
+
const toggleState = await testDevice.toggle.get({ channel: 0 });
|
|
92
|
+
const isOn = testDevice.toggle.isOn({ channel: 0 });
|
|
92
93
|
|
|
93
94
|
if (!toggleState) {
|
|
94
95
|
results.push({
|
|
95
96
|
name: 'should get toggle state',
|
|
96
97
|
passed: false,
|
|
97
98
|
skipped: false,
|
|
98
|
-
error: '
|
|
99
|
+
error: 'toggle.get() returned null or undefined',
|
|
99
100
|
device: deviceName
|
|
100
101
|
});
|
|
101
102
|
} else {
|
|
102
|
-
// Check cached toggle state
|
|
103
|
-
const cachedState = testDevice.getCachedToggleState(0);
|
|
104
|
-
const isOn = testDevice.isOn(0);
|
|
105
|
-
|
|
106
103
|
results.push({
|
|
107
104
|
name: 'should get toggle state',
|
|
108
105
|
passed: true,
|
|
@@ -111,7 +108,6 @@ async function runTests(context) {
|
|
|
111
108
|
device: deviceName,
|
|
112
109
|
details: {
|
|
113
110
|
toggleState: toggleState,
|
|
114
|
-
cachedState: cachedState,
|
|
115
111
|
isOn: isOn
|
|
116
112
|
}
|
|
117
113
|
});
|
|
@@ -121,7 +117,7 @@ async function runTests(context) {
|
|
|
121
117
|
name: 'should get toggle state',
|
|
122
118
|
passed: false,
|
|
123
119
|
skipped: true,
|
|
124
|
-
error: 'Device does not support
|
|
120
|
+
error: 'Device does not support toggle feature',
|
|
125
121
|
device: deviceName
|
|
126
122
|
});
|
|
127
123
|
}
|
|
@@ -137,141 +133,69 @@ async function runTests(context) {
|
|
|
137
133
|
|
|
138
134
|
// Test 3: Control toggle state (turn on/off)
|
|
139
135
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Test turnOn if available
|
|
152
|
-
if (typeof testDevice.turnOn === 'function') {
|
|
153
|
-
const turnOnResult = await testDevice.turnOn(0);
|
|
136
|
+
if (!testDevice.toggle) {
|
|
137
|
+
results.push({
|
|
138
|
+
name: 'should control toggle state',
|
|
139
|
+
passed: false,
|
|
140
|
+
skipped: true,
|
|
141
|
+
error: 'Device does not support toggle feature',
|
|
142
|
+
device: deviceName
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
// Get initial state
|
|
146
|
+
let initialState = testDevice.toggle.isOn({ channel: 0 });
|
|
154
147
|
|
|
155
|
-
|
|
148
|
+
// Test turn on
|
|
149
|
+
await testDevice.toggle.set({ channel: 0, on: true });
|
|
150
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
151
|
+
|
|
152
|
+
const isOnAfter = testDevice.toggle.isOn({ channel: 0 });
|
|
153
|
+
if (!isOnAfter) {
|
|
156
154
|
results.push({
|
|
157
155
|
name: 'should control toggle state (turn on)',
|
|
158
156
|
passed: false,
|
|
159
157
|
skipped: false,
|
|
160
|
-
error: '
|
|
158
|
+
error: 'Device did not turn on after set({ on: true })',
|
|
161
159
|
device: deviceName
|
|
162
160
|
});
|
|
163
161
|
} else {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
results.push({
|
|
172
|
-
name: 'should control toggle state (turn on)',
|
|
173
|
-
passed: false,
|
|
174
|
-
skipped: false,
|
|
175
|
-
error: 'Device did not turn on after turnOn() call',
|
|
176
|
-
device: deviceName
|
|
177
|
-
});
|
|
178
|
-
} else {
|
|
179
|
-
results.push({
|
|
180
|
-
name: 'should control toggle state (turn on)',
|
|
181
|
-
passed: true,
|
|
182
|
-
skipped: false,
|
|
183
|
-
error: null,
|
|
184
|
-
device: deviceName
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
// Can't verify, but command succeeded
|
|
189
|
-
results.push({
|
|
190
|
-
name: 'should control toggle state (turn on)',
|
|
191
|
-
passed: true,
|
|
192
|
-
skipped: false,
|
|
193
|
-
error: null,
|
|
194
|
-
device: deviceName,
|
|
195
|
-
details: { note: 'turnOn succeeded but cannot verify state (isOn not available)' }
|
|
196
|
-
});
|
|
197
|
-
}
|
|
162
|
+
results.push({
|
|
163
|
+
name: 'should control toggle state (turn on)',
|
|
164
|
+
passed: true,
|
|
165
|
+
skipped: false,
|
|
166
|
+
error: null,
|
|
167
|
+
device: deviceName
|
|
168
|
+
});
|
|
198
169
|
}
|
|
199
|
-
} else {
|
|
200
|
-
results.push({
|
|
201
|
-
name: 'should control toggle state (turn on)',
|
|
202
|
-
passed: false,
|
|
203
|
-
skipped: true,
|
|
204
|
-
error: 'Device does not support turnOn',
|
|
205
|
-
device: deviceName
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Test turnOff if available
|
|
210
|
-
if (typeof testDevice.turnOff === 'function') {
|
|
211
|
-
const turnOffResult = await testDevice.turnOff(0);
|
|
212
170
|
|
|
213
|
-
|
|
171
|
+
// Test turn off
|
|
172
|
+
await testDevice.toggle.set({ channel: 0, on: false });
|
|
173
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
174
|
+
|
|
175
|
+
const isOffAfter = testDevice.toggle.isOn({ channel: 0 });
|
|
176
|
+
if (isOffAfter) {
|
|
214
177
|
results.push({
|
|
215
178
|
name: 'should control toggle state (turn off)',
|
|
216
179
|
passed: false,
|
|
217
180
|
skipped: false,
|
|
218
|
-
error: '
|
|
181
|
+
error: 'Device did not turn off after set({ on: false })',
|
|
219
182
|
device: deviceName
|
|
220
183
|
});
|
|
221
184
|
} else {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
results.push({
|
|
230
|
-
name: 'should control toggle state (turn off)',
|
|
231
|
-
passed: false,
|
|
232
|
-
skipped: false,
|
|
233
|
-
error: 'Device did not turn off after turnOff() call',
|
|
234
|
-
device: deviceName
|
|
235
|
-
});
|
|
236
|
-
} else {
|
|
237
|
-
results.push({
|
|
238
|
-
name: 'should control toggle state (turn off)',
|
|
239
|
-
passed: true,
|
|
240
|
-
skipped: false,
|
|
241
|
-
error: null,
|
|
242
|
-
device: deviceName
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
// Can't verify, but command succeeded
|
|
247
|
-
results.push({
|
|
248
|
-
name: 'should control toggle state (turn off)',
|
|
249
|
-
passed: true,
|
|
250
|
-
skipped: false,
|
|
251
|
-
error: null,
|
|
252
|
-
device: deviceName,
|
|
253
|
-
details: { note: 'turnOff succeeded but cannot verify state (isOn not available)' }
|
|
254
|
-
});
|
|
255
|
-
}
|
|
185
|
+
results.push({
|
|
186
|
+
name: 'should control toggle state (turn off)',
|
|
187
|
+
passed: true,
|
|
188
|
+
skipped: false,
|
|
189
|
+
error: null,
|
|
190
|
+
device: deviceName
|
|
191
|
+
});
|
|
256
192
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
error: 'Device does not support turnOff',
|
|
263
|
-
device: deviceName
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Restore initial state if we changed it
|
|
268
|
-
if (initialState !== undefined && typeof testDevice.turnOn === 'function' && typeof testDevice.turnOff === 'function') {
|
|
269
|
-
if (initialState === 1 || initialState === true) {
|
|
270
|
-
await testDevice.turnOn(0);
|
|
271
|
-
} else {
|
|
272
|
-
await testDevice.turnOff(0);
|
|
193
|
+
|
|
194
|
+
// Restore initial state if we changed it
|
|
195
|
+
if (initialState !== undefined) {
|
|
196
|
+
await testDevice.toggle.set({ channel: 0, on: initialState });
|
|
197
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
273
198
|
}
|
|
274
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
275
199
|
}
|
|
276
200
|
|
|
277
201
|
} catch (error) {
|
|
@@ -284,54 +208,6 @@ async function runTests(context) {
|
|
|
284
208
|
});
|
|
285
209
|
}
|
|
286
210
|
|
|
287
|
-
// Test 4: Handle toggle push notifications
|
|
288
|
-
try {
|
|
289
|
-
const testDeviceForPush = toggleDevices[0];
|
|
290
|
-
|
|
291
|
-
// Set up listener for toggle push notifications
|
|
292
|
-
let receivedNotification = false;
|
|
293
|
-
const notificationHandler = (notification) => {
|
|
294
|
-
if (notification.namespace === 'Appliance.Control.ToggleX' ||
|
|
295
|
-
notification.namespace === 'Appliance.Control.Toggle') {
|
|
296
|
-
receivedNotification = true;
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
testDeviceForPush.on('pushNotification', notificationHandler);
|
|
301
|
-
|
|
302
|
-
// Get toggle state (may trigger a push notification)
|
|
303
|
-
if (typeof testDeviceForPush.getToggleState === 'function') {
|
|
304
|
-
await testDeviceForPush.getToggleState(0);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Wait a bit for potential push notifications
|
|
308
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
309
|
-
|
|
310
|
-
// Remove listener
|
|
311
|
-
testDeviceForPush.removeListener('pushNotification', notificationHandler);
|
|
312
|
-
|
|
313
|
-
// Note: We don't assert on receivedNotification since push notifications
|
|
314
|
-
// are device-initiated and may not occur during testing
|
|
315
|
-
// This test just verifies the listener mechanism works
|
|
316
|
-
results.push({
|
|
317
|
-
name: 'should handle toggle push notifications',
|
|
318
|
-
passed: true,
|
|
319
|
-
skipped: false,
|
|
320
|
-
error: null,
|
|
321
|
-
device: getDeviceName(testDeviceForPush),
|
|
322
|
-
details: { notificationReceived: receivedNotification }
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
} catch (error) {
|
|
326
|
-
results.push({
|
|
327
|
-
name: 'should handle toggle push notifications',
|
|
328
|
-
passed: false,
|
|
329
|
-
skipped: false,
|
|
330
|
-
error: error.message,
|
|
331
|
-
device: toggleDevices.length > 0 ? getDeviceName(toggleDevices[0]) : null
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
211
|
return results;
|
|
336
212
|
}
|
|
337
213
|
|
|
@@ -81,7 +81,7 @@ async function runTests(context) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
-
testDevice.on('
|
|
84
|
+
testDevice.on('pushNotificationReceived', handler);
|
|
85
85
|
|
|
86
86
|
// Timeout after 5 seconds if no push notification arrives
|
|
87
87
|
setTimeout(() => {
|
|
@@ -90,7 +90,7 @@ async function runTests(context) {
|
|
|
90
90
|
}, 5000);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
const createResult = await testDevice.
|
|
93
|
+
const createResult = await testDevice.trigger.set({ triggerx: testTrigger });
|
|
94
94
|
|
|
95
95
|
if (!createResult) {
|
|
96
96
|
results.push({
|
|
@@ -114,7 +114,7 @@ async function runTests(context) {
|
|
|
114
114
|
} else {
|
|
115
115
|
// Wait a bit and query by alias to find it
|
|
116
116
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
117
|
-
const triggers = await testDevice.
|
|
117
|
+
const triggers = await testDevice.trigger.get({ channel: 0 });
|
|
118
118
|
const foundTrigger = triggers?.triggerx?.find(t => t.alias === 'Test Trigger - CLI Test');
|
|
119
119
|
if (foundTrigger && foundTrigger.id) {
|
|
120
120
|
createdTriggerId = foundTrigger.id;
|
|
@@ -133,7 +133,7 @@ async function runTests(context) {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// Verify trigger exists by querying it
|
|
136
|
-
const triggerInfo = await testDevice.
|
|
136
|
+
const triggerInfo = await testDevice.trigger.get({ channel: 0 });
|
|
137
137
|
const triggerExists = triggerInfo?.triggerx?.some(t => t.id === createdTriggerId);
|
|
138
138
|
|
|
139
139
|
if (!triggerExists) {
|
|
@@ -148,7 +148,8 @@ async function runTests(context) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// Verify cached trigger state
|
|
151
|
-
const
|
|
151
|
+
const triggerResponse = await testDevice.trigger.get({ channel: 0 });
|
|
152
|
+
const cachedTriggers = triggerResponse?.triggerx || [];
|
|
152
153
|
|
|
153
154
|
results.push({
|
|
154
155
|
name: 'should create trigger',
|
|
@@ -158,7 +159,7 @@ async function runTests(context) {
|
|
|
158
159
|
device: deviceName,
|
|
159
160
|
details: {
|
|
160
161
|
triggerId: createdTriggerId,
|
|
161
|
-
cachedTriggerCount: cachedTriggers
|
|
162
|
+
cachedTriggerCount: cachedTriggers.length
|
|
162
163
|
}
|
|
163
164
|
});
|
|
164
165
|
} catch (error) {
|
|
@@ -226,50 +227,6 @@ async function runTests(context) {
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
|
|
229
|
-
// Test 3: Handle trigger push notifications
|
|
230
|
-
try {
|
|
231
|
-
// Set up listener for trigger push notifications
|
|
232
|
-
let receivedNotification = false;
|
|
233
|
-
const notificationHandler = (notification) => {
|
|
234
|
-
if (notification.namespace === 'Appliance.Control.TriggerX') {
|
|
235
|
-
receivedNotification = true;
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
testDevice.on('pushNotification', notificationHandler);
|
|
240
|
-
|
|
241
|
-
// Get trigger info (may trigger a push notification)
|
|
242
|
-
await testDevice.getTriggerX(0);
|
|
243
|
-
|
|
244
|
-
// Wait a bit for potential push notifications
|
|
245
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
246
|
-
|
|
247
|
-
// Remove listener
|
|
248
|
-
testDevice.removeListener('pushNotification', notificationHandler);
|
|
249
|
-
|
|
250
|
-
// Note: We don't assert on receivedNotification since push notifications
|
|
251
|
-
// are device-initiated and may not occur during testing
|
|
252
|
-
results.push({
|
|
253
|
-
name: 'should handle trigger push notifications',
|
|
254
|
-
passed: true,
|
|
255
|
-
skipped: false,
|
|
256
|
-
error: null,
|
|
257
|
-
device: deviceName,
|
|
258
|
-
details: {
|
|
259
|
-
notificationReceived: receivedNotification,
|
|
260
|
-
note: 'Push notifications are device-initiated and may not occur during testing'
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
} catch (error) {
|
|
264
|
-
results.push({
|
|
265
|
-
name: 'should handle trigger push notifications',
|
|
266
|
-
passed: false,
|
|
267
|
-
skipped: false,
|
|
268
|
-
error: error.message,
|
|
269
|
-
device: deviceName
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
230
|
return results;
|
|
274
231
|
}
|
|
275
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meross-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Command-line interface for controlling Meross smart home devices",
|
|
5
5
|
"author": "Abe Haverkamp",
|
|
6
6
|
"homepage": "https://github.com/Doekse/merossiot#readme",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"chalk": "^4.1.2",
|
|
25
25
|
"commander": "^12.1.0",
|
|
26
26
|
"inquirer": "^8.2.6",
|
|
27
|
-
"meross-iot": "^0.
|
|
27
|
+
"meross-iot": "^0.6.0",
|
|
28
28
|
"ora": "^5.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|