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,497 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Presence Sensor Device Tests
|
|
5
|
+
* Tests presence detection, light readings, and configuration for presence sensor devices
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { findDevicesByAbility, waitForDeviceConnection, getDeviceName, OnlineStatus } = require('./test-helper');
|
|
9
|
+
|
|
10
|
+
const metadata = {
|
|
11
|
+
name: 'presence',
|
|
12
|
+
description: 'Tests presence detection, light readings, and configuration for presence sensor devices',
|
|
13
|
+
requiredAbilities: ['Appliance.Control.Sensor.LatestX'],
|
|
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 presenceDevices = devices || [];
|
|
24
|
+
if (presenceDevices.length === 0) {
|
|
25
|
+
presenceDevices = await findDevicesByAbility(manager, 'Appliance.Control.Sensor.LatestX', OnlineStatus.ONLINE);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Filter out hub devices - presence test is for standalone presence sensors
|
|
29
|
+
presenceDevices = presenceDevices.filter(device => {
|
|
30
|
+
// Skip hub devices - they're tested separately
|
|
31
|
+
return device.constructor.name !== 'MerossHubDevice' && typeof device.getSubdevice !== 'function';
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Wait for devices to be connected
|
|
35
|
+
for (const device of presenceDevices) {
|
|
36
|
+
await waitForDeviceConnection(device, timeout);
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Test 1: Check if devices were found
|
|
41
|
+
if (presenceDevices.length === 0) {
|
|
42
|
+
results.push({
|
|
43
|
+
name: 'should find devices with presence sensor capability',
|
|
44
|
+
passed: false,
|
|
45
|
+
skipped: true,
|
|
46
|
+
error: 'No standalone presence sensor devices found (hub devices are tested separately)',
|
|
47
|
+
device: null
|
|
48
|
+
});
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
results.push({
|
|
53
|
+
name: 'should find devices with presence sensor capability',
|
|
54
|
+
passed: true,
|
|
55
|
+
skipped: false,
|
|
56
|
+
error: null,
|
|
57
|
+
device: null
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const testDevice = presenceDevices[0];
|
|
61
|
+
const deviceName = getDeviceName(testDevice);
|
|
62
|
+
|
|
63
|
+
// Test 2: Get latest sensor readings
|
|
64
|
+
try {
|
|
65
|
+
if (typeof testDevice.getLatestSensorReadings === 'function') {
|
|
66
|
+
const readings = await testDevice.getLatestSensorReadings(['presence', 'light'], timeout);
|
|
67
|
+
|
|
68
|
+
if (!readings || !readings.latest) {
|
|
69
|
+
results.push({
|
|
70
|
+
name: 'should get latest sensor readings',
|
|
71
|
+
passed: false,
|
|
72
|
+
skipped: false,
|
|
73
|
+
error: 'getLatestSensorReadings returned invalid response',
|
|
74
|
+
device: deviceName
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
results.push({
|
|
78
|
+
name: 'should get latest sensor readings',
|
|
79
|
+
passed: true,
|
|
80
|
+
skipped: false,
|
|
81
|
+
error: null,
|
|
82
|
+
device: deviceName,
|
|
83
|
+
details: {
|
|
84
|
+
hasLatest: !!readings.latest,
|
|
85
|
+
latestLength: readings.latest?.length || 0
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
results.push({
|
|
91
|
+
name: 'should get latest sensor readings',
|
|
92
|
+
passed: false,
|
|
93
|
+
skipped: true,
|
|
94
|
+
error: 'Device does not support getLatestSensorReadings',
|
|
95
|
+
device: deviceName
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
results.push({
|
|
100
|
+
name: 'should get latest sensor readings',
|
|
101
|
+
passed: false,
|
|
102
|
+
skipped: false,
|
|
103
|
+
error: error.message,
|
|
104
|
+
device: deviceName
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Wait a bit for state to update after reading
|
|
109
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
110
|
+
|
|
111
|
+
// Test 3: Get presence data
|
|
112
|
+
try {
|
|
113
|
+
if (typeof testDevice.getPresence === 'function') {
|
|
114
|
+
const presence = testDevice.getPresence();
|
|
115
|
+
|
|
116
|
+
// Presence can be null if no data yet, which is acceptable
|
|
117
|
+
if (presence === null) {
|
|
118
|
+
results.push({
|
|
119
|
+
name: 'should get presence data',
|
|
120
|
+
passed: true,
|
|
121
|
+
skipped: false,
|
|
122
|
+
error: null,
|
|
123
|
+
device: deviceName,
|
|
124
|
+
details: { note: 'No presence data available yet (device may need time to detect)' }
|
|
125
|
+
});
|
|
126
|
+
} else if (typeof presence === 'object') {
|
|
127
|
+
// Validate presence data structure
|
|
128
|
+
const isValid = (
|
|
129
|
+
typeof presence.isPresent === 'boolean' &&
|
|
130
|
+
typeof presence.state === 'string' &&
|
|
131
|
+
(presence.state === 'presence' || presence.state === 'absence')
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (!isValid) {
|
|
135
|
+
results.push({
|
|
136
|
+
name: 'should get presence data',
|
|
137
|
+
passed: false,
|
|
138
|
+
skipped: false,
|
|
139
|
+
error: `Invalid presence data structure: ${JSON.stringify(presence)}`,
|
|
140
|
+
device: deviceName
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
results.push({
|
|
144
|
+
name: 'should get presence data',
|
|
145
|
+
passed: true,
|
|
146
|
+
skipped: false,
|
|
147
|
+
error: null,
|
|
148
|
+
device: deviceName,
|
|
149
|
+
details: {
|
|
150
|
+
state: presence.state,
|
|
151
|
+
isPresent: presence.isPresent,
|
|
152
|
+
hasDistance: presence.distance !== null && presence.distance !== undefined,
|
|
153
|
+
hasTimestamp: presence.timestamp !== null && presence.timestamp !== undefined
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
results.push({
|
|
159
|
+
name: 'should get presence data',
|
|
160
|
+
passed: false,
|
|
161
|
+
skipped: false,
|
|
162
|
+
error: `getPresence returned unexpected type: ${typeof presence}`,
|
|
163
|
+
device: deviceName
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
results.push({
|
|
168
|
+
name: 'should get presence data',
|
|
169
|
+
passed: false,
|
|
170
|
+
skipped: true,
|
|
171
|
+
error: 'Device does not support getPresence',
|
|
172
|
+
device: deviceName
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
results.push({
|
|
177
|
+
name: 'should get presence data',
|
|
178
|
+
passed: false,
|
|
179
|
+
skipped: false,
|
|
180
|
+
error: error.message,
|
|
181
|
+
device: deviceName
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Test 4: Check if present
|
|
186
|
+
try {
|
|
187
|
+
if (typeof testDevice.isPresent === 'function') {
|
|
188
|
+
const isPresent = testDevice.isPresent();
|
|
189
|
+
|
|
190
|
+
// isPresent can return null if no data, which is acceptable
|
|
191
|
+
if (isPresent === null) {
|
|
192
|
+
results.push({
|
|
193
|
+
name: 'should check if presence is detected',
|
|
194
|
+
passed: true,
|
|
195
|
+
skipped: false,
|
|
196
|
+
error: null,
|
|
197
|
+
device: deviceName,
|
|
198
|
+
details: { note: 'No presence data available yet' }
|
|
199
|
+
});
|
|
200
|
+
} else if (typeof isPresent === 'boolean') {
|
|
201
|
+
results.push({
|
|
202
|
+
name: 'should check if presence is detected',
|
|
203
|
+
passed: true,
|
|
204
|
+
skipped: false,
|
|
205
|
+
error: null,
|
|
206
|
+
device: deviceName,
|
|
207
|
+
details: { isPresent: isPresent }
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
results.push({
|
|
211
|
+
name: 'should check if presence is detected',
|
|
212
|
+
passed: false,
|
|
213
|
+
skipped: false,
|
|
214
|
+
error: `isPresent returned unexpected type: ${typeof isPresent}`,
|
|
215
|
+
device: deviceName
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
results.push({
|
|
220
|
+
name: 'should check if presence is detected',
|
|
221
|
+
passed: false,
|
|
222
|
+
skipped: true,
|
|
223
|
+
error: 'Device does not support isPresent',
|
|
224
|
+
device: deviceName
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
results.push({
|
|
229
|
+
name: 'should check if presence is detected',
|
|
230
|
+
passed: false,
|
|
231
|
+
skipped: false,
|
|
232
|
+
error: error.message,
|
|
233
|
+
device: deviceName
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Test 5: Get light reading
|
|
238
|
+
try {
|
|
239
|
+
if (typeof testDevice.getLight === 'function') {
|
|
240
|
+
const light = testDevice.getLight();
|
|
241
|
+
|
|
242
|
+
// Light can be null if no data yet, which is acceptable
|
|
243
|
+
if (light === null) {
|
|
244
|
+
results.push({
|
|
245
|
+
name: 'should get light reading',
|
|
246
|
+
passed: true,
|
|
247
|
+
skipped: false,
|
|
248
|
+
error: null,
|
|
249
|
+
device: deviceName,
|
|
250
|
+
details: { note: 'No light data available yet' }
|
|
251
|
+
});
|
|
252
|
+
} else if (typeof light === 'object' && light.value !== undefined) {
|
|
253
|
+
results.push({
|
|
254
|
+
name: 'should get light reading',
|
|
255
|
+
passed: true,
|
|
256
|
+
skipped: false,
|
|
257
|
+
error: null,
|
|
258
|
+
device: deviceName,
|
|
259
|
+
details: {
|
|
260
|
+
value: light.value,
|
|
261
|
+
hasTimestamp: light.timestamp !== null && light.timestamp !== undefined
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
results.push({
|
|
266
|
+
name: 'should get light reading',
|
|
267
|
+
passed: false,
|
|
268
|
+
skipped: false,
|
|
269
|
+
error: `getLight returned invalid data: ${JSON.stringify(light)}`,
|
|
270
|
+
device: deviceName
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
results.push({
|
|
275
|
+
name: 'should get light reading',
|
|
276
|
+
passed: false,
|
|
277
|
+
skipped: true,
|
|
278
|
+
error: 'Device does not support getLight',
|
|
279
|
+
device: deviceName
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
results.push({
|
|
284
|
+
name: 'should get light reading',
|
|
285
|
+
passed: false,
|
|
286
|
+
skipped: false,
|
|
287
|
+
error: error.message,
|
|
288
|
+
device: deviceName
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Test 6: Get all sensor readings
|
|
293
|
+
try {
|
|
294
|
+
if (typeof testDevice.getAllSensorReadings === 'function') {
|
|
295
|
+
const allReadings = testDevice.getAllSensorReadings();
|
|
296
|
+
|
|
297
|
+
if (!allReadings || typeof allReadings !== 'object') {
|
|
298
|
+
results.push({
|
|
299
|
+
name: 'should get all sensor readings',
|
|
300
|
+
passed: false,
|
|
301
|
+
skipped: false,
|
|
302
|
+
error: 'getAllSensorReadings returned invalid response',
|
|
303
|
+
device: deviceName
|
|
304
|
+
});
|
|
305
|
+
} else {
|
|
306
|
+
results.push({
|
|
307
|
+
name: 'should get all sensor readings',
|
|
308
|
+
passed: true,
|
|
309
|
+
skipped: false,
|
|
310
|
+
error: null,
|
|
311
|
+
device: deviceName,
|
|
312
|
+
details: {
|
|
313
|
+
hasPresence: allReadings.presence !== null && allReadings.presence !== undefined,
|
|
314
|
+
hasLight: allReadings.light !== null && allReadings.light !== undefined
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
results.push({
|
|
320
|
+
name: 'should get all sensor readings',
|
|
321
|
+
passed: false,
|
|
322
|
+
skipped: true,
|
|
323
|
+
error: 'Device does not support getAllSensorReadings',
|
|
324
|
+
device: deviceName
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
results.push({
|
|
329
|
+
name: 'should get all sensor readings',
|
|
330
|
+
passed: false,
|
|
331
|
+
skipped: false,
|
|
332
|
+
error: error.message,
|
|
333
|
+
device: deviceName
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Test 7: Get presence configuration
|
|
338
|
+
try {
|
|
339
|
+
if (typeof testDevice.getPresenceConfig === 'function') {
|
|
340
|
+
const config = await testDevice.getPresenceConfig(0, timeout);
|
|
341
|
+
|
|
342
|
+
if (!config) {
|
|
343
|
+
results.push({
|
|
344
|
+
name: 'should get presence configuration',
|
|
345
|
+
passed: false,
|
|
346
|
+
skipped: false,
|
|
347
|
+
error: 'getPresenceConfig returned null or undefined',
|
|
348
|
+
device: deviceName
|
|
349
|
+
});
|
|
350
|
+
} else {
|
|
351
|
+
results.push({
|
|
352
|
+
name: 'should get presence configuration',
|
|
353
|
+
passed: true,
|
|
354
|
+
skipped: false,
|
|
355
|
+
error: null,
|
|
356
|
+
device: deviceName,
|
|
357
|
+
details: { hasConfig: !!config }
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
results.push({
|
|
362
|
+
name: 'should get presence configuration',
|
|
363
|
+
passed: false,
|
|
364
|
+
skipped: true,
|
|
365
|
+
error: 'Device does not support getPresenceConfig',
|
|
366
|
+
device: deviceName
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
// Some devices may not support this, so we'll mark as skipped if it's a not-supported error
|
|
371
|
+
const errorMsg = error.message || String(error);
|
|
372
|
+
if (errorMsg.includes('not supported') || errorMsg.includes('not found') || errorMsg.includes('timeout')) {
|
|
373
|
+
results.push({
|
|
374
|
+
name: 'should get presence configuration',
|
|
375
|
+
passed: false,
|
|
376
|
+
skipped: true,
|
|
377
|
+
error: `getPresenceConfig not supported or timed out: ${errorMsg}`,
|
|
378
|
+
device: deviceName
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
results.push({
|
|
382
|
+
name: 'should get presence configuration',
|
|
383
|
+
passed: false,
|
|
384
|
+
skipped: false,
|
|
385
|
+
error: error.message,
|
|
386
|
+
device: deviceName
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Test 8: Get presence study/calibration status
|
|
392
|
+
try {
|
|
393
|
+
if (typeof testDevice.getPresenceStudy === 'function') {
|
|
394
|
+
const study = await testDevice.getPresenceStudy(timeout);
|
|
395
|
+
|
|
396
|
+
if (!study) {
|
|
397
|
+
results.push({
|
|
398
|
+
name: 'should get presence study status',
|
|
399
|
+
passed: false,
|
|
400
|
+
skipped: false,
|
|
401
|
+
error: 'getPresenceStudy returned null or undefined',
|
|
402
|
+
device: deviceName
|
|
403
|
+
});
|
|
404
|
+
} else {
|
|
405
|
+
results.push({
|
|
406
|
+
name: 'should get presence study status',
|
|
407
|
+
passed: true,
|
|
408
|
+
skipped: false,
|
|
409
|
+
error: null,
|
|
410
|
+
device: deviceName,
|
|
411
|
+
details: { hasStudy: !!study }
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
results.push({
|
|
416
|
+
name: 'should get presence study status',
|
|
417
|
+
passed: false,
|
|
418
|
+
skipped: true,
|
|
419
|
+
error: 'Device does not support getPresenceStudy',
|
|
420
|
+
device: deviceName
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
} catch (error) {
|
|
424
|
+
// Some devices may not support this, so we'll mark as skipped if it's a not-supported error
|
|
425
|
+
const errorMsg = error.message || String(error);
|
|
426
|
+
if (errorMsg.includes('not supported') || errorMsg.includes('not found') || errorMsg.includes('timeout')) {
|
|
427
|
+
results.push({
|
|
428
|
+
name: 'should get presence study status',
|
|
429
|
+
passed: false,
|
|
430
|
+
skipped: true,
|
|
431
|
+
error: `getPresenceStudy not supported or timed out: ${errorMsg}`,
|
|
432
|
+
device: deviceName
|
|
433
|
+
});
|
|
434
|
+
} else {
|
|
435
|
+
results.push({
|
|
436
|
+
name: 'should get presence study status',
|
|
437
|
+
passed: false,
|
|
438
|
+
skipped: false,
|
|
439
|
+
error: error.message,
|
|
440
|
+
device: deviceName
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Test 9: Handle presence push notifications
|
|
446
|
+
try {
|
|
447
|
+
// Set up listener for presence push notifications
|
|
448
|
+
let receivedNotification = false;
|
|
449
|
+
const notificationHandler = (notification) => {
|
|
450
|
+
if (notification.namespace === 'Appliance.Control.Sensor.LatestX') {
|
|
451
|
+
receivedNotification = true;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
testDevice.on('pushNotification', notificationHandler);
|
|
456
|
+
|
|
457
|
+
// Request latest readings (may trigger a push notification)
|
|
458
|
+
if (typeof testDevice.getLatestSensorReadings === 'function') {
|
|
459
|
+
await testDevice.getLatestSensorReadings(['presence', 'light'], timeout);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Wait a bit for potential push notifications
|
|
463
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
464
|
+
|
|
465
|
+
// Remove listener
|
|
466
|
+
testDevice.removeListener('pushNotification', notificationHandler);
|
|
467
|
+
|
|
468
|
+
// Note: We don't assert on receivedNotification since push notifications
|
|
469
|
+
// are device-initiated and may not occur during testing
|
|
470
|
+
// This test just verifies the listener mechanism works
|
|
471
|
+
results.push({
|
|
472
|
+
name: 'should handle presence push notifications',
|
|
473
|
+
passed: true,
|
|
474
|
+
skipped: false,
|
|
475
|
+
error: null,
|
|
476
|
+
device: deviceName,
|
|
477
|
+
details: { notificationReceived: receivedNotification }
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
} catch (error) {
|
|
481
|
+
results.push({
|
|
482
|
+
name: 'should handle presence push notifications',
|
|
483
|
+
passed: false,
|
|
484
|
+
skipped: false,
|
|
485
|
+
error: error.message,
|
|
486
|
+
device: deviceName
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return results;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
module.exports = {
|
|
494
|
+
metadata,
|
|
495
|
+
runTests
|
|
496
|
+
};
|
|
497
|
+
|