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,313 @@
1
+ 'use strict';
2
+
3
+ const { OnlineStatus } = require('meross-iot');
4
+
5
+ /**
6
+ * Test Helper Utilities
7
+ *
8
+ * Pure utility functions for device discovery and connection management.
9
+ * All functions accept explicit parameters - no globals or environment variables.
10
+ */
11
+
12
+ /**
13
+ * Waits for devices to be discovered
14
+ * @param {Object} manager - MerossManager instance
15
+ * @param {number} timeout - Timeout in milliseconds (default: 5000)
16
+ * @returns {Promise<Array>} Array of device objects with structure: { deviceId, deviceDef, device }
17
+ */
18
+ function waitForDevices(manager, timeout = 5000) {
19
+ return new Promise((resolve) => {
20
+ // If devices already exist, return them immediately
21
+ const existingDevices = manager.getAllDevices();
22
+ if (existingDevices && existingDevices.length > 0) {
23
+ const devices = [];
24
+ const deviceIds = new Set();
25
+
26
+ for (const device of existingDevices) {
27
+ // Use unique identifier for subdevices (internalId) or UUID for base devices
28
+ const deviceId = device.subdeviceId
29
+ ? `${device.dev?.uuid || device.uuid}:${device.subdeviceId}`
30
+ : (device.dev?.uuid || device.uuid);
31
+
32
+ if (!deviceIds.has(deviceId)) {
33
+ deviceIds.add(deviceId);
34
+ devices.push({
35
+ deviceId: deviceId,
36
+ deviceDef: device.dev || { uuid: device.uuid },
37
+ device
38
+ });
39
+ }
40
+ }
41
+ resolve(devices);
42
+ return;
43
+ }
44
+
45
+ // Wait for devices to be initialized
46
+ const devices = [];
47
+ const deviceIds = new Set();
48
+ let timeoutId = null;
49
+
50
+ const onDeviceInitialized = (deviceId, deviceDef, device) => {
51
+ if (!deviceIds.has(deviceId)) {
52
+ deviceIds.add(deviceId);
53
+ devices.push({ deviceId, deviceDef, device });
54
+ }
55
+ };
56
+
57
+ manager.on('deviceInitialized', onDeviceInitialized);
58
+
59
+ timeoutId = setTimeout(() => {
60
+ manager.removeListener('deviceInitialized', onDeviceInitialized);
61
+ resolve(devices);
62
+ }, timeout);
63
+
64
+ // Also check periodically if devices were added (in case event was missed)
65
+ const checkInterval = setInterval(() => {
66
+ const currentDevices = manager.getAllDevices();
67
+ if (currentDevices && currentDevices.length > 0) {
68
+ clearInterval(checkInterval);
69
+ if (timeoutId) {
70
+ clearTimeout(timeoutId);
71
+ }
72
+ manager.removeListener('deviceInitialized', onDeviceInitialized);
73
+
74
+ // Add any new devices
75
+ for (const device of currentDevices) {
76
+ const deviceId = device.subdeviceId
77
+ ? `${device.dev?.uuid || device.uuid}:${device.subdeviceId}`
78
+ : (device.dev?.uuid || device.uuid);
79
+
80
+ if (!deviceIds.has(deviceId)) {
81
+ deviceIds.add(deviceId);
82
+ devices.push({
83
+ deviceId: deviceId,
84
+ deviceDef: device.dev || { uuid: device.uuid },
85
+ device
86
+ });
87
+ }
88
+ }
89
+ resolve(devices);
90
+ }
91
+ }, 200);
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Gets device online status
97
+ * @param {Object} device - Device instance
98
+ * @returns {number|null} OnlineStatus value or null if not available
99
+ */
100
+ function getDeviceOnlineStatus(device) {
101
+ if (device.onlineStatus !== undefined) {
102
+ return device.onlineStatus;
103
+ }
104
+ if (device.dev && device.dev.onlineStatus !== undefined) {
105
+ return device.dev.onlineStatus;
106
+ }
107
+ return null;
108
+ }
109
+
110
+ /**
111
+ * Checks if device has a specific ability
112
+ * @param {Object} device - Device instance
113
+ * @param {string} namespace - Ability namespace
114
+ * @returns {boolean} True if device has the ability
115
+ */
116
+ function deviceHasAbility(device, namespace) {
117
+ return !!(device._abilities && device._abilities[namespace]);
118
+ }
119
+
120
+ /**
121
+ * Finds devices by ability namespace
122
+ * @param {Object} manager - MerossManager instance
123
+ * @param {string} namespace - Ability namespace (e.g., 'Appliance.Control.ToggleX')
124
+ * @param {number|null} onlineStatus - OnlineStatus filter (optional, null = any status)
125
+ * @param {Array<Object>} deviceFilter - Optional pre-filtered device list (for CLI device selection)
126
+ * @returns {Promise<Array>} Array of devices with the specified ability
127
+ */
128
+ async function findDevicesByAbility(manager, namespace, onlineStatus = null, deviceFilter = null) {
129
+ // If device filter is provided, use it instead of discovering devices
130
+ if (deviceFilter && Array.isArray(deviceFilter) && deviceFilter.length > 0) {
131
+ return deviceFilter.filter(device => {
132
+ // Check ability
133
+ if (!deviceHasAbility(device, namespace)) {
134
+ return false;
135
+ }
136
+ // Filter by online status if specified
137
+ if (onlineStatus !== null) {
138
+ const deviceOnlineStatus = getDeviceOnlineStatus(device);
139
+ return deviceOnlineStatus === onlineStatus;
140
+ }
141
+ return true;
142
+ });
143
+ }
144
+
145
+ // Normal behavior - discover devices
146
+ const devices = await waitForDevices(manager, 2000);
147
+ const filteredDevices = [];
148
+
149
+ for (const { device } of devices) {
150
+ // Check if device has the ability
151
+ if (deviceHasAbility(device, namespace)) {
152
+ // Filter by online status if specified
153
+ if (onlineStatus !== null) {
154
+ const deviceOnlineStatus = getDeviceOnlineStatus(device);
155
+ if (deviceOnlineStatus === onlineStatus) {
156
+ filteredDevices.push(device);
157
+ }
158
+ } else {
159
+ filteredDevices.push(device);
160
+ }
161
+ }
162
+ }
163
+
164
+ return filteredDevices;
165
+ }
166
+
167
+ /**
168
+ * Finds devices by device type
169
+ * @param {Object} manager - MerossManager instance
170
+ * @param {string} deviceType - Device type (e.g., 'mss310', 'msg100')
171
+ * @param {number|null} onlineStatus - OnlineStatus filter (optional, null = any status)
172
+ * @param {Array<Object>} deviceFilter - Optional pre-filtered device list (for CLI device selection)
173
+ * @returns {Promise<Array>} Array of devices with the specified type
174
+ */
175
+ async function findDevicesByType(manager, deviceType, onlineStatus = null, deviceFilter = null) {
176
+ // If device filter is provided, use it instead of discovering devices
177
+ if (deviceFilter && Array.isArray(deviceFilter) && deviceFilter.length > 0) {
178
+ return deviceFilter.filter(device => {
179
+ const baseDeviceType = device.dev?.deviceType;
180
+ const subdeviceType = device.type || device._type;
181
+ const matchesType = baseDeviceType === deviceType || subdeviceType === deviceType;
182
+
183
+ if (!matchesType) {
184
+ return false;
185
+ }
186
+
187
+ // Filter by online status if specified
188
+ if (onlineStatus !== null) {
189
+ const deviceOnlineStatus = getDeviceOnlineStatus(device);
190
+ return deviceOnlineStatus === onlineStatus;
191
+ }
192
+ return true;
193
+ });
194
+ }
195
+
196
+ // Normal behavior - discover devices
197
+ const devices = await waitForDevices(manager, 2000);
198
+ const filteredDevices = [];
199
+
200
+ for (const { device } of devices) {
201
+ // Check both base device type and subdevice type
202
+ const baseDeviceType = device.dev?.deviceType;
203
+ const subdeviceType = device.type || device._type;
204
+ const matchesType = baseDeviceType === deviceType || subdeviceType === deviceType;
205
+
206
+ if (matchesType) {
207
+ // Filter by online status if specified
208
+ if (onlineStatus !== null) {
209
+ const deviceOnlineStatus = getDeviceOnlineStatus(device);
210
+ if (deviceOnlineStatus === onlineStatus) {
211
+ filteredDevices.push(device);
212
+ }
213
+ } else {
214
+ filteredDevices.push(device);
215
+ }
216
+ }
217
+ }
218
+
219
+ return filteredDevices;
220
+ }
221
+
222
+ /**
223
+ * Waits for a device to be connected
224
+ * @param {Object} device - Device instance
225
+ * @param {number} timeout - Timeout in milliseconds (default: 10000)
226
+ * @returns {Promise<boolean>} True if connected, false if timeout
227
+ */
228
+ function waitForDeviceConnection(device, timeout = 10000) {
229
+ return new Promise((resolve) => {
230
+ if (device.deviceConnected) {
231
+ resolve(true);
232
+ return;
233
+ }
234
+
235
+ const onConnected = () => {
236
+ device.removeListener('connected', onConnected);
237
+ device.removeListener('error', onError);
238
+ clearTimeout(timeoutId);
239
+ resolve(true);
240
+ };
241
+
242
+ const onError = (error) => {
243
+ device.removeListener('connected', onConnected);
244
+ device.removeListener('error', onError);
245
+ clearTimeout(timeoutId);
246
+ resolve(false);
247
+ };
248
+
249
+ const timeoutId = setTimeout(() => {
250
+ device.removeListener('connected', onConnected);
251
+ device.removeListener('error', onError);
252
+ resolve(false);
253
+ }, timeout);
254
+
255
+ device.on('connected', onConnected);
256
+ device.on('error', onError);
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Waits for a push notification on a device
262
+ * @param {Object} device - Device instance
263
+ * @param {string} namespace - Namespace to wait for
264
+ * @param {number} timeout - Timeout in milliseconds (default: 10000)
265
+ * @returns {Promise<Object>} Push notification payload
266
+ */
267
+ function waitForPushNotification(device, namespace, timeout = 10000) {
268
+ return new Promise((resolve, reject) => {
269
+ const timeoutId = setTimeout(() => {
270
+ device.removeListener('data', onData);
271
+ reject(new Error(`Timeout waiting for push notification: ${namespace}`));
272
+ }, timeout);
273
+
274
+ const onData = (ns, payload) => {
275
+ if (ns === namespace) {
276
+ clearTimeout(timeoutId);
277
+ device.removeListener('data', onData);
278
+ resolve(payload);
279
+ }
280
+ };
281
+
282
+ device.on('data', onData);
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Gets device display name
288
+ * @param {Object} device - Device instance
289
+ * @returns {string} Device display name
290
+ */
291
+ function getDeviceName(device) {
292
+ if (!device) return 'Unknown device';
293
+ return device.name || device.uuid || 'Unknown device';
294
+ }
295
+
296
+ module.exports = {
297
+ // Device discovery
298
+ waitForDevices,
299
+ findDevicesByAbility,
300
+ findDevicesByType,
301
+
302
+ // Device connection
303
+ waitForDeviceConnection,
304
+ waitForPushNotification,
305
+
306
+ // Device utilities
307
+ getDeviceName,
308
+ getDeviceOnlineStatus,
309
+ deviceHasAbility,
310
+
311
+ // Enums
312
+ OnlineStatus
313
+ };
@@ -0,0 +1,355 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Hub MTS100 Thermostat Valves Tests
5
+ * Tests MTS100 thermostat valve control and configuration
6
+ */
7
+
8
+ const { findDevicesByAbility, waitForDeviceConnection, getDeviceName, OnlineStatus } = require('./test-helper');
9
+
10
+ const metadata = {
11
+ name: 'hub-mts100',
12
+ description: 'Tests MTS100 thermostat valve control and configuration',
13
+ requiredAbilities: ['Appliance.Hub.Mts100.All'],
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 testHub = null;
24
+ if (devices && devices.length > 0) {
25
+ testHub = devices[0];
26
+ } else {
27
+ // Find hub devices with MTS100 support
28
+ const mts100Hubs = await findDevicesByAbility(manager, 'Appliance.Hub.Mts100.All', OnlineStatus.ONLINE);
29
+
30
+ if (mts100Hubs.length > 0) {
31
+ testHub = mts100Hubs[0];
32
+ }
33
+ }
34
+
35
+ if (!testHub) {
36
+ results.push({
37
+ name: 'should get MTS100 all data',
38
+ passed: false,
39
+ skipped: true,
40
+ error: 'No Hub device with MTS100 support has been found to run this test on',
41
+ device: null
42
+ });
43
+ return results;
44
+ }
45
+
46
+ const deviceName = getDeviceName(testHub);
47
+
48
+ await waitForDeviceConnection(testHub, timeout);
49
+ await new Promise(resolve => setTimeout(resolve, 2000));
50
+
51
+ // Test 1: Get MTS100 all data
52
+ try {
53
+ if (typeof testHub.getMts100All !== 'function') {
54
+ results.push({
55
+ name: 'should get MTS100 all data',
56
+ passed: false,
57
+ skipped: true,
58
+ error: 'Hub does not support getMts100All',
59
+ device: deviceName
60
+ });
61
+ } else {
62
+ // Get MTS100 subdevices
63
+ const subdevices = testHub.getSubdevices();
64
+ const mts100Ids = subdevices
65
+ .filter(sub => sub.type === 'mts100v3' || sub.type === 'mts100')
66
+ .map(sub => sub.subdeviceId);
67
+
68
+ if (mts100Ids.length === 0) {
69
+ results.push({
70
+ name: 'should get MTS100 all data',
71
+ passed: false,
72
+ skipped: true,
73
+ error: 'Hub has no MTS100 subdevices',
74
+ device: deviceName
75
+ });
76
+ } else {
77
+ const response = await testHub.getMts100All(mts100Ids);
78
+
79
+ if (!response) {
80
+ results.push({
81
+ name: 'should get MTS100 all data',
82
+ passed: false,
83
+ skipped: false,
84
+ error: 'getMts100All returned null or undefined',
85
+ device: deviceName
86
+ });
87
+ } else if (!Array.isArray(response.all)) {
88
+ results.push({
89
+ name: 'should get MTS100 all data',
90
+ passed: false,
91
+ skipped: false,
92
+ error: 'Response all is not an array',
93
+ device: deviceName
94
+ });
95
+ } else {
96
+ results.push({
97
+ name: 'should get MTS100 all data',
98
+ passed: true,
99
+ skipped: false,
100
+ error: null,
101
+ device: deviceName,
102
+ details: { deviceCount: response.all.length }
103
+ });
104
+ }
105
+ }
106
+ }
107
+ } catch (error) {
108
+ results.push({
109
+ name: 'should get MTS100 all data',
110
+ passed: false,
111
+ skipped: false,
112
+ error: error.message,
113
+ device: deviceName
114
+ });
115
+ }
116
+
117
+ // Test 2: Control MTS100 mode
118
+ try {
119
+ if (typeof testHub.controlHubMts100Mode !== 'function') {
120
+ results.push({
121
+ name: 'should control MTS100 mode',
122
+ passed: false,
123
+ skipped: true,
124
+ error: 'Hub does not support controlHubMts100Mode',
125
+ device: deviceName
126
+ });
127
+ } else {
128
+ const subdevices = testHub.getSubdevices();
129
+ const mts100Ids = subdevices
130
+ .filter(sub => sub.type === 'mts100v3' || sub.type === 'mts100')
131
+ .map(sub => sub.subdeviceId);
132
+
133
+ if (mts100Ids.length === 0) {
134
+ results.push({
135
+ name: 'should control MTS100 mode',
136
+ passed: false,
137
+ skipped: true,
138
+ error: 'Hub has no MTS100 subdevices',
139
+ device: deviceName
140
+ });
141
+ } else {
142
+ // Get current state first
143
+ await testHub.getMts100All([mts100Ids[0]]);
144
+ await new Promise(resolve => setTimeout(resolve, 1000));
145
+
146
+ // Note: We don't actually change the mode to avoid disrupting the device
147
+ // We just verify the method exists and can be called
148
+ results.push({
149
+ name: 'should control MTS100 mode',
150
+ passed: true,
151
+ skipped: false,
152
+ error: null,
153
+ device: deviceName,
154
+ details: { note: 'Method exists, but not changing mode to avoid disruption' }
155
+ });
156
+ }
157
+ }
158
+ } catch (error) {
159
+ results.push({
160
+ name: 'should control MTS100 mode',
161
+ passed: false,
162
+ skipped: false,
163
+ error: error.message,
164
+ device: deviceName
165
+ });
166
+ }
167
+
168
+ // Test 3: Control MTS100 temperature
169
+ try {
170
+ if (typeof testHub.controlHubMts100Temperature !== 'function') {
171
+ results.push({
172
+ name: 'should control MTS100 temperature',
173
+ passed: false,
174
+ skipped: true,
175
+ error: 'Hub does not support controlHubMts100Temperature',
176
+ device: deviceName
177
+ });
178
+ } else {
179
+ const subdevices = testHub.getSubdevices();
180
+ const mts100Ids = subdevices
181
+ .filter(sub => sub.type === 'mts100v3' || sub.type === 'mts100')
182
+ .map(sub => sub.subdeviceId);
183
+
184
+ if (mts100Ids.length === 0) {
185
+ results.push({
186
+ name: 'should control MTS100 temperature',
187
+ passed: false,
188
+ skipped: true,
189
+ error: 'Hub has no MTS100 subdevices',
190
+ device: deviceName
191
+ });
192
+ } else {
193
+ // Get current state first
194
+ await testHub.getMts100All([mts100Ids[0]]);
195
+ await new Promise(resolve => setTimeout(resolve, 1000));
196
+
197
+ // Note: We don't actually change the temperature to avoid disrupting the device
198
+ // We just verify the method exists
199
+ results.push({
200
+ name: 'should control MTS100 temperature',
201
+ passed: true,
202
+ skipped: false,
203
+ error: null,
204
+ device: deviceName,
205
+ details: { note: 'Method exists, but not changing temperature to avoid disruption' }
206
+ });
207
+ }
208
+ }
209
+ } catch (error) {
210
+ results.push({
211
+ name: 'should control MTS100 temperature',
212
+ passed: false,
213
+ skipped: false,
214
+ error: error.message,
215
+ device: deviceName
216
+ });
217
+ }
218
+
219
+ // Test 4: Get MTS100 adjustment settings
220
+ try {
221
+ if (typeof testHub.getMts100Adjust !== 'function') {
222
+ results.push({
223
+ name: 'should get MTS100 adjustment settings',
224
+ passed: false,
225
+ skipped: true,
226
+ error: 'Hub does not support getMts100Adjust',
227
+ device: deviceName
228
+ });
229
+ } else {
230
+ const subdevices = testHub.getSubdevices();
231
+ const mts100Ids = subdevices
232
+ .filter(sub => sub.type === 'mts100v3' || sub.type === 'mts100')
233
+ .map(sub => sub.subdeviceId);
234
+
235
+ if (mts100Ids.length === 0) {
236
+ results.push({
237
+ name: 'should get MTS100 adjustment settings',
238
+ passed: false,
239
+ skipped: true,
240
+ error: 'Hub has no MTS100 subdevices',
241
+ device: deviceName
242
+ });
243
+ } else {
244
+ const response = await testHub.getMts100Adjust(mts100Ids);
245
+
246
+ if (!response) {
247
+ results.push({
248
+ name: 'should get MTS100 adjustment settings',
249
+ passed: false,
250
+ skipped: false,
251
+ error: 'getMts100Adjust returned null or undefined',
252
+ device: deviceName
253
+ });
254
+ } else if (!Array.isArray(response.adjust)) {
255
+ results.push({
256
+ name: 'should get MTS100 adjustment settings',
257
+ passed: false,
258
+ skipped: false,
259
+ error: 'Response adjust is not an array',
260
+ device: deviceName
261
+ });
262
+ } else {
263
+ results.push({
264
+ name: 'should get MTS100 adjustment settings',
265
+ passed: true,
266
+ skipped: false,
267
+ error: null,
268
+ device: deviceName,
269
+ details: { adjustCount: response.adjust.length }
270
+ });
271
+ }
272
+ }
273
+ }
274
+ } catch (error) {
275
+ results.push({
276
+ name: 'should get MTS100 adjustment settings',
277
+ passed: false,
278
+ skipped: false,
279
+ error: error.message,
280
+ device: deviceName
281
+ });
282
+ }
283
+
284
+ // Test 5: Get MTS100 config
285
+ try {
286
+ if (typeof testHub.getMts100Config !== 'function') {
287
+ results.push({
288
+ name: 'should get MTS100 config',
289
+ passed: false,
290
+ skipped: true,
291
+ error: 'Hub does not support getMts100Config',
292
+ device: deviceName
293
+ });
294
+ } else {
295
+ const subdevices = testHub.getSubdevices();
296
+ const mts100Ids = subdevices
297
+ .filter(sub => sub.type === 'mts100v3' || sub.type === 'mts100')
298
+ .map(sub => sub.subdeviceId);
299
+
300
+ if (mts100Ids.length === 0) {
301
+ results.push({
302
+ name: 'should get MTS100 config',
303
+ passed: false,
304
+ skipped: true,
305
+ error: 'Hub has no MTS100 subdevices',
306
+ device: deviceName
307
+ });
308
+ } else {
309
+ const response = await testHub.getMts100Config(mts100Ids);
310
+
311
+ if (!response) {
312
+ results.push({
313
+ name: 'should get MTS100 config',
314
+ passed: false,
315
+ skipped: false,
316
+ error: 'getMts100Config returned null or undefined',
317
+ device: deviceName
318
+ });
319
+ } else if (!Array.isArray(response.config)) {
320
+ results.push({
321
+ name: 'should get MTS100 config',
322
+ passed: false,
323
+ skipped: false,
324
+ error: 'Response config is not an array',
325
+ device: deviceName
326
+ });
327
+ } else {
328
+ results.push({
329
+ name: 'should get MTS100 config',
330
+ passed: true,
331
+ skipped: false,
332
+ error: null,
333
+ device: deviceName,
334
+ details: { configCount: response.config.length }
335
+ });
336
+ }
337
+ }
338
+ }
339
+ } catch (error) {
340
+ results.push({
341
+ name: 'should get MTS100 config',
342
+ passed: false,
343
+ skipped: false,
344
+ error: error.message,
345
+ device: deviceName
346
+ });
347
+ }
348
+
349
+ return results;
350
+ }
351
+
352
+ module.exports = {
353
+ metadata,
354
+ runTests
355
+ };