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/control-registry.js
CHANGED
|
@@ -1,102 +1,67 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Control method registry for device control
|
|
5
|
-
*
|
|
4
|
+
* Control method registry for device control.
|
|
5
|
+
* Uses feature-based API structure: "feature.action" (e.g., "toggle.set", "light.set")
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Mapping from control method names to required ability namespaces.
|
|
10
|
-
*
|
|
10
|
+
* Method names are in format: "feature.action"
|
|
11
11
|
*/
|
|
12
12
|
const METHOD_TO_NAMESPACE_MAP = {
|
|
13
13
|
// Power Control
|
|
14
|
-
|
|
15
|
-
setToggleX: ['Appliance.Control.ToggleX'],
|
|
14
|
+
'toggle.set': ['Appliance.Control.ToggleX', 'Appliance.Control.Toggle'],
|
|
16
15
|
|
|
17
16
|
// Light Control
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
setDiffuserLight: ['Appliance.Control.Diffuser.Light'],
|
|
17
|
+
'light.set': ['Appliance.Control.Light'],
|
|
18
|
+
'diffuser.setLight': ['Appliance.Control.Diffuser.Light'],
|
|
21
19
|
|
|
22
20
|
// Climate Control
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
setSpray: ['Appliance.Control.Spray'],
|
|
26
|
-
setDiffuserSpray: ['Appliance.Control.Diffuser.Spray'],
|
|
21
|
+
'thermostat.set': ['Appliance.Control.Thermostat.Mode', 'Appliance.Control.Thermostat.ModeB'],
|
|
22
|
+
'spray.set': ['Appliance.Control.Spray'],
|
|
23
|
+
'diffuser.setSpray': ['Appliance.Control.Diffuser.Spray'],
|
|
27
24
|
|
|
28
25
|
// Cover Control
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
closeGarageDoor: ['Appliance.GarageDoor.State'],
|
|
32
|
-
setGarageDoorConfig: ['Appliance.GarageDoor.Config', 'Appliance.GarageDoor.MultipleConfig'],
|
|
33
|
-
setRollerShutterPosition: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
34
|
-
setRollerShutterUp: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
35
|
-
setRollerShutterDown: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
36
|
-
setRollerShutterStop: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
37
|
-
openRollerShutter: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
38
|
-
closeRollerShutter: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
39
|
-
stopRollerShutter: ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
40
|
-
setRollerShutterConfig: ['Appliance.RollerShutter.Config'],
|
|
41
|
-
|
|
42
|
-
// Configuration
|
|
43
|
-
setChildLock: ['Appliance.Control.PhysicalLock'],
|
|
44
|
-
setSystemLedMode: ['Appliance.System.LedMode'],
|
|
45
|
-
setScreenBrightness: ['Appliance.Control.Screen.Brightness'],
|
|
46
|
-
setTempUnit: ['Appliance.Control.TempUnit'],
|
|
47
|
-
setDNDMode: ['Appliance.System.DNDMode'],
|
|
48
|
-
setConfigOverTemp: ['Appliance.Config.OverTemp'],
|
|
49
|
-
setPresenceConfig: ['Appliance.Control.Presence.Config'],
|
|
50
|
-
setPresenceStudy: ['Appliance.Control.Presence.Study'],
|
|
26
|
+
'garage.set': ['Appliance.GarageDoor.State'],
|
|
27
|
+
'rollerShutter.setPosition': ['Appliance.RollerShutter.Position', 'Appliance.RollerShutter.State'],
|
|
51
28
|
|
|
52
29
|
// Timer and Trigger
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
'timer.set': ['Appliance.Control.TimerX'],
|
|
31
|
+
'timer.delete': ['Appliance.Control.TimerX'],
|
|
32
|
+
'trigger.set': ['Appliance.Control.TriggerX'],
|
|
33
|
+
'trigger.delete': ['Appliance.Control.TriggerX'],
|
|
34
|
+
|
|
35
|
+
// Configuration
|
|
36
|
+
'childLock.set': ['Appliance.Control.PhysicalLock'],
|
|
37
|
+
'system.setLedMode': ['Appliance.System.LedMode'],
|
|
38
|
+
'screen.setBrightness': ['Appliance.Control.Screen.Brightness'],
|
|
39
|
+
'tempUnit.set': ['Appliance.Control.TempUnit'],
|
|
40
|
+
'dnd.set': ['Appliance.System.DNDMode'],
|
|
41
|
+
'config.setOverTemp': ['Appliance.Config.OverTemp'],
|
|
42
|
+
'presence.setConfig': ['Appliance.Control.Presence.Config'],
|
|
43
|
+
'presence.setStudy': ['Appliance.Control.Presence.Study']
|
|
57
44
|
};
|
|
58
45
|
|
|
59
46
|
/**
|
|
60
47
|
* Control method metadata registry.
|
|
61
|
-
*
|
|
62
|
-
* - name: Human-readable name
|
|
63
|
-
* - category: Feature category for grouping
|
|
64
|
-
* - params: Parameter schema for input collection
|
|
65
|
-
* - description: Brief description
|
|
48
|
+
* Method names use feature-based format: "feature.action"
|
|
66
49
|
*/
|
|
67
50
|
const CONTROL_METHOD_REGISTRY = {
|
|
68
51
|
// Power Control
|
|
69
|
-
|
|
52
|
+
'toggle.set': {
|
|
70
53
|
name: 'Toggle (On/Off)',
|
|
71
54
|
category: 'Power Control',
|
|
72
|
-
description: 'Turn device on or off',
|
|
73
|
-
params: [
|
|
74
|
-
{
|
|
75
|
-
name: 'onoff',
|
|
76
|
-
type: 'boolean',
|
|
77
|
-
label: 'State',
|
|
78
|
-
choices: [
|
|
79
|
-
{ name: 'On', value: true },
|
|
80
|
-
{ name: 'Off', value: false }
|
|
81
|
-
],
|
|
82
|
-
required: true
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
},
|
|
86
|
-
setToggleX: {
|
|
87
|
-
name: 'Toggle Channel (On/Off)',
|
|
88
|
-
category: 'Power Control',
|
|
89
55
|
description: 'Turn device channel on or off',
|
|
90
56
|
params: [
|
|
91
57
|
{
|
|
92
58
|
name: 'channel',
|
|
93
59
|
type: 'number',
|
|
94
60
|
label: 'Channel',
|
|
95
|
-
required: true,
|
|
96
61
|
default: 0
|
|
97
62
|
},
|
|
98
63
|
{
|
|
99
|
-
name: '
|
|
64
|
+
name: 'on',
|
|
100
65
|
type: 'boolean',
|
|
101
66
|
label: 'State',
|
|
102
67
|
choices: [
|
|
@@ -109,30 +74,10 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
109
74
|
},
|
|
110
75
|
|
|
111
76
|
// Light Control
|
|
112
|
-
|
|
77
|
+
'light.set': {
|
|
113
78
|
name: 'Light Control',
|
|
114
79
|
category: 'Light Control',
|
|
115
|
-
description: '
|
|
116
|
-
params: [
|
|
117
|
-
{
|
|
118
|
-
name: 'light',
|
|
119
|
-
type: 'object',
|
|
120
|
-
label: 'Light Configuration',
|
|
121
|
-
required: true,
|
|
122
|
-
properties: [
|
|
123
|
-
{ name: 'channel', type: 'number', default: 0 },
|
|
124
|
-
{ name: 'onoff', type: 'number', default: 1 },
|
|
125
|
-
{ name: 'rgb', type: 'number' },
|
|
126
|
-
{ name: 'luminance', type: 'number', min: 0, max: 100 },
|
|
127
|
-
{ name: 'temperature', type: 'number', min: 0, max: 100 }
|
|
128
|
-
]
|
|
129
|
-
}
|
|
130
|
-
]
|
|
131
|
-
},
|
|
132
|
-
setLightColor: {
|
|
133
|
-
name: 'Light Color & Brightness',
|
|
134
|
-
category: 'Light Control',
|
|
135
|
-
description: 'Set light color, brightness, and temperature',
|
|
80
|
+
description: 'Set light color, brightness, temperature, and on/off state',
|
|
136
81
|
params: [
|
|
137
82
|
{
|
|
138
83
|
name: 'channel',
|
|
@@ -141,7 +86,7 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
141
86
|
default: 0
|
|
142
87
|
},
|
|
143
88
|
{
|
|
144
|
-
name: '
|
|
89
|
+
name: 'on',
|
|
145
90
|
type: 'boolean',
|
|
146
91
|
label: 'Turn On/Off',
|
|
147
92
|
required: false
|
|
@@ -176,31 +121,45 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
176
121
|
}
|
|
177
122
|
]
|
|
178
123
|
},
|
|
179
|
-
|
|
124
|
+
'diffuser.setLight': {
|
|
180
125
|
name: 'Diffuser Light',
|
|
181
126
|
category: 'Light Control',
|
|
182
127
|
description: 'Control diffuser light settings',
|
|
183
128
|
params: [
|
|
184
129
|
{
|
|
185
|
-
name: '
|
|
186
|
-
type: '
|
|
187
|
-
label: '
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
130
|
+
name: 'channel',
|
|
131
|
+
type: 'number',
|
|
132
|
+
label: 'Channel',
|
|
133
|
+
default: 0
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'on',
|
|
137
|
+
type: 'boolean',
|
|
138
|
+
label: 'State',
|
|
139
|
+
required: false
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'rgb',
|
|
143
|
+
type: 'rgb',
|
|
144
|
+
label: 'RGB Color (r,g,b)',
|
|
145
|
+
required: false
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'luminance',
|
|
149
|
+
type: 'number',
|
|
150
|
+
label: 'Brightness (0-100)',
|
|
151
|
+
min: 0,
|
|
152
|
+
max: 100,
|
|
153
|
+
required: false
|
|
195
154
|
}
|
|
196
155
|
]
|
|
197
156
|
},
|
|
198
157
|
|
|
199
158
|
// Climate Control
|
|
200
|
-
|
|
201
|
-
name: 'Thermostat
|
|
159
|
+
'thermostat.set': {
|
|
160
|
+
name: 'Thermostat Control',
|
|
202
161
|
category: 'Climate Control',
|
|
203
|
-
description: 'Set thermostat mode and
|
|
162
|
+
description: 'Set thermostat mode, temperature, and on/off state',
|
|
204
163
|
params: [
|
|
205
164
|
{
|
|
206
165
|
name: 'channel',
|
|
@@ -250,29 +209,17 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
250
209
|
type: 'number',
|
|
251
210
|
label: 'Manual Temperature (°C)',
|
|
252
211
|
required: false
|
|
253
|
-
}
|
|
254
|
-
]
|
|
255
|
-
},
|
|
256
|
-
setThermostatModeB: {
|
|
257
|
-
name: 'Thermostat Mode B',
|
|
258
|
-
category: 'Climate Control',
|
|
259
|
-
description: 'Set thermostat mode B (advanced)',
|
|
260
|
-
params: [
|
|
261
|
-
{
|
|
262
|
-
name: 'channel',
|
|
263
|
-
type: 'number',
|
|
264
|
-
label: 'Channel',
|
|
265
|
-
default: 0
|
|
266
212
|
},
|
|
267
213
|
{
|
|
268
|
-
name: '
|
|
269
|
-
type: '
|
|
270
|
-
label: '
|
|
271
|
-
required:
|
|
214
|
+
name: 'partialUpdate',
|
|
215
|
+
type: 'boolean',
|
|
216
|
+
label: 'Partial Update',
|
|
217
|
+
required: false,
|
|
218
|
+
default: false
|
|
272
219
|
}
|
|
273
220
|
]
|
|
274
221
|
},
|
|
275
|
-
|
|
222
|
+
'spray.set': {
|
|
276
223
|
name: 'Spray/Humidifier',
|
|
277
224
|
category: 'Climate Control',
|
|
278
225
|
description: 'Control spray/humidifier mode',
|
|
@@ -296,7 +243,7 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
296
243
|
}
|
|
297
244
|
]
|
|
298
245
|
},
|
|
299
|
-
|
|
246
|
+
'diffuser.setSpray': {
|
|
300
247
|
name: 'Diffuser Spray',
|
|
301
248
|
category: 'Climate Control',
|
|
302
249
|
description: 'Control diffuser spray mode',
|
|
@@ -322,7 +269,7 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
322
269
|
},
|
|
323
270
|
|
|
324
271
|
// Cover Control
|
|
325
|
-
|
|
272
|
+
'garage.set': {
|
|
326
273
|
name: 'Garage Door',
|
|
327
274
|
category: 'Cover Control',
|
|
328
275
|
description: 'Open or close garage door',
|
|
@@ -334,18 +281,14 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
334
281
|
default: 0
|
|
335
282
|
},
|
|
336
283
|
{
|
|
337
|
-
name: '
|
|
338
|
-
type: '
|
|
339
|
-
label: 'Action',
|
|
340
|
-
choices: [
|
|
341
|
-
{ name: 'Open', value: true },
|
|
342
|
-
{ name: 'Close', value: false }
|
|
343
|
-
],
|
|
284
|
+
name: 'onoff',
|
|
285
|
+
type: 'number',
|
|
286
|
+
label: 'Action (0=close, 1=open)',
|
|
344
287
|
required: true
|
|
345
288
|
}
|
|
346
289
|
]
|
|
347
290
|
},
|
|
348
|
-
|
|
291
|
+
'rollerShutter.setPosition': {
|
|
349
292
|
name: 'Roller Shutter Position',
|
|
350
293
|
category: 'Cover Control',
|
|
351
294
|
description: 'Set roller shutter position (0-100, -1 to stop)',
|
|
@@ -366,63 +309,56 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
366
309
|
}
|
|
367
310
|
]
|
|
368
311
|
},
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
312
|
+
|
|
313
|
+
// Timer and Trigger
|
|
314
|
+
'timer.set': {
|
|
315
|
+
name: 'Timer Control',
|
|
316
|
+
category: 'Automation',
|
|
317
|
+
description: 'Create or update a timer',
|
|
373
318
|
params: [
|
|
374
319
|
{
|
|
375
320
|
name: 'channel',
|
|
376
321
|
type: 'number',
|
|
377
322
|
label: 'Channel',
|
|
378
323
|
default: 0
|
|
379
|
-
}
|
|
380
|
-
]
|
|
381
|
-
},
|
|
382
|
-
setRollerShutterDown: {
|
|
383
|
-
name: 'Roller Shutter Close',
|
|
384
|
-
category: 'Cover Control',
|
|
385
|
-
description: 'Close roller shutter (position 0)',
|
|
386
|
-
params: [
|
|
324
|
+
},
|
|
387
325
|
{
|
|
388
|
-
name: '
|
|
326
|
+
name: 'id',
|
|
327
|
+
type: 'string',
|
|
328
|
+
label: 'Timer ID (for updates)',
|
|
329
|
+
required: false
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'onoff',
|
|
389
333
|
type: 'number',
|
|
390
|
-
label: '
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
]
|
|
394
|
-
},
|
|
395
|
-
setRollerShutterStop: {
|
|
396
|
-
name: 'Roller Shutter Stop',
|
|
397
|
-
category: 'Cover Control',
|
|
398
|
-
description: 'Stop roller shutter movement',
|
|
399
|
-
params: [
|
|
334
|
+
label: 'Action (0=off, 1=on)',
|
|
335
|
+
required: true
|
|
336
|
+
},
|
|
400
337
|
{
|
|
401
|
-
name: '
|
|
338
|
+
name: 'type',
|
|
402
339
|
type: 'number',
|
|
403
|
-
label: '
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
]
|
|
407
|
-
},
|
|
408
|
-
openRollerShutter: {
|
|
409
|
-
name: 'Roller Shutter Open',
|
|
410
|
-
category: 'Cover Control',
|
|
411
|
-
description: 'Open roller shutter (convenience method)',
|
|
412
|
-
params: [
|
|
340
|
+
label: 'Timer Type',
|
|
341
|
+
required: true
|
|
342
|
+
},
|
|
413
343
|
{
|
|
414
|
-
name: '
|
|
344
|
+
name: 'time',
|
|
415
345
|
type: 'number',
|
|
416
|
-
label: '
|
|
417
|
-
|
|
346
|
+
label: 'Time Value',
|
|
347
|
+
required: true
|
|
418
348
|
}
|
|
419
349
|
]
|
|
420
350
|
},
|
|
421
|
-
|
|
422
|
-
name: '
|
|
423
|
-
category: '
|
|
424
|
-
description: '
|
|
351
|
+
'timer.delete': {
|
|
352
|
+
name: 'Delete Timer',
|
|
353
|
+
category: 'Automation',
|
|
354
|
+
description: 'Delete a timer',
|
|
425
355
|
params: [
|
|
356
|
+
{
|
|
357
|
+
name: 'timerId',
|
|
358
|
+
type: 'string',
|
|
359
|
+
label: 'Timer ID',
|
|
360
|
+
required: true
|
|
361
|
+
},
|
|
426
362
|
{
|
|
427
363
|
name: 'channel',
|
|
428
364
|
type: 'number',
|
|
@@ -431,30 +367,34 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
431
367
|
}
|
|
432
368
|
]
|
|
433
369
|
},
|
|
434
|
-
|
|
435
|
-
name: '
|
|
436
|
-
category: '
|
|
437
|
-
description: '
|
|
370
|
+
'trigger.set': {
|
|
371
|
+
name: 'Trigger Control',
|
|
372
|
+
category: 'Automation',
|
|
373
|
+
description: 'Create or update a trigger',
|
|
438
374
|
params: [
|
|
439
375
|
{
|
|
440
376
|
name: 'channel',
|
|
441
377
|
type: 'number',
|
|
442
378
|
label: 'Channel',
|
|
443
379
|
default: 0
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: 'triggerx',
|
|
383
|
+
type: 'object',
|
|
384
|
+
label: 'Trigger Configuration',
|
|
385
|
+
required: true
|
|
444
386
|
}
|
|
445
387
|
]
|
|
446
388
|
},
|
|
447
|
-
|
|
448
|
-
name: '
|
|
449
|
-
category: '
|
|
450
|
-
description: '
|
|
389
|
+
'trigger.delete': {
|
|
390
|
+
name: 'Delete Trigger',
|
|
391
|
+
category: 'Automation',
|
|
392
|
+
description: 'Delete a trigger',
|
|
451
393
|
params: [
|
|
452
394
|
{
|
|
453
|
-
name: '
|
|
454
|
-
type: '
|
|
455
|
-
label: '
|
|
456
|
-
min: 0,
|
|
457
|
-
max: 100,
|
|
395
|
+
name: 'triggerId',
|
|
396
|
+
type: 'string',
|
|
397
|
+
label: 'Trigger ID',
|
|
458
398
|
required: true
|
|
459
399
|
},
|
|
460
400
|
{
|
|
@@ -465,77 +405,28 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
465
405
|
}
|
|
466
406
|
]
|
|
467
407
|
},
|
|
468
|
-
|
|
469
|
-
|
|
408
|
+
|
|
409
|
+
// Configuration
|
|
410
|
+
'childLock.set': {
|
|
411
|
+
name: 'Child Lock',
|
|
470
412
|
category: 'Configuration',
|
|
471
|
-
description: '
|
|
472
|
-
params: [
|
|
473
|
-
{
|
|
474
|
-
name: 'config',
|
|
475
|
-
type: 'object',
|
|
476
|
-
label: 'Configuration Object',
|
|
477
|
-
required: true
|
|
478
|
-
}
|
|
479
|
-
]
|
|
480
|
-
},
|
|
481
|
-
openGarageDoor: {
|
|
482
|
-
name: 'Garage Door Open',
|
|
483
|
-
category: 'Cover Control',
|
|
484
|
-
description: 'Open garage door (convenience method)',
|
|
413
|
+
description: 'Enable or disable child lock',
|
|
485
414
|
params: [
|
|
486
415
|
{
|
|
487
416
|
name: 'channel',
|
|
488
417
|
type: 'number',
|
|
489
418
|
label: 'Channel',
|
|
490
419
|
default: 0
|
|
491
|
-
}
|
|
492
|
-
]
|
|
493
|
-
},
|
|
494
|
-
closeGarageDoor: {
|
|
495
|
-
name: 'Garage Door Close',
|
|
496
|
-
category: 'Cover Control',
|
|
497
|
-
description: 'Close garage door (convenience method)',
|
|
498
|
-
params: [
|
|
420
|
+
},
|
|
499
421
|
{
|
|
500
|
-
name: '
|
|
422
|
+
name: 'lock',
|
|
501
423
|
type: 'number',
|
|
502
|
-
label: '
|
|
503
|
-
default: 0
|
|
504
|
-
}
|
|
505
|
-
]
|
|
506
|
-
},
|
|
507
|
-
setGarageDoorConfig: {
|
|
508
|
-
name: 'Garage Door Configuration',
|
|
509
|
-
category: 'Configuration',
|
|
510
|
-
description: 'Configure garage door settings',
|
|
511
|
-
params: [
|
|
512
|
-
{
|
|
513
|
-
name: 'configData',
|
|
514
|
-
type: 'object',
|
|
515
|
-
label: 'Configuration Object',
|
|
424
|
+
label: 'Lock (0=unlock, 1=lock)',
|
|
516
425
|
required: true
|
|
517
426
|
}
|
|
518
427
|
]
|
|
519
428
|
},
|
|
520
|
-
|
|
521
|
-
// Configuration
|
|
522
|
-
setChildLock: {
|
|
523
|
-
name: 'Child Lock',
|
|
524
|
-
category: 'Configuration',
|
|
525
|
-
description: 'Enable or disable child lock',
|
|
526
|
-
params: [
|
|
527
|
-
{
|
|
528
|
-
name: 'lockData',
|
|
529
|
-
type: 'object',
|
|
530
|
-
label: 'Lock Configuration',
|
|
531
|
-
required: true,
|
|
532
|
-
properties: [
|
|
533
|
-
{ name: 'lock', type: 'number', label: 'Lock (0=unlock, 1=lock)', required: true }
|
|
534
|
-
]
|
|
535
|
-
}
|
|
536
|
-
]
|
|
537
|
-
},
|
|
538
|
-
setSystemLedMode: {
|
|
429
|
+
'system.setLedMode': {
|
|
539
430
|
name: 'LED Indicator Mode',
|
|
540
431
|
category: 'Configuration',
|
|
541
432
|
description: 'Control LED indicator mode',
|
|
@@ -548,56 +439,60 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
548
439
|
}
|
|
549
440
|
]
|
|
550
441
|
},
|
|
551
|
-
|
|
442
|
+
'screen.setBrightness': {
|
|
552
443
|
name: 'Screen Brightness',
|
|
553
444
|
category: 'Configuration',
|
|
554
445
|
description: 'Set device screen brightness',
|
|
555
446
|
params: [
|
|
556
447
|
{
|
|
557
|
-
name: '
|
|
558
|
-
type: '
|
|
559
|
-
label: '
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
448
|
+
name: 'channel',
|
|
449
|
+
type: 'number',
|
|
450
|
+
label: 'Channel',
|
|
451
|
+
default: 0
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'brightness',
|
|
455
|
+
type: 'number',
|
|
456
|
+
label: 'Brightness (0-100)',
|
|
457
|
+
min: 0,
|
|
458
|
+
max: 100,
|
|
459
|
+
required: true
|
|
564
460
|
}
|
|
565
461
|
]
|
|
566
462
|
},
|
|
567
|
-
|
|
463
|
+
'tempUnit.set': {
|
|
568
464
|
name: 'Temperature Unit',
|
|
569
465
|
category: 'Configuration',
|
|
570
466
|
description: 'Set temperature unit (Celsius/Fahrenheit)',
|
|
571
467
|
params: [
|
|
572
468
|
{
|
|
573
|
-
name: '
|
|
574
|
-
type: '
|
|
575
|
-
label: '
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
469
|
+
name: 'channel',
|
|
470
|
+
type: 'number',
|
|
471
|
+
label: 'Channel',
|
|
472
|
+
default: 0
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: 'unit',
|
|
476
|
+
type: 'number',
|
|
477
|
+
label: 'Unit (0=Celsius, 1=Fahrenheit)',
|
|
478
|
+
required: true
|
|
580
479
|
}
|
|
581
480
|
]
|
|
582
481
|
},
|
|
583
|
-
|
|
482
|
+
'dnd.set': {
|
|
584
483
|
name: 'Do Not Disturb Mode',
|
|
585
484
|
category: 'Configuration',
|
|
586
|
-
description: 'Enable or disable Do Not Disturb mode
|
|
485
|
+
description: 'Enable or disable Do Not Disturb mode',
|
|
587
486
|
params: [
|
|
588
487
|
{
|
|
589
488
|
name: 'mode',
|
|
590
|
-
type: '
|
|
591
|
-
label: 'DND Mode',
|
|
592
|
-
choices: [
|
|
593
|
-
{ name: 'Enable DND', value: true },
|
|
594
|
-
{ name: 'Disable DND', value: false }
|
|
595
|
-
],
|
|
489
|
+
type: 'number',
|
|
490
|
+
label: 'DND Mode (0=disabled, 1=enabled)',
|
|
596
491
|
required: true
|
|
597
492
|
}
|
|
598
493
|
]
|
|
599
494
|
},
|
|
600
|
-
|
|
495
|
+
'config.setOverTemp': {
|
|
601
496
|
name: 'Over-Temperature Protection',
|
|
602
497
|
category: 'Configuration',
|
|
603
498
|
description: 'Enable or disable over-temperature protection',
|
|
@@ -614,122 +509,56 @@ const CONTROL_METHOD_REGISTRY = {
|
|
|
614
509
|
}
|
|
615
510
|
]
|
|
616
511
|
},
|
|
617
|
-
|
|
512
|
+
'presence.setConfig': {
|
|
618
513
|
name: 'Presence Sensor Configuration',
|
|
619
514
|
category: 'Configuration',
|
|
620
|
-
description: 'Configure presence sensor settings
|
|
515
|
+
description: 'Configure presence sensor settings',
|
|
621
516
|
params: [
|
|
517
|
+
{
|
|
518
|
+
name: 'channel',
|
|
519
|
+
type: 'number',
|
|
520
|
+
label: 'Channel',
|
|
521
|
+
default: 0
|
|
522
|
+
},
|
|
622
523
|
{
|
|
623
524
|
name: 'configData',
|
|
624
525
|
type: 'object',
|
|
625
526
|
label: 'Configuration Object',
|
|
626
|
-
required: true
|
|
627
|
-
properties: [
|
|
628
|
-
{ name: 'channel', type: 'number', label: 'Channel', default: 0 },
|
|
629
|
-
{ name: 'mode', type: 'object', label: 'Mode Configuration' },
|
|
630
|
-
{ name: 'noBodyTime', type: 'object', label: 'No Body Time Configuration' },
|
|
631
|
-
{ name: 'distance', type: 'object', label: 'Distance Configuration' },
|
|
632
|
-
{ name: 'sensitivity', type: 'object', label: 'Sensitivity Configuration' },
|
|
633
|
-
{ name: 'mthx', type: 'object', label: 'Motion Threshold Configuration' }
|
|
634
|
-
]
|
|
527
|
+
required: true
|
|
635
528
|
}
|
|
636
529
|
]
|
|
637
530
|
},
|
|
638
|
-
|
|
531
|
+
'presence.setStudy': {
|
|
639
532
|
name: 'Presence Sensor Study/Calibration',
|
|
640
533
|
category: 'Configuration',
|
|
641
534
|
description: 'Start or stop presence sensor study/calibration mode',
|
|
642
535
|
params: [
|
|
643
|
-
{
|
|
644
|
-
name: 'studyData',
|
|
645
|
-
type: 'object',
|
|
646
|
-
label: 'Study Data Object',
|
|
647
|
-
required: true,
|
|
648
|
-
properties: [
|
|
649
|
-
{ name: 'channel', type: 'number', label: 'Channel', default: 0 },
|
|
650
|
-
{ name: 'value', type: 'number', label: 'Study Mode Value (typically 1-3)' },
|
|
651
|
-
{ name: 'status', type: 'number', label: 'Status (0=stop, 1=start)', required: true }
|
|
652
|
-
]
|
|
653
|
-
}
|
|
654
|
-
]
|
|
655
|
-
},
|
|
656
|
-
setTimerX: {
|
|
657
|
-
name: 'Timer Control',
|
|
658
|
-
category: 'Automation',
|
|
659
|
-
description: 'Create or update a timer',
|
|
660
|
-
params: [
|
|
661
|
-
{
|
|
662
|
-
name: 'timerx',
|
|
663
|
-
type: 'object',
|
|
664
|
-
label: 'Timer Configuration',
|
|
665
|
-
required: true,
|
|
666
|
-
properties: [
|
|
667
|
-
{ name: 'id', type: 'string', label: 'Timer ID (for updates)' },
|
|
668
|
-
{ name: 'channel', type: 'number', default: 0 },
|
|
669
|
-
{ name: 'onoff', type: 'number', label: 'Action (0=off, 1=on)', required: true },
|
|
670
|
-
{ name: 'type', type: 'number', label: 'Timer Type', required: true },
|
|
671
|
-
{ name: 'time', type: 'number', label: 'Time Value', required: true }
|
|
672
|
-
]
|
|
673
|
-
}
|
|
674
|
-
]
|
|
675
|
-
},
|
|
676
|
-
deleteTimerX: {
|
|
677
|
-
name: 'Delete Timer',
|
|
678
|
-
category: 'Automation',
|
|
679
|
-
description: 'Delete a timer',
|
|
680
|
-
params: [
|
|
681
|
-
{
|
|
682
|
-
name: 'timerId',
|
|
683
|
-
type: 'string',
|
|
684
|
-
label: 'Timer ID',
|
|
685
|
-
required: true
|
|
686
|
-
},
|
|
687
536
|
{
|
|
688
537
|
name: 'channel',
|
|
689
538
|
type: 'number',
|
|
690
539
|
label: 'Channel',
|
|
691
540
|
default: 0
|
|
692
|
-
}
|
|
693
|
-
]
|
|
694
|
-
},
|
|
695
|
-
setTriggerX: {
|
|
696
|
-
name: 'Trigger Control',
|
|
697
|
-
category: 'Automation',
|
|
698
|
-
description: 'Create or update a trigger',
|
|
699
|
-
params: [
|
|
700
|
-
{
|
|
701
|
-
name: 'triggerx',
|
|
702
|
-
type: 'object',
|
|
703
|
-
label: 'Trigger Configuration',
|
|
704
|
-
required: true
|
|
705
|
-
}
|
|
706
|
-
]
|
|
707
|
-
},
|
|
708
|
-
deleteTriggerX: {
|
|
709
|
-
name: 'Delete Trigger',
|
|
710
|
-
category: 'Automation',
|
|
711
|
-
description: 'Delete a trigger',
|
|
712
|
-
params: [
|
|
541
|
+
},
|
|
713
542
|
{
|
|
714
|
-
name: '
|
|
715
|
-
type: '
|
|
716
|
-
label: '
|
|
717
|
-
required:
|
|
543
|
+
name: 'value',
|
|
544
|
+
type: 'number',
|
|
545
|
+
label: 'Study Mode Value',
|
|
546
|
+
required: false
|
|
718
547
|
},
|
|
719
548
|
{
|
|
720
|
-
name: '
|
|
549
|
+
name: 'status',
|
|
721
550
|
type: 'number',
|
|
722
|
-
label: '
|
|
723
|
-
required:
|
|
724
|
-
default: 0
|
|
551
|
+
label: 'Status (0=stop, 1=start)',
|
|
552
|
+
required: true
|
|
725
553
|
}
|
|
726
554
|
]
|
|
727
555
|
}
|
|
728
556
|
};
|
|
729
557
|
|
|
730
558
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
559
|
+
* Gets metadata for a control method from the registry.
|
|
560
|
+
*
|
|
561
|
+
* @param {string} methodName - Name of the control method (format: "feature.action")
|
|
733
562
|
* @returns {Object|null} Method metadata or null if not found
|
|
734
563
|
*/
|
|
735
564
|
function getMethodMetadata(methodName) {
|
|
@@ -737,7 +566,8 @@ function getMethodMetadata(methodName) {
|
|
|
737
566
|
}
|
|
738
567
|
|
|
739
568
|
/**
|
|
740
|
-
*
|
|
569
|
+
* Gets all control methods grouped by category for organized display.
|
|
570
|
+
*
|
|
741
571
|
* @returns {Object} Methods grouped by category
|
|
742
572
|
*/
|
|
743
573
|
function getMethodsByCategory() {
|
|
@@ -756,7 +586,8 @@ function getMethodsByCategory() {
|
|
|
756
586
|
}
|
|
757
587
|
|
|
758
588
|
/**
|
|
759
|
-
*
|
|
589
|
+
* Checks if a device supports a specific ability namespace.
|
|
590
|
+
*
|
|
760
591
|
* @param {Object} device - Device instance
|
|
761
592
|
* @param {string} namespace - Namespace to check
|
|
762
593
|
* @returns {boolean} True if device supports the namespace
|
|
@@ -769,90 +600,65 @@ function deviceSupportsNamespace(device, namespace) {
|
|
|
769
600
|
}
|
|
770
601
|
|
|
771
602
|
/**
|
|
772
|
-
*
|
|
603
|
+
* Checks if a device supports any of the required namespaces for a method.
|
|
604
|
+
*
|
|
605
|
+
* Some methods accept multiple namespace alternatives (e.g., ToggleX or Toggle),
|
|
606
|
+
* so any match satisfies the requirement.
|
|
607
|
+
*
|
|
773
608
|
* @param {Object} device - Device instance
|
|
774
609
|
* @param {Array<string>} namespaces - Array of namespace strings (any match is sufficient)
|
|
775
610
|
* @returns {boolean} True if device supports at least one namespace
|
|
776
611
|
*/
|
|
777
612
|
function deviceSupportsAnyNamespace(device, namespaces) {
|
|
778
613
|
if (!namespaces || namespaces.length === 0) {
|
|
779
|
-
return true;
|
|
614
|
+
return true;
|
|
780
615
|
}
|
|
781
616
|
return namespaces.some(namespace => deviceSupportsNamespace(device, namespace));
|
|
782
617
|
}
|
|
783
618
|
|
|
784
619
|
/**
|
|
785
|
-
*
|
|
786
|
-
*
|
|
620
|
+
* Detects available control methods on a device based on device abilities.
|
|
621
|
+
*
|
|
622
|
+
* Validates both namespace requirements and feature availability to determine
|
|
623
|
+
* which methods can be used with a specific device. Uses feature-based API structure.
|
|
624
|
+
*
|
|
787
625
|
* @param {Object} device - Device instance
|
|
788
626
|
* @returns {Array} Array of available control methods with metadata
|
|
789
627
|
*/
|
|
790
628
|
function detectControlMethods(device) {
|
|
791
629
|
const availableMethods = [];
|
|
792
630
|
|
|
793
|
-
// Only show methods that are in our registry and device supports
|
|
794
|
-
// Iterate through registry instead of device methods to ensure we only show known methods
|
|
795
631
|
for (const [methodName, metadata] of Object.entries(CONTROL_METHOD_REGISTRY)) {
|
|
796
|
-
// Check if method exists on device (check both exact name and common variations)
|
|
797
|
-
const deviceMethod = device[methodName];
|
|
798
|
-
if (typeof deviceMethod !== 'function') {
|
|
799
|
-
// Try alternative naming (e.g., setDNDMode might be on device)
|
|
800
|
-
continue; // Method doesn't exist on device, skip
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Check if this method requires specific namespaces
|
|
804
632
|
const requiredNamespaces = METHOD_TO_NAMESPACE_MAP[methodName];
|
|
805
633
|
|
|
806
|
-
// If method has namespace requirements, check if device supports them
|
|
807
634
|
if (requiredNamespaces && requiredNamespaces.length > 0) {
|
|
808
635
|
if (!deviceSupportsAnyNamespace(device, requiredNamespaces)) {
|
|
809
|
-
// Device doesn't support required namespaces, skip this method
|
|
810
636
|
continue;
|
|
811
637
|
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const parts = methodName.split('.');
|
|
641
|
+
if (parts.length !== 2) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const [featureName, action] = parts;
|
|
646
|
+
|
|
647
|
+
const feature = device[featureName];
|
|
648
|
+
if (!feature) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (typeof feature[action] !== 'function') {
|
|
815
653
|
continue;
|
|
816
654
|
}
|
|
817
655
|
|
|
818
|
-
// Method exists, has namespace requirements, and device supports them - add it
|
|
819
656
|
availableMethods.push({
|
|
820
657
|
methodName,
|
|
821
658
|
...metadata
|
|
822
659
|
});
|
|
823
660
|
}
|
|
824
661
|
|
|
825
|
-
// Also check for any control/set methods on device that might not be in registry
|
|
826
|
-
// but have namespace requirements (for future extensibility)
|
|
827
|
-
for (const prop in device) {
|
|
828
|
-
if ((prop.startsWith('control') || prop.startsWith('set')) && typeof device[prop] === 'function') {
|
|
829
|
-
// Skip if already in our list
|
|
830
|
-
if (availableMethods.some(m => m.methodName === prop)) {
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// Check if this method has namespace requirements
|
|
835
|
-
const requiredNamespaces = METHOD_TO_NAMESPACE_MAP[prop];
|
|
836
|
-
|
|
837
|
-
// Only include if it has namespace requirements AND device supports them
|
|
838
|
-
if (requiredNamespaces && requiredNamespaces.length > 0) {
|
|
839
|
-
if (deviceSupportsAnyNamespace(device, requiredNamespaces)) {
|
|
840
|
-
// Unknown method but device supports it - add with basic info
|
|
841
|
-
const displayName = prop.replace(/^(control|set)/, '').replace(/([A-Z])/g, ' $1').trim();
|
|
842
|
-
availableMethods.push({
|
|
843
|
-
methodName: prop,
|
|
844
|
-
name: displayName,
|
|
845
|
-
category: 'Other',
|
|
846
|
-
description: 'Control method',
|
|
847
|
-
params: []
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
// If no namespace requirement, skip it (don't show unknown methods without namespace checks)
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// Sort by category, then by name
|
|
856
662
|
availableMethods.sort((a, b) => {
|
|
857
663
|
if (a.category !== b.category) {
|
|
858
664
|
return a.category.localeCompare(b.category);
|
|
@@ -872,4 +678,3 @@ module.exports = {
|
|
|
872
678
|
deviceSupportsNamespace,
|
|
873
679
|
deviceSupportsAnyNamespace
|
|
874
680
|
};
|
|
875
|
-
|