meross-cli 0.2.0 → 0.4.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 CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-01-19
9
+
10
+ ### Changed
11
+ - **BREAKING**: Updated to use new manager module structure from `meross-iot` v0.5.0
12
+ - Updated to use manager properties (`manager.devices`, `manager.mqtt`, `manager.http`, etc.) instead of direct methods
13
+ - Updated all commands and helpers to use new property-based access patterns
14
+ - **BREAKING**: Updated to use standardized error handling from `meross-iot` v0.5.0
15
+ - Updated to use new `MerossError*` error class names
16
+ - Replaced inline error handling with centralized `handleError()` function
17
+ - All error handling now uses the new error handler utility for consistent, user-friendly formatted messages
18
+
19
+ ### Added
20
+ - Centralized error handler utility (`cli/utils/error-handler.js`) with formatted error messages
21
+ - Enhanced error display with better context and user-friendly formatting
22
+
23
+ ## [0.3.0] - 2026-01-16
24
+
25
+ ### Changed
26
+ - **BREAKING**: Updated to use simplified device API from `meross-iot` v0.4.0
27
+ - Updated to use `initializeDevices()` instead of `getDevices()`
28
+ - Updated to use direct device properties instead of `cachedHttpInfo`
29
+ - Updated to use camelCase property names consistently
30
+ - Updated all tests and commands to use new API patterns
31
+
8
32
  ## [0.2.0] - 2026-01-15
9
33
 
10
34
  ### Changed
package/README.md CHANGED
@@ -23,7 +23,7 @@ Command-line interface for controlling and managing Meross smart home devices.
23
23
  npm install -g meross-cli@alpha
24
24
 
25
25
  # Or install specific version
