meross-cli 0.1.0 → 0.3.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 +18 -1
- package/README.md +23 -6
- package/cli/commands/control/execute.js +1 -1
- package/cli/commands/control/menu.js +5 -5
- package/cli/commands/dump.js +1 -1
- package/cli/commands/info.js +30 -27
- package/cli/commands/list.js +2 -2
- package/cli/commands/mqtt.js +2 -2
- package/cli/commands/sniffer/sniffer-menu.js +3 -3
- package/cli/commands/status/device-status.js +2 -2
- package/cli/commands/status/index.js +1 -1
- package/cli/control-registry.js +2 -2
- package/cli/helpers/meross.js +2 -2
- package/cli/menu/main.js +6 -6
- package/cli/menu/settings.js +11 -11
- package/cli/meross-cli.js +3 -3
- package/cli/tests/README.md +1 -1
- package/cli/tests/test-control.js +4 -5
- package/cli/tests/test-diffuser.js +2 -2
- package/cli/tests/test-dnd.js +2 -3
- package/cli/tests/test-encryption.js +5 -5
- package/cli/tests/test-helper.js +29 -39
- package/cli/tests/test-hub-sensors.js +1 -1
- package/cli/tests/test-light.js +2 -2
- package/cli/tests/test-runner.js +10 -13
- package/cli/tests/test-runtime.js +4 -5
- package/cli/tests/test-template.js +3 -3
- package/cli/tests/test-toggle.js +4 -4
- package/package.json +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,24 @@ 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
|
-
## [
|
|
8
|
+
## [0.3.0] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **BREAKING**: Updated to use simplified device API from `meross-iot` v0.4.0
|
|
12
|
+
- Updated to use `initializeDevices()` instead of `getDevices()`
|
|
13
|
+
- Updated to use direct device properties instead of `cachedHttpInfo`
|
|
14
|
+
- Updated to use camelCase property names consistently
|
|
15
|
+
- Updated all tests and commands to use new API patterns
|
|
16
|
+
|
|
17
|
+
## [0.2.0] - 2026-01-15
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **BREAKING**: Updated to use new Manager-prefix naming pattern from `meross-iot` package
|
|
21
|
+
- `MerossManager` → `ManagerMeross` (internal usage)
|
|
22
|
+
- `SubscriptionManager` → `ManagerSubscription` (internal usage)
|
|
23
|
+
- **BREAKING**: Updated to use new property-based access patterns from `meross-iot` package
|
|
24
|
+
- Uses `meross.subscription` instead of `getSubscriptionManager()`
|
|
25
|
+
- Uses `meross.devices.get()`, `meross.devices.find()`, and `meross.devices.list()` instead of wrapper methods
|
|
9
26
|
|
|
10
27
|
## [0.1.0] - 2026-01-10
|
|
11
28
|
|
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.
|
|
26
|
+
npm install -g meross-cli@0.3.0
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Or use via npx:
|
|
@@ -77,6 +77,28 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
|
|
|
77
77
|
|
|
78
78
|
## Changelog
|
|
79
79
|
|
|
80
|
+
### [0.3.0] - 2026-01-16
|
|
81
|
+
|
|
82
|
+
#### Changed
|
|
83
|
+
- **BREAKING**: Updated to use simplified device API from `meross-iot` v0.4.0
|
|
84
|
+
- Updated to use `initializeDevices()` instead of `getDevices()`
|
|
85
|
+
- Updated to use direct device properties instead of `cachedHttpInfo`
|
|
86
|
+
- Updated to use camelCase property names consistently
|
|
87
|
+
- Updated all tests and commands to use new API patterns
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary>Older</summary>
|
|
91
|
+
|
|
92
|
+
### [0.2.0] - 2026-01-15
|
|
93
|
+
|
|
94
|
+
#### Changed
|
|
95
|
+
- **BREAKING**: Updated to use new Manager-prefix naming pattern from `meross-iot` package
|
|
96
|
+
- `MerossManager` → `ManagerMeross` (internal usage)
|
|
97
|
+
- `SubscriptionManager` → `ManagerSubscription` (internal usage)
|
|
98
|
+
- **BREAKING**: Updated to use new property-based access patterns from `meross-iot` package
|
|
99
|
+
- Uses `meross.subscription` instead of `getSubscriptionManager()`
|
|
100
|
+
- Uses `meross.devices.get()`, `meross.devices.find()`, and `meross.devices.list()` instead of wrapper methods
|
|
101
|
+
|
|
80
102
|
### [0.1.0] - 2026-01-10
|
|
81
103
|
|
|
82
104
|
#### Added
|
|
@@ -97,11 +119,6 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
|
|
|
97
119
|
- This is an initial, pre-stable release. Please expect bugs.
|
|
98
120
|
- Some edge cases may not be fully handled yet.
|
|
99
121
|
|
|
100
|
-
<details>
|
|
101
|
-
<summary>Older</summary>
|
|
102
|
-
|
|
103
|
-
<!-- Older changelog entries will appear here -->
|
|
104
|
-
|
|
105
122
|
</details>
|
|
106
123
|
|
|
107
124
|
## Disclaimer
|
|
@@ -23,12 +23,12 @@ async function question(rl, query) {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Interactive device control menu.
|
|
26
|
-
* @param {Object} manager -
|
|
26
|
+
* @param {Object} manager - ManagerMeross instance
|
|
27
27
|
* @param {Object} rl - Readline interface
|
|
28
28
|
* @param {string|null} currentUser - Current logged in user name
|
|
29
29
|
*/
|
|
30
30
|
async function controlDeviceMenu(manager, rl, currentUser = null) {
|
|
31
|
-
const devices = manager.
|
|
31
|
+
const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
32
32
|
if (devices.length === 0) {
|
|
33
33
|
console.log('\nNo devices found.');
|
|
34
34
|
return;
|
|
@@ -50,7 +50,7 @@ async function controlDeviceMenu(manager, rl, currentUser = null) {
|
|
|
50
50
|
choices: deviceChoices
|
|
51
51
|
}]);
|
|
52
52
|
|
|
53
|
-
const device = manager.
|
|
53
|
+
const device = manager.devices.get(uuid);
|
|
54
54
|
|
|
55
55
|
// Wait for device to connect if needed
|
|
56
56
|
if (!device.deviceConnected) {
|
|
@@ -92,11 +92,11 @@ async function controlDeviceMenu(manager, rl, currentUser = null) {
|
|
|
92
92
|
// Control loop
|
|
93
93
|
while (true) {
|
|
94
94
|
clearScreen();
|
|
95
|
-
const deviceCount = manager.
|
|
95
|
+
const deviceCount = manager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length;
|
|
96
96
|
renderSimpleHeader(currentUser, deviceCount);
|
|
97
97
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
98
98
|
|
|
99
|
-
const info = formatDevice(manager.
|
|
99
|
+
const info = formatDevice(manager.devices.get(uuid));
|
|
100
100
|
console.log(chalk.bold(`=== Control Device: ${info.name} ===\n`));
|
|
101
101
|
|
|
102
102
|
// Build choices grouped by category
|
package/cli/commands/dump.js
CHANGED
package/cli/commands/info.js
CHANGED
|
@@ -84,35 +84,41 @@ function _displayChannels(device) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
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(
|
|
95
|
+
function _buildHttpInfoData(device) {
|
|
90
96
|
const httpInfoData = [];
|
|
91
97
|
|
|
92
|
-
if (
|
|
93
|
-
httpInfoData.push(['MQTT Domain',
|
|
98
|
+
if (device.domain) {
|
|
99
|
+
httpInfoData.push(['MQTT Domain', device.domain]);
|
|
94
100
|
}
|
|
95
|
-
if (
|
|
96
|
-
httpInfoData.push(['Reserved Domain',
|
|
101
|
+
if (device.reservedDomain) {
|
|
102
|
+
httpInfoData.push(['Reserved Domain', device.reservedDomain]);
|
|
97
103
|
}
|
|
98
|
-
if (
|
|
99
|
-
httpInfoData.push(['Sub Type',
|
|
104
|
+
if (device.subType) {
|
|
105
|
+
httpInfoData.push(['Sub Type', device.subType]);
|
|
100
106
|
}
|
|
101
|
-
if (
|
|
102
|
-
httpInfoData.push(['Region',
|
|
107
|
+
if (device.region) {
|
|
108
|
+
httpInfoData.push(['Region', device.region]);
|
|
103
109
|
}
|
|
104
|
-
if (
|
|
105
|
-
httpInfoData.push(['Skill Number',
|
|
110
|
+
if (device.skillNumber) {
|
|
111
|
+
httpInfoData.push(['Skill Number', device.skillNumber]);
|
|
106
112
|
}
|
|
107
|
-
if (
|
|
108
|
-
httpInfoData.push(['Icon ID',
|
|
113
|
+
if (device.devIconId) {
|
|
114
|
+
httpInfoData.push(['Icon ID', device.devIconId]);
|
|
109
115
|
}
|
|
110
|
-
if (
|
|
111
|
-
httpInfoData.push(['Bind Time',
|
|
116
|
+
if (device.bindTime) {
|
|
117
|
+
httpInfoData.push(['Bind Time', device.bindTime.toLocaleString()]);
|
|
112
118
|
}
|
|
113
|
-
if (
|
|
114
|
-
const onlineStatusText =
|
|
115
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -197,7 +200,7 @@ function _displayCapabilities(device) {
|
|
|
197
200
|
}
|
|
198
201
|
|
|
199
202
|
async function showDeviceInfo(manager, uuid) {
|
|
200
|
-
const device = manager.
|
|
203
|
+
const device = manager.devices.get(uuid);
|
|
201
204
|
|
|
202
205
|
if (!device) {
|
|
203
206
|
console.error(`Device with UUID ${chalk.cyan(uuid)} not found.`);
|
package/cli/commands/list.js
CHANGED
|
@@ -6,7 +6,7 @@ const { MerossHubDevice, MerossSubDevice, OnlineStatus, parsePushNotification }
|
|
|
6
6
|
const { formatDevice } = require('../utils/display');
|
|
7
7
|
|
|
8
8
|
async function listDevices(manager) {
|
|
9
|
-
const hubs = manager.
|
|
9
|
+
const hubs = manager.devices.list().filter(device => device instanceof MerossHubDevice);
|
|
10
10
|
if (hubs.length > 0) {
|
|
11
11
|
const spinner = ora('Loading devices and refreshing hub status').start();
|
|
12
12
|
// Refresh hub state to update subdevice online status
|
|
@@ -48,7 +48,7 @@ async function listDevices(manager) {
|
|
|
48
48
|
spinner.stop();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const allDevices = manager.
|
|
51
|
+
const allDevices = manager.devices.list();
|
|
52
52
|
const devices = allDevices.filter(device => !(device instanceof MerossSubDevice));
|
|
53
53
|
|
|
54
54
|
if (devices.length === 0) {
|
package/cli/commands/mqtt.js
CHANGED
|
@@ -42,7 +42,7 @@ async function listMqttConnections(manager, options = {}) {
|
|
|
42
42
|
|
|
43
43
|
const namespaces = new Set();
|
|
44
44
|
deviceUuids.forEach(uuid => {
|
|
45
|
-
const device = manager.
|
|
45
|
+
const device = manager.devices.get(uuid);
|
|
46
46
|
if (device && device.abilities) {
|
|
47
47
|
Object.keys(device.abilities).forEach(namespace => {
|
|
48
48
|
namespaces.add(namespace);
|
|
@@ -170,7 +170,7 @@ async function listMqttConnections(manager, options = {}) {
|
|
|
170
170
|
if (conn.deviceUuids.length > 0) {
|
|
171
171
|
console.log(`\n ${chalk.white.bold(`Device UUIDs (${chalk.cyan(conn.deviceUuids.length)}):`)}`);
|
|
172
172
|
conn.deviceUuids.forEach((uuid) => {
|
|
173
|
-
const device = manager.
|
|
173
|
+
const device = manager.devices.get(uuid);
|
|
174
174
|
const deviceName = device ? (device.name || 'Unknown') : 'Unknown';
|
|
175
175
|
console.log(` ${chalk.cyan(uuid)} ${chalk.gray(`(${deviceName})`)}`);
|
|
176
176
|
});
|
|
@@ -447,7 +447,7 @@ async function _createZipArchive(options) {
|
|
|
447
447
|
|
|
448
448
|
/**
|
|
449
449
|
* Main sniffer menu function
|
|
450
|
-
* @param {Object} manager -
|
|
450
|
+
* @param {Object} manager - ManagerMeross instance
|
|
451
451
|
* @param {Object} rl - Readline interface
|
|
452
452
|
* @param {string|null} currentUser - Current logged in user name
|
|
453
453
|
*/
|
|
@@ -464,7 +464,7 @@ async function snifferMenu(manager, rl, currentUser = null) {
|
|
|
464
464
|
logStream.write(`[${timestamp}] ${msg}\n`);
|
|
465
465
|
};
|
|
466
466
|
|
|
467
|
-
const deviceCount = manager.
|
|
467
|
+
const deviceCount = manager.devices.list().filter(d => {
|
|
468
468
|
const { MerossSubDevice } = require('meross-iot');
|
|
469
469
|
return !(d instanceof MerossSubDevice);
|
|
470
470
|
}).length;
|
|
@@ -473,7 +473,7 @@ async function snifferMenu(manager, rl, currentUser = null) {
|
|
|
473
473
|
_printWelcomeMessage(currentUser, deviceCount);
|
|
474
474
|
|
|
475
475
|
// Select device
|
|
476
|
-
const devices = manager.
|
|
476
|
+
const devices = manager.devices.list().filter(d => {
|
|
477
477
|
// Filter out subdevices
|
|
478
478
|
const { MerossSubDevice } = require('meross-iot');
|
|
479
479
|
return !(d instanceof MerossSubDevice);
|
|
@@ -410,7 +410,7 @@ function _mapRollerShutterFeatureNamespace(namespace, device) {
|
|
|
410
410
|
|
|
411
411
|
/**
|
|
412
412
|
* Feature namespace mapping registry organized by feature type.
|
|
413
|
-
* Matches the
|
|
413
|
+
* Matches the ManagerMeross class structure where features are organized by type.
|
|
414
414
|
*/
|
|
415
415
|
const FEATURE_NAMESPACE_MAPPERS = [
|
|
416
416
|
{ prefix: 'Appliance.Control.Toggle', mapper: _mapToggleFeatureNamespace },
|
|
@@ -433,7 +433,7 @@ const FEATURE_NAMESPACE_MAPPERS = [
|
|
|
433
433
|
/**
|
|
434
434
|
* Maps a namespace to its feature method and arguments.
|
|
435
435
|
* Returns { featureMethod, featureArgs } or { featureMethod: null, featureArgs: [] } if no feature method exists.
|
|
436
|
-
* Organized by feature type to match
|
|
436
|
+
* Organized by feature type to match ManagerMeross class structure.
|
|
437
437
|
*/
|
|
438
438
|
function _mapNamespaceToFeatureMethod(namespace, device) {
|
|
439
439
|
for (const { prefix, mapper } of FEATURE_NAMESPACE_MAPPERS) {
|
|
@@ -6,7 +6,7 @@ const { displayHubStatus } = require('./hub-status');
|
|
|
6
6
|
const { displayDeviceStatus } = require('./device-status');
|
|
7
7
|
|
|
8
8
|
async function getDeviceStatus(manager, filterUuid = null, filterSubdeviceId = null) {
|
|
9
|
-
const allDevices = filterUuid ? [manager.
|
|
9
|
+
const allDevices = filterUuid ? [manager.devices.get(filterUuid)].filter(Boolean) : manager.devices.list();
|
|
10
10
|
const devices = allDevices.filter(device => !(device instanceof MerossSubDevice));
|
|
11
11
|
|
|
12
12
|
if (devices.length === 0) {
|
package/cli/control-registry.js
CHANGED
|
@@ -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.
|
|
765
|
+
if (!device.abilities || typeof device.abilities !== 'object') {
|
|
766
766
|
return false;
|
|
767
767
|
}
|
|
768
|
-
return !!device.
|
|
768
|
+
return !!device.abilities[namespace];
|
|
769
769
|
}
|
|
770
770
|
|
|
771
771
|
/**
|
package/cli/helpers/meross.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const ManagerMeross = require('meross-iot');
|
|
4
4
|
const { MerossHttpClient, TransportMode } = require('meross-iot');
|
|
5
5
|
const ora = require('ora');
|
|
6
6
|
|
|
@@ -41,7 +41,7 @@ async function createMerossInstance(optionsOrEmail, password, mfaCode, transport
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const instance = new
|
|
44
|
+
const instance = new ManagerMeross({
|
|
45
45
|
httpClient,
|
|
46
46
|
transportMode: finalTransportMode,
|
|
47
47
|
timeout: finalTimeout,
|
package/cli/menu/main.js
CHANGED
|
@@ -14,7 +14,7 @@ const { showSettingsMenu } = require('./settings');
|
|
|
14
14
|
// Helper functions
|
|
15
15
|
function _getDeviceCount(manager) {
|
|
16
16
|
if (!manager) {return 0;}
|
|
17
|
-
return manager.
|
|
17
|
+
return manager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function _renderMainMenuHeader(currentUser, manager) {
|
|
@@ -170,7 +170,7 @@ async function _saveCredentialsPrompt(rl, currentCredentials) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
async function _selectDevice(manager, message = 'Select device:') {
|
|
173
|
-
const devices = manager.
|
|
173
|
+
const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
174
174
|
if (devices.length === 0) {
|
|
175
175
|
return null;
|
|
176
176
|
}
|
|
@@ -194,7 +194,7 @@ async function _selectDevice(manager, message = 'Select device:') {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
async function _selectDeviceOrAll(manager) {
|
|
197
|
-
const devices = manager.
|
|
197
|
+
const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
198
198
|
if (devices.length === 0) {
|
|
199
199
|
return null;
|
|
200
200
|
}
|
|
@@ -225,7 +225,7 @@ async function _selectSubdevice(manager, uuid) {
|
|
|
225
225
|
return null;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
const device = manager.
|
|
228
|
+
const device = manager.devices.get(uuid);
|
|
229
229
|
if (!(device instanceof MerossHubDevice)) {
|
|
230
230
|
return null;
|
|
231
231
|
}
|
|
@@ -262,7 +262,7 @@ async function _handleListCommand(manager, rl) {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
async function _handleInfoCommand(manager, rl) {
|
|
265
|
-
const devices = manager.
|
|
265
|
+
const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
266
266
|
if (devices.length === 0) {
|
|
267
267
|
console.log('\nNo devices found.');
|
|
268
268
|
await question(rl, '\nPress Enter to return to menu...');
|
|
@@ -277,7 +277,7 @@ async function _handleInfoCommand(manager, rl) {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
async function _handleStatusCommand(manager, rl) {
|
|
280
|
-
const devices = manager.
|
|
280
|
+
const devices = manager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
281
281
|
if (devices.length === 0) {
|
|
282
282
|
console.log('\nNo devices found.');
|
|
283
283
|
await question(rl, '\nPress Enter to return to menu...');
|
package/cli/menu/settings.js
CHANGED
|
@@ -25,7 +25,7 @@ async function showSettingsMenu(rl, currentManager, currentUser, timeout, enable
|
|
|
25
25
|
while (true) {
|
|
26
26
|
// Clear screen and render simple header
|
|
27
27
|
clearScreen();
|
|
28
|
-
const deviceCount = currentManager ? currentManager.
|
|
28
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
29
29
|
renderSimpleHeader(currentUser, deviceCount);
|
|
30
30
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
31
31
|
|
|
@@ -102,7 +102,7 @@ async function showSettingsMenu(rl, currentManager, currentUser, timeout, enable
|
|
|
102
102
|
async function showTransportModeSettings(rl, currentManager, currentUser, setTransportMode) {
|
|
103
103
|
// Clear screen and render simple header
|
|
104
104
|
clearScreen();
|
|
105
|
-
const deviceCount = currentManager ? currentManager.
|
|
105
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
106
106
|
renderSimpleHeader(currentUser, deviceCount);
|
|
107
107
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
108
108
|
|
|
@@ -135,7 +135,7 @@ async function showTransportModeSettings(rl, currentManager, currentUser, setTra
|
|
|
135
135
|
async function showStatisticsSettings(rl, currentManager, currentUser, enableStats, setEnableStats) {
|
|
136
136
|
// Clear screen and render simple header
|
|
137
137
|
clearScreen();
|
|
138
|
-
const deviceCount = currentManager ? currentManager.
|
|
138
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
139
139
|
renderSimpleHeader(currentUser, deviceCount);
|
|
140
140
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
141
141
|
|
|
@@ -190,7 +190,7 @@ async function showStatisticsSettings(rl, currentManager, currentUser, enableSta
|
|
|
190
190
|
// Helper functions for user management menu
|
|
191
191
|
function _renderUserManagementHeader(currentManager, currentUser) {
|
|
192
192
|
clearScreen();
|
|
193
|
-
const deviceCount = currentManager ? currentManager.
|
|
193
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
194
194
|
renderSimpleHeader(currentUser, deviceCount);
|
|
195
195
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
196
196
|
process.stdout.write(chalk.bold('=== User Management ===\n\n'));
|
|
@@ -478,7 +478,7 @@ async function showUserManagementMenu(rl, currentManager, currentUser, callbacks
|
|
|
478
478
|
async function showTimeoutSettings(rl, currentManager, currentUser, timeout, setTimeout) {
|
|
479
479
|
// Clear screen and render simple header
|
|
480
480
|
clearScreen();
|
|
481
|
-
const deviceCount = currentManager ? currentManager.
|
|
481
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
482
482
|
renderSimpleHeader(currentUser, deviceCount);
|
|
483
483
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
484
484
|
|
|
@@ -521,7 +521,7 @@ async function showTimeoutSettings(rl, currentManager, currentUser, timeout, set
|
|
|
521
521
|
async function showVerboseSettings(rl, currentManager, currentUser, verbose, setVerbose) {
|
|
522
522
|
// Clear screen and render simple header
|
|
523
523
|
clearScreen();
|
|
524
|
-
const deviceCount = currentManager ? currentManager.
|
|
524
|
+
const deviceCount = currentManager ? currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length : 0;
|
|
525
525
|
renderSimpleHeader(currentUser, deviceCount);
|
|
526
526
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
527
527
|
|
|
@@ -574,7 +574,7 @@ async function showErrorBudgetSettings(rl, currentManager, currentUser) {
|
|
|
574
574
|
while (true) {
|
|
575
575
|
// Clear screen and render simple header
|
|
576
576
|
clearScreen();
|
|
577
|
-
const deviceCount = currentManager.
|
|
577
|
+
const deviceCount = currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice)).length;
|
|
578
578
|
renderSimpleHeader(currentUser, deviceCount);
|
|
579
579
|
clearMenuArea(SIMPLE_CONTENT_START_LINE);
|
|
580
580
|
|
|
@@ -619,7 +619,7 @@ async function showErrorBudgetSettings(rl, currentManager, currentUser) {
|
|
|
619
619
|
if (action === 'back') {
|
|
620
620
|
break;
|
|
621
621
|
} else if (action === 'view-all') {
|
|
622
|
-
const devices = currentManager.
|
|
622
|
+
const devices = currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
623
623
|
if (devices.length === 0) {
|
|
624
624
|
console.log(chalk.yellow('\n No devices found.\n'));
|
|
625
625
|
} else {
|
|
@@ -642,7 +642,7 @@ async function showErrorBudgetSettings(rl, currentManager, currentUser) {
|
|
|
642
642
|
message: 'Press Enter to continue...'
|
|
643
643
|
}]);
|
|
644
644
|
} else if (action === 'view-device') {
|
|
645
|
-
const devices = currentManager.
|
|
645
|
+
const devices = currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
646
646
|
if (devices.length === 0) {
|
|
647
647
|
console.log(chalk.yellow('\n No devices found.\n'));
|
|
648
648
|
await inquirer.prompt([{
|
|
@@ -693,7 +693,7 @@ async function showErrorBudgetSettings(rl, currentManager, currentUser) {
|
|
|
693
693
|
message: 'Press Enter to continue...'
|
|
694
694
|
}]);
|
|
695
695
|
} else if (action === 'reset-device') {
|
|
696
|
-
const devices = currentManager.
|
|
696
|
+
const devices = currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
697
697
|
if (devices.length === 0) {
|
|
698
698
|
console.log(chalk.yellow('\n No devices found.\n'));
|
|
699
699
|
await inquirer.prompt([{
|
|
@@ -756,7 +756,7 @@ async function showErrorBudgetSettings(rl, currentManager, currentUser) {
|
|
|
756
756
|
}]);
|
|
757
757
|
|
|
758
758
|
if (confirm) {
|
|
759
|
-
const devices = currentManager.
|
|
759
|
+
const devices = currentManager.devices.list().filter(d => !(d instanceof MerossSubDevice));
|
|
760
760
|
let resetCount = 0;
|
|
761
761
|
devices.forEach(device => {
|
|
762
762
|
const uuid = device.uuid;
|
package/cli/meross-cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const ManagerMeross = require('meross-iot');
|
|
5
5
|
const { MerossHubDevice } = require('meross-iot');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const testRunner = require('./tests/test-runner');
|
|
@@ -85,7 +85,7 @@ Examples:
|
|
|
85
85
|
|
|
86
86
|
const { options, config } = processed;
|
|
87
87
|
|
|
88
|
-
const manager = new
|
|
88
|
+
const manager = new ManagerMeross(options);
|
|
89
89
|
|
|
90
90
|
// Handle device events
|
|
91
91
|
manager.on('deviceInitialized', (deviceId) => {
|
|
@@ -280,7 +280,7 @@ Examples:
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
function _resolveDevice(manager, uuid, subdeviceId) {
|
|
283
|
-
let device = manager.
|
|
283
|
+
let device = manager.devices.get(uuid.trim());
|
|
284
284
|
if (!device) {
|
|
285
285
|
console.error(chalk.red(`Error: Device not found: ${uuid}`));
|
|
286
286
|
process.exit(1);
|
package/cli/tests/README.md
CHANGED
|
@@ -210,7 +210,7 @@ module.exports = {
|
|
|
210
210
|
- `minDevices`: Minimum number of devices required (default: 1)
|
|
211
211
|
|
|
212
212
|
2. **runTests Function**: Must accept a `context` object with:
|
|
213
|
-
- `manager`:
|
|
213
|
+
- `manager`: ManagerMeross instance
|
|
214
214
|
- `devices`: Array of pre-selected devices (optional, will auto-discover if not provided)
|
|
215
215
|
- `options`: Test options (timeout, verbose, etc.)
|
|
216
216
|
|
|
@@ -26,17 +26,16 @@ async function runTests(context) {
|
|
|
26
26
|
const multipleDevices = await findDevicesByAbility(manager, 'Appliance.Control.Multiple', OnlineStatus.ONLINE);
|
|
27
27
|
|
|
28
28
|
// Also get any online device for testing control features
|
|
29
|
-
const allDevices = manager.
|
|
29
|
+
const allDevices = manager.devices.list();
|
|
30
30
|
const onlineDevices = allDevices.filter(device => {
|
|
31
|
-
|
|
32
|
-
return status === OnlineStatus.ONLINE;
|
|
31
|
+
return device.onlineStatus === OnlineStatus.ONLINE;
|
|
33
32
|
});
|
|
34
33
|
|
|
35
34
|
// Combine and deduplicate
|
|
36
35
|
testDevices = [...multipleDevices];
|
|
37
36
|
for (const device of onlineDevices.slice(0, 2)) {
|
|
38
|
-
const uuid = device.
|
|
39
|
-
if (!testDevices.find(d =>
|
|
37
|
+
const uuid = device.uuid;
|
|
38
|
+
if (!testDevices.find(d => d.uuid === uuid)) {
|
|
40
39
|
testDevices.push(device);
|
|
41
40
|
}
|
|
42
41
|
}
|
|
@@ -27,10 +27,10 @@ async function runTests(context) {
|
|
|
27
27
|
if (devices && devices.length > 0) {
|
|
28
28
|
// Use provided devices, filter by capabilities
|
|
29
29
|
lightDevices = devices.filter(d =>
|
|
30
|
-
d.
|
|
30
|
+
d.abilities && d.abilities['Appliance.Control.Diffuser.Light']
|
|
31
31
|
);
|
|
32
32
|
sprayDevices = devices.filter(d =>
|
|
33
|
-
d.
|
|
33
|
+
d.abilities && d.abilities['Appliance.Control.Diffuser.Spray']
|
|
34
34
|
);
|
|
35
35
|
} else {
|
|
36
36
|
// Find diffuser light devices
|
package/cli/tests/test-dnd.js
CHANGED
|
@@ -24,10 +24,9 @@ async function runTests(context) {
|
|
|
24
24
|
let testDevices = devices || [];
|
|
25
25
|
if (testDevices.length === 0) {
|
|
26
26
|
// Find devices that support DND mode (most devices support this)
|
|
27
|
-
const allDevices = manager.
|
|
27
|
+
const allDevices = manager.devices.list();
|
|
28
28
|
testDevices = allDevices.filter(d => {
|
|
29
|
-
|
|
30
|
-
return status === OnlineStatus.ONLINE &&
|
|
29
|
+
return d.onlineStatus === OnlineStatus.ONLINE &&
|
|
31
30
|
typeof d.getDNDMode === 'function' &&
|
|
32
31
|
typeof d.setDNDMode === 'function';
|
|
33
32
|
});
|
|
@@ -31,7 +31,7 @@ async function runTests(context) {
|
|
|
31
31
|
// Combine and deduplicate
|
|
32
32
|
const allDevices = [...encryptionDevices];
|
|
33
33
|
for (const device of suiteDevices) {
|
|
34
|
-
if (!allDevices.find(d => d.
|
|
34
|
+
if (!allDevices.find(d => d.uuid === device.uuid)) {
|
|
35
35
|
allDevices.push(device);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -115,8 +115,8 @@ async function runTests(context) {
|
|
|
115
115
|
const isKeySet = testDevice.isEncryptionKeySet();
|
|
116
116
|
|
|
117
117
|
// If key is not set and we have the required info, set it
|
|
118
|
-
if (!isKeySet && testDevice.
|
|
119
|
-
testDevice.setEncryptionKey(testDevice.
|
|
118
|
+
if (!isKeySet && testDevice.uuid && manager.key && testDevice.macAddress) {
|
|
119
|
+
testDevice.setEncryptionKey(testDevice.uuid, manager.key, testDevice.macAddress);
|
|
120
120
|
|
|
121
121
|
const keySetAfter = testDevice.isEncryptionKeySet();
|
|
122
122
|
|
|
@@ -180,8 +180,8 @@ async function runTests(context) {
|
|
|
180
180
|
} else {
|
|
181
181
|
// Ensure encryption key is set
|
|
182
182
|
if (!testDevice.isEncryptionKeySet()) {
|
|
183
|
-
if (testDevice.
|
|
184
|
-
testDevice.setEncryptionKey(testDevice.
|
|
183
|
+
if (testDevice.uuid && manager.key && testDevice.macAddress) {
|
|
184
|
+
testDevice.setEncryptionKey(testDevice.uuid, manager.key, testDevice.macAddress);
|
|
185
185
|
} else {
|
|
186
186
|
results.push({
|
|
187
187
|
name: 'should encrypt and decrypt messages if encryption key is set',
|
package/cli/tests/test-helper.js
CHANGED
|
@@ -10,47 +10,47 @@ const { OnlineStatus } = require('meross-iot');
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Waits for devices to be discovered
|
|
14
|
-
*
|
|
13
|
+
* Waits for devices to be discovered.
|
|
14
|
+
*
|
|
15
|
+
* Returns an array of MerossDevice instances once they are initialized. Uses
|
|
16
|
+
* device UUID (or subdevice ID) for deduplication to avoid returning the same
|
|
17
|
+
* device multiple times.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} manager - ManagerMeross instance
|
|
15
20
|
* @param {number} timeout - Timeout in milliseconds (default: 5000)
|
|
16
|
-
* @returns {Promise<Array
|
|
21
|
+
* @returns {Promise<Array<MerossDevice>>} Array of device instances
|
|
17
22
|
*/
|
|
18
23
|
function waitForDevices(manager, timeout = 5000) {
|
|
19
24
|
return new Promise((resolve) => {
|
|
20
25
|
// If devices already exist, return them immediately
|
|
21
|
-
const existingDevices = manager.
|
|
26
|
+
const existingDevices = manager.devices.list();
|
|
22
27
|
if (existingDevices && existingDevices.length > 0) {
|
|
23
28
|
const devices = [];
|
|
24
29
|
const deviceIds = new Set();
|
|
25
30
|
|
|
26
31
|
for (const device of existingDevices) {
|
|
27
|
-
// Use unique identifier for subdevices (
|
|
32
|
+
// Use unique identifier for subdevices (parentUUID:subdeviceId) or UUID for base devices
|
|
28
33
|
const deviceId = device.subdeviceId
|
|
29
|
-
? `${device.
|
|
30
|
-
:
|
|
34
|
+
? `${device.uuid}:${device.subdeviceId}`
|
|
35
|
+
: device.uuid;
|
|
31
36
|
|
|
32
37
|
if (!deviceIds.has(deviceId)) {
|
|
33
38
|
deviceIds.add(deviceId);
|
|
34
|
-
devices.push(
|
|
35
|
-
deviceId: deviceId,
|
|
36
|
-
deviceDef: device.dev || { uuid: device.uuid },
|
|
37
|
-
device
|
|
38
|
-
});
|
|
39
|
+
devices.push(device);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
resolve(devices);
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
// Wait for devices to be initialized
|
|
46
46
|
const devices = [];
|
|
47
47
|
const deviceIds = new Set();
|
|
48
48
|
let timeoutId = null;
|
|
49
49
|
|
|
50
|
-
const onDeviceInitialized = (deviceId,
|
|
50
|
+
const onDeviceInitialized = (deviceId, device) => {
|
|
51
51
|
if (!deviceIds.has(deviceId)) {
|
|
52
52
|
deviceIds.add(deviceId);
|
|
53
|
-
devices.push(
|
|
53
|
+
devices.push(device);
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -61,9 +61,10 @@ function waitForDevices(manager, timeout = 5000) {
|
|
|
61
61
|
resolve(devices);
|
|
62
62
|
}, timeout);
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// Periodic check handles cases where deviceInitialized event was missed
|
|
65
|
+
// (e.g., devices initialized before event handler was attached)
|
|
65
66
|
const checkInterval = setInterval(() => {
|
|
66
|
-
const currentDevices = manager.
|
|
67
|
+
const currentDevices = manager.devices.list();
|
|
67
68
|
if (currentDevices && currentDevices.length > 0) {
|
|
68
69
|
clearInterval(checkInterval);
|
|
69
70
|
if (timeoutId) {
|
|
@@ -71,19 +72,14 @@ function waitForDevices(manager, timeout = 5000) {
|
|
|
71
72
|
}
|
|
72
73
|
manager.removeListener('deviceInitialized', onDeviceInitialized);
|
|
73
74
|
|
|
74
|
-
// Add any new devices
|
|
75
75
|
for (const device of currentDevices) {
|
|
76
76
|
const deviceId = device.subdeviceId
|
|
77
|
-
? `${device.
|
|
78
|
-
:
|
|
77
|
+
? `${device.uuid}:${device.subdeviceId}`
|
|
78
|
+
: device.uuid;
|
|
79
79
|
|
|
80
80
|
if (!deviceIds.has(deviceId)) {
|
|
81
81
|
deviceIds.add(deviceId);
|
|
82
|
-
devices.push(
|
|
83
|
-
deviceId: deviceId,
|
|
84
|
-
deviceDef: device.dev || { uuid: device.uuid },
|
|
85
|
-
device
|
|
86
|
-
});
|
|
82
|
+
devices.push(device);
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
resolve(devices);
|
|
@@ -98,13 +94,7 @@ function waitForDevices(manager, timeout = 5000) {
|
|
|
98
94
|
* @returns {number|null} OnlineStatus value or null if not available
|
|
99
95
|
*/
|
|
100
96
|
function getDeviceOnlineStatus(device) {
|
|
101
|
-
|
|
102
|
-
return device.onlineStatus;
|
|
103
|
-
}
|
|
104
|
-
if (device.dev && device.dev.onlineStatus !== undefined) {
|
|
105
|
-
return device.dev.onlineStatus;
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
97
|
+
return device.onlineStatus !== undefined ? device.onlineStatus : null;
|
|
108
98
|
}
|
|
109
99
|
|
|
110
100
|
/**
|
|
@@ -114,12 +104,12 @@ function getDeviceOnlineStatus(device) {
|
|
|
114
104
|
* @returns {boolean} True if device has the ability
|
|
115
105
|
*/
|
|
116
106
|
function deviceHasAbility(device, namespace) {
|
|
117
|
-
return !!(device.
|
|
107
|
+
return !!(device.abilities && device.abilities[namespace]);
|
|
118
108
|
}
|
|
119
109
|
|
|
120
110
|
/**
|
|
121
111
|
* Finds devices by ability namespace
|
|
122
|
-
* @param {Object} manager -
|
|
112
|
+
* @param {Object} manager - ManagerMeross instance
|
|
123
113
|
* @param {string} namespace - Ability namespace (e.g., 'Appliance.Control.ToggleX')
|
|
124
114
|
* @param {number|null} onlineStatus - OnlineStatus filter (optional, null = any status)
|
|
125
115
|
* @param {Array<Object>} deviceFilter - Optional pre-filtered device list (for CLI device selection)
|
|
@@ -146,7 +136,7 @@ async function findDevicesByAbility(manager, namespace, onlineStatus = null, dev
|
|
|
146
136
|
const devices = await waitForDevices(manager, 2000);
|
|
147
137
|
const filteredDevices = [];
|
|
148
138
|
|
|
149
|
-
for (const
|
|
139
|
+
for (const device of devices) {
|
|
150
140
|
// Check if device has the ability
|
|
151
141
|
if (deviceHasAbility(device, namespace)) {
|
|
152
142
|
// Filter by online status if specified
|
|
@@ -166,7 +156,7 @@ async function findDevicesByAbility(manager, namespace, onlineStatus = null, dev
|
|
|
166
156
|
|
|
167
157
|
/**
|
|
168
158
|
* Finds devices by device type
|
|
169
|
-
* @param {Object} manager -
|
|
159
|
+
* @param {Object} manager - ManagerMeross instance
|
|
170
160
|
* @param {string} deviceType - Device type (e.g., 'mss310', 'msg100')
|
|
171
161
|
* @param {number|null} onlineStatus - OnlineStatus filter (optional, null = any status)
|
|
172
162
|
* @param {Array<Object>} deviceFilter - Optional pre-filtered device list (for CLI device selection)
|
|
@@ -176,7 +166,7 @@ async function findDevicesByType(manager, deviceType, onlineStatus = null, devic
|
|
|
176
166
|
// If device filter is provided, use it instead of discovering devices
|
|
177
167
|
if (deviceFilter && Array.isArray(deviceFilter) && deviceFilter.length > 0) {
|
|
178
168
|
return deviceFilter.filter(device => {
|
|
179
|
-
const baseDeviceType = device.
|
|
169
|
+
const baseDeviceType = device.deviceType;
|
|
180
170
|
const subdeviceType = device.type || device._type;
|
|
181
171
|
const matchesType = baseDeviceType === deviceType || subdeviceType === deviceType;
|
|
182
172
|
|
|
@@ -197,9 +187,9 @@ async function findDevicesByType(manager, deviceType, onlineStatus = null, devic
|
|
|
197
187
|
const devices = await waitForDevices(manager, 2000);
|
|
198
188
|
const filteredDevices = [];
|
|
199
189
|
|
|
200
|
-
for (const
|
|
190
|
+
for (const device of devices) {
|
|
201
191
|
// Check both base device type and subdevice type
|
|
202
|
-
const baseDeviceType = device.
|
|
192
|
+
const baseDeviceType = device.deviceType;
|
|
203
193
|
const subdeviceType = device.type || device._type;
|
|
204
194
|
const matchesType = baseDeviceType === deviceType || subdeviceType === deviceType;
|
|
205
195
|
|
|
@@ -36,7 +36,7 @@ async function runTests(context) {
|
|
|
36
36
|
// Also try finding by hub sensor abilities
|
|
37
37
|
const sensorHubs = await findDevicesByAbility(manager, 'Appliance.Hub.Sensor.All', OnlineStatus.ONLINE);
|
|
38
38
|
for (const device of sensorHubs) {
|
|
39
|
-
if (!hubDevices.find(d =>
|
|
39
|
+
if (!hubDevices.find(d => d.uuid === device.uuid)) {
|
|
40
40
|
hubDevices.push(device);
|
|
41
41
|
}
|
|
42
42
|
}
|
package/cli/tests/test-light.js
CHANGED
|
@@ -233,13 +233,13 @@ async function runTests(context) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
// Test 3: RGB Push Notification
|
|
236
|
-
// Note: This test requires a second
|
|
236
|
+
// Note: This test requires a second ManagerMeross instance which is complex to set up
|
|
237
237
|
// For now, we'll skip this test or simplify it
|
|
238
238
|
results.push({
|
|
239
239
|
name: 'should receive push notification when RGB color changes',
|
|
240
240
|
passed: true,
|
|
241
241
|
skipped: true,
|
|
242
|
-
error: 'Push notification test requires second
|
|
242
|
+
error: 'Push notification test requires second ManagerMeross instance - skipped for simplicity',
|
|
243
243
|
device: deviceName,
|
|
244
244
|
details: { note: 'Test can be implemented later if needed' }
|
|
245
245
|
});
|
package/cli/tests/test-runner.js
CHANGED
|
@@ -157,7 +157,7 @@ async function runTest(testType, context) {
|
|
|
157
157
|
const { manager, devices = [], options = {} } = context;
|
|
158
158
|
|
|
159
159
|
if (!manager) {
|
|
160
|
-
throw new Error('
|
|
160
|
+
throw new Error('ManagerMeross instance is required in context');
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// Resolve test type (handles aliases)
|
|
@@ -310,7 +310,7 @@ function getTestFile(testType) {
|
|
|
310
310
|
/**
|
|
311
311
|
* Finds devices for a given test type
|
|
312
312
|
* @param {string} testType - Test type name or alias
|
|
313
|
-
* @param {Object} manager -
|
|
313
|
+
* @param {Object} manager - ManagerMeross instance
|
|
314
314
|
* @returns {Promise<Array>} Array of matching devices
|
|
315
315
|
*/
|
|
316
316
|
async function findDevicesForTestType(testType, manager) {
|
|
@@ -321,11 +321,11 @@ async function findDevicesForTestType(testType, manager) {
|
|
|
321
321
|
return [];
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
-
const allDevices = manager.
|
|
324
|
+
const allDevices = manager.devices.list();
|
|
325
325
|
if (!allDevices || allDevices.length === 0) {
|
|
326
326
|
const { waitForDevices } = require('./test-helper');
|
|
327
327
|
const deviceList = await waitForDevices(manager, 1000);
|
|
328
|
-
return deviceList.
|
|
328
|
+
return deviceList.filter(d => d);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
const matchingDevices = [];
|
|
@@ -340,13 +340,12 @@ async function findDevicesForTestType(testType, manager) {
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
for (const device of allDevices) {
|
|
343
|
-
const uuid = device.
|
|
343
|
+
const uuid = device.uuid;
|
|
344
344
|
if (seenUuids.has(uuid)) {
|
|
345
345
|
continue;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
if (device.onlineStatus !== OnlineStatus.ONLINE
|
|
349
|
-
device.dev?.onlineStatus !== OnlineStatus.ONLINE) {
|
|
348
|
+
if (device.onlineStatus !== OnlineStatus.ONLINE) {
|
|
350
349
|
continue;
|
|
351
350
|
}
|
|
352
351
|
|
|
@@ -371,7 +370,7 @@ async function findDevicesForTestType(testType, manager) {
|
|
|
371
370
|
}
|
|
372
371
|
|
|
373
372
|
const hasAbility = abilities.some(ability =>
|
|
374
|
-
device.
|
|
373
|
+
device.abilities && device.abilities[ability]
|
|
375
374
|
);
|
|
376
375
|
|
|
377
376
|
if (hasAbility) {
|
|
@@ -383,17 +382,15 @@ async function findDevicesForTestType(testType, manager) {
|
|
|
383
382
|
// For garage, also try by device type if no matches found
|
|
384
383
|
if (testType.toLowerCase() === 'garage' && matchingDevices.length === 0) {
|
|
385
384
|
for (const device of allDevices) {
|
|
386
|
-
const uuid = device.
|
|
385
|
+
const uuid = device.uuid;
|
|
387
386
|
if (seenUuids.has(uuid)) {
|
|
388
387
|
continue;
|
|
389
388
|
}
|
|
390
389
|
|
|
391
|
-
const baseDeviceType = device.dev?.deviceType;
|
|
392
390
|
const subdeviceType = device.type || device._type;
|
|
393
|
-
const matchesType =
|
|
391
|
+
const matchesType = device.deviceType === 'msg100' || subdeviceType === 'msg100';
|
|
394
392
|
|
|
395
|
-
if (matchesType &&
|
|
396
|
-
device.dev?.onlineStatus === OnlineStatus.ONLINE)) {
|
|
393
|
+
if (matchesType && device.onlineStatus === OnlineStatus.ONLINE) {
|
|
397
394
|
seenUuids.add(uuid);
|
|
398
395
|
matchingDevices.push(device);
|
|
399
396
|
}
|
|
@@ -23,13 +23,12 @@ async function runTests(context) {
|
|
|
23
23
|
let testDevices = devices || [];
|
|
24
24
|
if (testDevices.length === 0) {
|
|
25
25
|
// Runtime is typically available on most devices, so we'll test with any online device
|
|
26
|
-
const allDevices = manager.
|
|
26
|
+
const allDevices = manager.devices.list();
|
|
27
27
|
testDevices = allDevices.filter(device => {
|
|
28
|
-
|
|
29
|
-
if (status !== OnlineStatus.ONLINE) return false;
|
|
28
|
+
if (device.onlineStatus !== OnlineStatus.ONLINE) return false;
|
|
30
29
|
// Check if device has runtime ability or if it's a common device type
|
|
31
|
-
return device.
|
|
32
|
-
device.
|
|
30
|
+
return device.abilities && (
|
|
31
|
+
device.abilities['Appliance.System.Runtime'] ||
|
|
33
32
|
// Most devices support runtime, so we'll test with any online device
|
|
34
33
|
true
|
|
35
34
|
);
|
|
@@ -32,7 +32,7 @@ const metadata = {
|
|
|
32
32
|
/**
|
|
33
33
|
* Runs all tests for this test type
|
|
34
34
|
* @param {Object} context - Test context object
|
|
35
|
-
* @param {Object} context.manager -
|
|
35
|
+
* @param {Object} context.manager - ManagerMeross instance (already connected)
|
|
36
36
|
* @param {Array<Object>} context.devices - Pre-filtered devices (from CLI selection or auto-discovery)
|
|
37
37
|
* @param {Object} context.options - Test options (timeout, verbose, etc.)
|
|
38
38
|
* @returns {Promise<Array>} Array of test result objects
|
|
@@ -52,8 +52,8 @@ async function runTests(context) {
|
|
|
52
52
|
// Combine and deduplicate
|
|
53
53
|
testDevices = [...toggleXDevices];
|
|
54
54
|
for (const device of toggleDevices) {
|
|
55
|
-
const uuid = device.
|
|
56
|
-
if (!testDevices.find(d =>
|
|
55
|
+
const uuid = device.uuid;
|
|
56
|
+
if (!testDevices.find(d => d.uuid === uuid)) {
|
|
57
57
|
testDevices.push(device);
|
|
58
58
|
}
|
|
59
59
|
}
|
package/cli/tests/test-toggle.js
CHANGED
|
@@ -19,7 +19,7 @@ const metadata = {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Helper function to find all toggle-capable devices
|
|
22
|
-
* @param {Object} manager -
|
|
22
|
+
* @param {Object} manager - ManagerMeross instance
|
|
23
23
|
* @returns {Promise<Array>} Array of toggle devices
|
|
24
24
|
*/
|
|
25
25
|
async function findAllToggleDevices(manager) {
|
|
@@ -29,8 +29,8 @@ async function findAllToggleDevices(manager) {
|
|
|
29
29
|
// Combine and deduplicate
|
|
30
30
|
const allToggleDevices = [...toggleXDevices];
|
|
31
31
|
for (const device of toggleDevices) {
|
|
32
|
-
const uuid = device.
|
|
33
|
-
if (!allToggleDevices.find(d =>
|
|
32
|
+
const uuid = device.uuid;
|
|
33
|
+
if (!allToggleDevices.find(d => d.uuid === uuid)) {
|
|
34
34
|
allToggleDevices.push(device);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -40,7 +40,7 @@ async function findAllToggleDevices(manager) {
|
|
|
40
40
|
/**
|
|
41
41
|
* Runs all tests for toggle/switch devices
|
|
42
42
|
* @param {Object} context - Test context object
|
|
43
|
-
* @param {Object} context.manager -
|
|
43
|
+
* @param {Object} context.manager - ManagerMeross instance (already connected)
|
|
44
44
|
* @param {Array<Object>} context.devices - Pre-filtered devices (from CLI selection or auto-discovery)
|
|
45
45
|
* @param {Object} context.options - Test options (timeout, verbose, etc.)
|
|
46
46
|
* @returns {Promise<Array>} Array of test result objects
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meross-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Command-line interface for controlling Meross smart home devices",
|
|
5
5
|
"author": "Abe Haverkamp",
|
|
6
6
|
"homepage": "https://github.com/Doekse/merossiot#readme",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"chalk": "^4.1.2",
|
|
25
25
|
"commander": "^12.1.0",
|
|
26
26
|
"inquirer": "^8.2.6",
|
|
27
|
-
"meross-iot": "^0.
|
|
27
|
+
"meross-iot": "^0.4.0",
|
|
28
28
|
"ora": "^5.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -50,4 +50,3 @@
|
|
|
50
50
|
"lint:fix": "eslint . --fix"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
|