26
- npm install -g meross-cli@0.2.0
26
+ npm install -g meross-cli@0.4.0
27
27
  ```
28
28
 
29
29
  Or use via npx:
@@ -77,6 +77,33 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
77
77
 
78
78
  ## Changelog
79
79
 
80
+ ### [0.4.0] - 2026-01-19
81
+
82
+ #### Changed
83
+ - **BREAKING**: Updated to use new manager module structure from `meross-iot` v0.5.0
84
+ - Updated to use manager properties (`manager.devices`, `manager.mqtt`, `manager.http`, etc.) instead of direct methods
85
+ - Updated all commands and helpers to use new property-based access patterns
86
+ - **BREAKING**: Updated to use standardized error handling from `meross-iot` v0.5.0
87
+ - Updated to use new `MerossError*` error class names
88
+ - Replaced inline error handling with centralized `handleError()` function
89
+ - All error handling now uses the new error handler utility for consistent, user-friendly formatted messages
90
+
91
+ #### Added
92
+ - Centralized error handler utility (`cli/utils/error-handler.js`) with formatted error messages
93
+ - Enhanced error display with better context and user-friendly formatting
94
+
95
+ <details>
96
+ <summary>Older</summary>
97
+
98
+ ### [0.3.0] - 2026-01-16
99
+
100
+ #### Changed
101
+ - **BREAKING**: Updated to use simplified device API from `meross-iot` v0.4.0
102
+ - Updated to use `initializeDevices()` instead of `getDevices()`
103
+ - Updated to use direct device properties instead of `cachedHttpInfo`
104
+ - Updated to use camelCase property names consistently
105
+ - Updated all tests and commands to use new API patterns
106
+
80
107
  ### [0.2.0] - 2026-01-15
81
108
 
82
109
  #### Changed
@@ -107,11 +134,6 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
107
134
  - This is an initial, pre-stable release. Please expect bugs.
108
135
  - Some edge cases may not be fully handled yet.
109
136
 
110
- <details>
111
- <summary>Older</summary>
112
-
113
- <!-- Older changelog entries will appear here -->
114
-
115
137
  </details>
116
138
 
117
139
  ## Disclaimer
@@ -1,18 +1,31 @@
1
1
  'use strict';
2
2
 
3
+ const ManagerMeross = require('meross-iot');
4
+
3
5
  async function executeControlCommand(manager, uuid, methodName, params) {
4
6
  const device = manager.devices.get(uuid);
5
7
 
6
8
  if (!device) {
7
- throw new Error(`Device not found: ${uuid}`);
9
+ throw new ManagerMeross.MerossErrorNotFound(
10
+ `Device not found: ${uuid}`,
11
+ 'device',
12
+ uuid
13
+ );
8
14
  }
9
15
 
10
16
  if (!device.deviceConnected) {
11
- throw new Error('Device is not connected. Please wait for device to connect.');
17
+ throw new ManagerMeross.MerossErrorUnconnected(
18
+ 'Device is not connected. Please wait for device to connect.',
19
+ uuid
20
+ );
12
21
  }
13
22
 
14
23
  if (typeof device[methodName] !== 'function') {
15
- throw new Error(`Control method not available: ${methodName}`);
24
+ throw new ManagerMeross.MerossErrorUnsupported(
25
+ `Control method not available: ${methodName}`,
26
+ methodName,
27
+ 'Method not supported by this device'
28
+ );
16
29
  }
17
30
 
18
31
  // All methods now use unified options pattern, so we can call directly with params
@@ -143,7 +143,7 @@ async function controlDeviceMenu(manager, rl, currentUser = null) {
143
143
  }
144
144
 
145
145
  // Check error budget if using LAN HTTP transport modes
146
- const transportMode = manager.defaultTransportMode;
146
+ const transportMode = manager.transport.defaultMode;
147
147
  const usesLanHttp = transportMode === TransportMode.LAN_HTTP_FIRST ||
148
148
  transportMode === TransportMode.LAN_HTTP_FIRST_ONLY_GET;
149
149
  if (usesLanHttp) {
@@ -170,10 +170,8 @@ async function controlDeviceMenu(manager, rl, currentUser = null) {
170
170
  }
171
171
 
172
172
  } catch (error) {
173
- console.log(chalk.red(`\n✗ Error: ${error.message}`));
174
- if (error.stack && process.env.MEROSS_VERBOSE) {
175
- console.error(error.stack);
176
- }
173
+ const { handleError } = require('../../utils/error-handler');
174
+ handleError(error, { verbose: process.env.MEROSS_VERBOSE === 'true' });
177
175
  }
178
176
 
179
177
  const { continueControl } = await inquirer.prompt([{
@@ -34,7 +34,7 @@ function _buildBasicDeviceInfo(device, manager) {
34
34
 
35
35
  info.push(['Connected', status]);
36
36
  info.push(['Online', onlineStatus]);
37
- info.push(['Transport', getTransportModeName(manager.defaultTransportMode)]);
37
+ info.push(['Transport', getTransportModeName(manager.transport.defaultMode)]);
38
38
 
39
39
  return info;
40
40
  }
@@ -84,35 +84,41 @@ function _displayChannels(device) {
84
84
  }
85
85
 
86
86
  /**
87
- * Builds HTTP device info data array.
87
+ * Extracts HTTP device info properties for display.
88
+ *
89
+ * Collects HTTP metadata properties (domain, reservedDomain, subType, region, etc.)
90
+ * that are now directly accessible on the device instance for formatting in the CLI output.
91
+ *
92
+ * @param {Object} device - MerossDevice instance
93
+ * @returns {Array<Array<string>>} Array of [label, value] pairs for display
88
94
  */
89
- function _buildHttpInfoData(httpInfo) {
95
+ function _buildHttpInfoData(device) {
90
96
  const httpInfoData = [];
91
97
 
92
- if (httpInfo.domain) {
93
- httpInfoData.push(['MQTT Domain', httpInfo.domain]);
98
+ if (device.domain) {
99
+ httpInfoData.push(['MQTT Domain', device.domain]);
94
100
  }
95
- if (httpInfo.reservedDomain) {
96
- httpInfoData.push(['Reserved Domain', httpInfo.reservedDomain]);
101
+ if (device.reservedDomain) {
102
+ httpInfoData.push(['Reserved Domain', device.reservedDomain]);
97
103
  }
98
- if (httpInfo.subType) {
99
- httpInfoData.push(['Sub Type', httpInfo.subType]);
104
+ if (device.subType) {
105
+ httpInfoData.push(['Sub Type', device.subType]);
100
106
  }
101
- if (httpInfo.region) {
102
- httpInfoData.push(['Region', httpInfo.region]);
107
+ if (device.region) {
108
+ httpInfoData.push(['Region', device.region]);
103
109
  }
104
- if (httpInfo.skillNumber) {
105
- httpInfoData.push(['Skill Number', httpInfo.skillNumber]);
110
+ if (device.skillNumber) {
111
+ httpInfoData.push(['Skill Number', device.skillNumber]);
106
112
  }
107
- if (httpInfo.devIconId) {
108
- httpInfoData.push(['Icon ID', httpInfo.devIconId]);
113
+ if (device.devIconId) {
114
+ httpInfoData.push(['Icon ID', device.devIconId]);
109
115
  }
110
- if (httpInfo.bindTime) {
111
- httpInfoData.push(['Bind Time', httpInfo.bindTime.toLocaleString()]);
116
+ if (device.bindTime) {
117
+ httpInfoData.push(['Bind Time', device.bindTime.toLocaleString()]);
112
118
  }
113
- if (httpInfo.onlineStatus !== undefined) {
114
- const onlineStatusText = httpInfo.onlineStatus === OnlineStatus.ONLINE ? chalk.green('Online') :
115
- httpInfo.onlineStatus === OnlineStatus.OFFLINE ? chalk.red('Offline') :
119
+ if (device.onlineStatus !== undefined) {
120
+ const onlineStatusText = device.onlineStatus === OnlineStatus.ONLINE ? chalk.green('Online') :
121
+ device.onlineStatus === OnlineStatus.OFFLINE ? chalk.red('Offline') :
116
122
  chalk.yellow('Unknown');
117
123
  httpInfoData.push(['Online Status', onlineStatusText]);
118
124
  }
@@ -121,15 +127,12 @@ function _buildHttpInfoData(httpInfo) {
121
127
  }
122
128
 
123
129
  /**
124
- * Displays HTTP device info if available.
130
+ * Displays HTTP device info section if properties are available.
131
+ *
132
+ * @param {Object} device - MerossDevice instance
125
133
  */
126
134
  function _displayHttpInfo(device) {
127
- if (!device.cachedHttpInfo) {
128
- return;
129
- }
130
-
131
- const httpInfo = device.cachedHttpInfo;
132
- const httpInfoData = _buildHttpInfoData(httpInfo);
135
+ const httpInfoData = _buildHttpInfoData(device);
133
136
 
134
137
  if (httpInfoData.length === 0) {
135
138
  return;
@@ -762,10 +762,10 @@ function getMethodsByCategory() {
762
762
  * @returns {boolean} True if device supports the namespace
763
763
  */
764
764
  function deviceSupportsNamespace(device, namespace) {
765
- if (!device._abilities || typeof device._abilities !== 'object') {
765
+ if (!device.abilities || typeof device.abilities !== 'object') {
766
766
  return false;
767
767
  }
768
- return !!device._abilities[namespace];
768
+ return !!device.abilities[namespace];
769
769
  }
770
770
 
771
771
  /**
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const { MerossHttpClient, TransportMode } = require('meross-iot');
5
+ const { handleError } = require('../utils/error-handler');
5
6
 
6
7
  async function processOptionsAndCreateHttpClient(opts) {
7
8
  const email = opts.email || process.env.MEROSS_EMAIL || null;
@@ -33,16 +34,21 @@ async function processOptionsAndCreateHttpClient(opts) {
33
34
  maxStatsSamples: 1000
34
35
  });
35
36
  } else if (email && password) {
36
- httpClient = await MerossHttpClient.fromUserPassword({
37
- email,
38
- password,
39
- mfaCode,
40
- logger: verbose ? console.log : null,
41
- timeout,
42
- autoRetryOnBadDomain: true,
43
- enableStats,
44
- maxStatsSamples: 1000
45
- });
37
+ try {
38
+ httpClient = await MerossHttpClient.fromUserPassword({
39
+ email,
40
+ password,
41
+ mfaCode,
42
+ logger: verbose ? console.log : null,
43
+ timeout,
44
+ autoRetryOnBadDomain: true,
45
+ enableStats,
46
+ maxStatsSamples: 1000
47
+ });
48
+ } catch (error) {
49
+ // Re-throw with better context for MFA/auth errors
50
+ throw error;
51
+ }
46
52
  } else {
47
53
  throw new Error('Email and password are required (or provide token data).\nUse --email and --password options or set MEROSS_EMAIL and MEROSS_PASSWORD environment variables.');
48
54
  }
@@ -3,6 +3,7 @@
3
3
  const ManagerMeross = require('meross-iot');
4
4
  const { MerossHttpClient, TransportMode } = require('meross-iot');
5
5
  const ora = require('ora');
6
+ const { handleError } = require('../utils/error-handler');
6
7
 
7
8
  async function createMerossInstance(optionsOrEmail, password, mfaCode, transportMode, timeout, enableStats, verbose) {
8
9
  let httpClient;
@@ -29,16 +30,21 @@ async function createMerossInstance(optionsOrEmail, password, mfaCode, transport
29
30
  finalVerbose = verbose || false;
30
31
 
31
32
  // Create HTTP client
32
- httpClient = await MerossHttpClient.fromUserPassword({
33
- email,
34
- password,
35
- mfaCode,
36
- logger: finalVerbose ? console.log : null,
37
- timeout: finalTimeout,
38
- autoRetryOnBadDomain: true,
39
- enableStats: finalEnableStats,
40
- maxStatsSamples: 1000
41
- });
33
+ try {
34
+ httpClient = await MerossHttpClient.fromUserPassword({
35
+ email,
36
+ password,
37
+ mfaCode,
38
+ logger: finalVerbose ? console.log : null,
39
+ timeout: finalTimeout,
40
+ autoRetryOnBadDomain: true,
41
+ enableStats: finalEnableStats,
42
+ maxStatsSamples: 1000
43
+ });
44
+ } catch (error) {
45
+ // Re-throw to let caller handle with proper error formatting
46
+ throw error;
47
+ }
42
48
  }
43
49
 
44
50
  const instance = new ManagerMeross({
@@ -79,10 +85,8 @@ async function connectMeross(manager) {
79
85
  await new Promise(resolve => setTimeout(resolve, 2000));
80
86
  return true;
81
87
  } catch (error) {
82
- spinner.fail(`Connection error: ${error.message}`);
83
- if (error.stack && process.env.MEROSS_VERBOSE) {
84
- console.error(error.stack);
85
- }
88
+ spinner.stop();
89
+ handleError(error, { verbose: process.env.MEROSS_VERBOSE === 'true' });
86
90
  return false;
87
91
  }
88
92
  }
package/cli/menu/main.js CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  const chalk = require('chalk');
4
4
  const inquirer = require('inquirer');
5
+ const ora = require('ora');
5
6
  const { MerossHubDevice, MerossSubDevice, createDebugUtils, TransportMode } = require('meross-iot');
6
7
  const testRunner = require('../tests/test-runner');
7
8
  const { clearScreen, renderLogoAtTop, renderSimpleHeader, clearMenuArea, CONTENT_START_LINE, SIMPLE_CONTENT_START_LINE, createRL, question, promptForPassword } = require('../utils/terminal');
8
9
  const { formatDevice } = require('../utils/display');
9
10
  const { listDevices, showStats, dumpRegistry, listMqttConnections, getDeviceStatus, showDeviceInfo, controlDeviceMenu, runTestCommand, snifferMenu } = require('../commands');
10
11
  const { addUser, getUser, listUsers } = require('../config/users');
11
- const { createMerossInstance, connectMeross, disconnectMeross } = require('../helpers/meross');
12
+ const { createMerossInstance, disconnectMeross } = require('../helpers/meross');
12
13
  const { showSettingsMenu } = require('./settings');
13
14
 
14
15
  // Helper functions
@@ -169,6 +170,164 @@ async function _saveCredentialsPrompt(rl, currentCredentials) {
169
170
  return null;
170
171
  }
171
172
 
173
+ /**
174
+ * Prompts user to select devices and subdevices to initialize.
175
+ *
176
+ * Discovers available devices and subdevices, presents them in a hierarchical
177
+ * selection UI with subdevices indented under their hubs, and initializes
178
+ * only the selected items.
179
+ *
180
+ * @param {ManagerMeross} manager - Meross manager instance
181
+ * @returns {Promise<boolean>} True if initialization succeeded, false otherwise
182
+ * @private
183
+ */
184
+ async function _selectDevicesToInitialize(manager) {
185
+ const spinner = ora('Discovering available devices...').start();
186
+ try {
187
+ const [baseDevices, subdevices] = await Promise.all([
188
+ manager.devices.discover({ onlineOnly: true }),
189
+ manager.devices.discoverSubdevices({ onlineOnly: true })
190
+ ]);
191
+ spinner.stop();
192
+
193
+ if ((!baseDevices || baseDevices.length === 0) && (!subdevices || subdevices.length === 0)) {
194
+ console.log(chalk.yellow('\nNo online devices or subdevices found.'));
195
+ return false;
196
+ }
197
+
198
+ // Group subdevices by hub UUID to display them hierarchically
199
+ const subdevicesByHub = new Map();
200
+ if (subdevices && subdevices.length > 0) {
201
+ for (const subdevice of subdevices) {
202
+ const hubUuid = subdevice.hubUuid;
203
+ if (!subdevicesByHub.has(hubUuid)) {
204
+ subdevicesByHub.set(hubUuid, []);
205
+ }
206
+ subdevicesByHub.get(hubUuid).push(subdevice);
207
+ }
208
+ }
209
+
210
+ const deviceChoices = [];
211
+ const deviceUuids = new Set();
212
+
213
+ if (baseDevices && baseDevices.length > 0) {
214
+ for (const device of baseDevices) {
215
+ const hasSubdevices = subdevicesByHub.has(device.uuid);
216
+ if (hasSubdevices) {
217
+ deviceUuids.add(device.uuid);
218
+ }
219
+
220
+ deviceChoices.push({
221
+ name: `${device.devName || 'Unknown'} (${device.deviceType}) - ${chalk.grey(device.uuid)}`,
222
+ value: `device:${device.uuid}`,
223
+ checked: true
224
+ });
225
+
226
+ // Display subdevices indented under their hub for visual hierarchy
227
+ if (hasSubdevices) {
228
+ const hubSubdevices = subdevicesByHub.get(device.uuid);
229
+ for (const subdevice of hubSubdevices) {
230
+ deviceChoices.push({
231
+ name: ` └─ ${subdevice.subdeviceName || 'Unknown'} (${subdevice.subdeviceType}) - ${chalk.grey(subdevice.subdeviceId)}`,
232
+ value: `subdevice:${subdevice.hubUuid}:${subdevice.subdeviceId}`,
233
+ checked: true
234
+ });
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ // Handle edge case where subdevices exist but their hub isn't in base device list
241
+ for (const [hubUuid, hubSubdevices] of subdevicesByHub) {
242
+ if (!deviceUuids.has(hubUuid)) {
243
+ for (const subdevice of hubSubdevices) {
244
+ deviceChoices.push({
245
+ name: ` └─ ${subdevice.subdeviceName || 'Unknown'} (${subdevice.subdeviceType}) - ${chalk.grey(subdevice.subdeviceId)} [Hub: ${chalk.grey(subdevice.hubUuid)}]`,
246
+ value: `subdevice:${subdevice.hubUuid}:${subdevice.subdeviceId}`,
247
+ checked: true
248
+ });
249
+ }
250
+ }
251
+ }
252
+
253
+ if (deviceChoices.length === 0) {
254
+ console.log(chalk.yellow('\nNo devices or subdevices found.'));
255
+ return false;
256
+ }
257
+
258
+ const { selectedItems } = await inquirer.prompt([{
259
+ type: 'checkbox',
260
+ name: 'selectedItems',
261
+ message: 'Select devices/subdevices to initialize (use space to toggle, enter to confirm):',
262
+ choices: deviceChoices,
263
+ pageSize: 20
264
+ }]);
265
+
266
+ if (!selectedItems || selectedItems.length === 0) {
267
+ console.log(chalk.yellow('\nNo devices selected. Skipping initialization.'));
268
+ return false;
269
+ }
270
+
271
+ // Parse selection into base devices and subdevices for different initialization paths
272
+ const baseDeviceUuids = [];
273
+ const subdeviceIdentifiers = [];
274
+
275
+ for (const item of selectedItems) {
276
+ if (item.startsWith('device:')) {
277
+ baseDeviceUuids.push(item.replace('device:', ''));
278
+ } else if (item.startsWith('subdevice:')) {
279
+ const parts = item.replace('subdevice:', '').split(':');
280
+ if (parts.length === 2) {
281
+ subdeviceIdentifiers.push({ hubUuid: parts[0], id: parts[1] });
282
+ }
283
+ }
284
+ }
285
+
286
+ spinner.start('Initializing selected devices...');
287
+ try {
288
+ let totalInitialized = 0;
289
+
290
+ if (baseDeviceUuids.length > 0) {
291
+ const deviceCount = await manager.devices.initialize({ uuids: baseDeviceUuids });
292
+ totalInitialized += deviceCount;
293
+ }
294
+
295
+ // Initialize subdevices individually since they require hub context
296
+ for (const subdeviceId of subdeviceIdentifiers) {
297
+ try {
298
+ const subdevice = await manager.devices.initializeDevice(subdeviceId);
299
+ if (subdevice) {
300
+ totalInitialized++;
301
+ }
302
+ } catch (error) {
303
+ if (manager.options && manager.options.logger) {
304
+ manager.options.logger(`Failed to initialize subdevice ${subdeviceId.id}: ${error.message}`);
305
+ }
306
+ }
307
+ }
308
+
309
+ spinner.succeed(`Initialized ${totalInitialized} device(s)`);
310
+
311
+ // Allow time for MQTT connections to establish
312
+ await new Promise(resolve => setTimeout(resolve, 2000));
313
+
314
+ manager.authenticated = true;
315
+
316
+ return true;
317
+ } catch (error) {
318
+ spinner.stop();
319
+ const { handleError } = require('../utils/error-handler');
320
+ handleError(error, { verbose: process.env.MEROSS_VERBOSE === 'true' });
321
+ return false;
322
+ }
323
+ } catch (error) {
324
+ spinner.stop();
325
+ const { handleError } = require('../utils/error-handler');
326
+ handleError(error, { verbose: process.env.MEROSS_VERBOSE === 'true' });
327
+ return false;
328
+ }
329
+ }
330
+
172
331
  async function _selectDevice(manager, message = 'Select device:') {
173
332
  const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
174
333
  if (devices.length === 0) {
@@ -384,12 +543,12 @@ function _buildSettingsCallbacks(
384
543
  enableStatsRef.current,
385
544
  verboseRef.current
386
545
  );
387
- const connected = await connectMeross(managerRef.current, rl);
546
+ const connected = await _selectDevicesToInitialize(managerRef.current);
388
547
  if (connected) {
389
548
  currentUserRef.current = userName;
390
549
  return { success: true };
391
550
  }
392
- return { success: false, error: 'Failed to connect with new user' };
551
+ return { success: false, error: 'Failed to initialize devices with new user' };
393
552
  },
394
553
  onSaveCredentials: async (name) => {
395
554
  if (!currentCredentials) {
@@ -408,15 +567,15 @@ function _buildSettingsCallbacks(
408
567
  }
409
568
 
410
569
  async function _handleSettingsCommand(manager, rl, currentUserRef, currentCredentials) {
411
- const transportModeRef = { current: manager.defaultTransportMode || TransportMode.MQTT_ONLY };
570
+ const transportModeRef = { current: manager.transport.defaultMode || TransportMode.MQTT_ONLY };
412
571
  const timeoutRef = { current: 10000 };
413
- const enableStatsRef = { current: manager._mqttStatsCounter !== null || manager.httpClient._httpStatsCounter !== null };
572
+ const enableStatsRef = { current: manager.statistics.isEnabled() };
414
573
  const verboseRef = { current: manager.options && manager.options.logger !== null };
415
574
  const managerRef = { current: manager };
416
575
 
417
576
  const setTransportMode = (mode) => {
418
577
  transportModeRef.current = mode;
419
- managerRef.current.defaultTransportMode = mode;
578
+ managerRef.current.transport.defaultMode = mode;
420
579
  };
421
580
  const setTimeout = (newTimeout) => {
422
581
  timeoutRef.current = newTimeout;
@@ -540,10 +699,10 @@ async function menuMode() {
540
699
  currentCredentials = result.credentials;
541
700
  }
542
701
 
543
- // Connect
544
- const connected = await connectMeross(currentManager, rl);
702
+ // Discover and select devices to initialize
703
+ const connected = await _selectDevicesToInitialize(currentManager);
545
704
  if (!connected) {
546
- console.error('Failed to connect. Exiting.');
705
+ console.error('Failed to initialize devices. Exiting.');
547
706
  rl.close();
548
707
  return;
549
708
  }
@@ -622,10 +781,8 @@ async function menuMode() {
622
781
  }
623
782
  }
624
783
  } catch (error) {
625
- console.error(`\nError: ${error.message}`);
626
- if (error.stack && process.env.MEROSS_VERBOSE) {
627
- console.error(error.stack);
628
- }
784
+ const { handleError } = require('../utils/error-handler');
785
+ handleError(error, { verbose: process.env.MEROSS_VERBOSE === 'true' });
629
786
  await question(rl, '\nPress Enter to return to menu...');
630
787
  }
631
788
  }
@@ -32,7 +32,7 @@ async function showSettingsMenu(rl, currentManager, currentUser, timeout, enable
32
32
  const debug = currentManager ? createDebugUtils(currentManager) : null;
33
33
  const currentStatsEnabled = debug ? debug.isStatsEnabled() : enableStats;
34
34
  const currentTransportMode = currentManager
35
- ? getTransportModeName(currentManager.defaultTransportMode)
35
+ ? getTransportModeName(currentManager.transport.defaultMode)
36
36
  : getTransportModeName(TransportMode.MQTT_ONLY);
37
37
  const currentVerboseState = currentManager && currentManager.options ? (currentManager.options.logger !== null) : verbose;
38
38
 
@@ -111,7 +111,7 @@ async function showTransportModeSettings(rl, currentManager, currentUser, setTra
111
111
  type: 'list',
112
112
  name: 'mode',
113
113
  message: 'Transport Mode',
114
- default: currentManager.defaultTransportMode,
114
+ default: currentManager.transport.defaultMode,
115
115
  choices: [
116
116
  {
117
117
  name: 'MQTT Only (default, works remotely)',