meross-cli 0.5.0 → 0.6.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 +8 -0
- package/README.md +12 -19
- package/cli/commands/control/params/timer.js +21 -29
- package/cli/commands/control/params/trigger.js +21 -29
- package/cli/commands/info.js +181 -4
- package/cli/tests/test-timer.js +1 -1
- package/cli/tests/test-trigger.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ 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.6.0] - 2026-01-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Enhanced `info` command with normalized capabilities display
|
|
12
|
+
- Displays user-friendly device capabilities using the new `device.capabilities` map
|
|
13
|
+
- Shows channel information and supported features in an organized format
|
|
14
|
+
- Verbose mode support for displaying raw abilities (namespaces) when `MEROSS_VERBOSE=true` is set
|
|
15
|
+
|
|
8
16
|
## [0.5.0] - 2026-01-20
|
|
9
17
|
|
|
10
18
|
### 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.
|
|
26
|
+
npm install -g meross-cli@0.6.0
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Or use via npx:
|
|
@@ -77,6 +77,17 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
|
|
|
77
77
|
|
|
78
78
|
## Changelog
|
|
79
79
|
|
|
80
|
+
### [0.6.0] - 2026-01-20
|
|
81
|
+
|
|
82
|
+
#### Added
|
|
83
|
+
- Enhanced `info` command with normalized capabilities display
|
|
84
|
+
- Displays user-friendly device capabilities using the new `device.capabilities` map
|
|
85
|
+
- Shows channel information and supported features in an organized format
|
|
86
|
+
- Verbose mode support for displaying raw abilities (namespaces) when `MEROSS_VERBOSE=true` is set
|
|
87
|
+
|
|
88
|
+
<details>
|
|
89
|
+
<summary>Older</summary>
|
|
90
|
+
|
|
80
91
|
### [0.5.0] - 2026-01-20
|
|
81
92
|
|
|
82
93
|
#### Changed
|
|
@@ -103,24 +114,6 @@ The CLI supports all devices that are supported by the underlying `meross-iot` l
|
|
|
103
114
|
- Centralized error handler utility (`cli/utils/error-handler.js`) with formatted error messages
|
|
104
115
|
- Enhanced error display with better context and user-friendly formatting
|
|
105
116
|
|
|
106
|
-
<details>
|
|
107
|
-
<summary>Older</summary>
|
|
108
|
-
|
|
109
|
-
### [0.4.0] - 2026-01-19
|
|
110
|
-
|
|
111
|
-
#### Changed
|
|
112
|
-
- **BREAKING**: Updated to use new manager module structure from `meross-iot` v0.5.0
|
|
113
|
-
- Updated to use manager properties (`manager.devices`, `manager.mqtt`, `manager.http`, etc.) instead of direct methods
|
|
114
|
-
- Updated all commands and helpers to use new property-based access patterns
|
|
115
|
-
- **BREAKING**: Updated to use standardized error handling from `meross-iot` v0.5.0
|
|
116
|
-
- Updated to use new `MerossError*` error class names
|
|
117
|
-
- Replaced inline error handling with centralized `handleError()` function
|
|
118
|
-
- All error handling now uses the new error handler utility for consistent, user-friendly formatted messages
|
|
119
|
-
|
|
120
|
-
#### Added
|
|
121
|
-
- Centralized error handler utility (`cli/utils/error-handler.js`) with formatted error messages
|
|
122
|
-
- Enhanced error display with better context and user-friendly formatting
|
|
123
|
-
|
|
124
117
|
### [0.3.0] - 2026-01-16
|
|
125
118
|
|
|
126
119
|
#### Changed
|
|
@@ -163,7 +163,7 @@ async function collectSetTimerXParams(methodMetadata, device) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
|
-
* Collects parameters for
|
|
166
|
+
* Collects parameters for timer.delete with interactive prompts.
|
|
167
167
|
*/
|
|
168
168
|
async function collectDeleteTimerXParams(methodMetadata, device) {
|
|
169
169
|
const params = {};
|
|
@@ -171,10 +171,15 @@ async function collectDeleteTimerXParams(methodMetadata, device) {
|
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
173
|
if (device.timer && typeof device.timer.get === 'function') {
|
|
174
|
+
// Clear cache to force fresh fetch after potential deletions
|
|
175
|
+
if (device._timerxStateByChannel) {
|
|
176
|
+
device._timerxStateByChannel.delete(channel);
|
|
177
|
+
}
|
|
174
178
|
console.log(chalk.dim('Fetching existing timers...'));
|
|
175
179
|
const response = await device.timer.get({ channel });
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
const items = response && response.timerx && Array.isArray(response.timerx) ? response.timerx : [];
|
|
181
|
+
|
|
182
|
+
if (items.length > 0) {
|
|
178
183
|
console.log(chalk.cyan(`\nExisting Timers (Channel ${channel}):`));
|
|
179
184
|
items.forEach((item, index) => {
|
|
180
185
|
const timeMinutes = item.time || 0;
|
|
@@ -200,12 +205,6 @@ async function collectDeleteTimerXParams(methodMetadata, device) {
|
|
|
200
205
|
};
|
|
201
206
|
});
|
|
202
207
|
|
|
203
|
-
choices.push(new inquirer.Separator());
|
|
204
|
-
choices.push({
|
|
205
|
-
name: 'Enter ID Manually',
|
|
206
|
-
value: '__manual__'
|
|
207
|
-
});
|
|
208
|
-
|
|
209
208
|
const selected = await inquirer.prompt([{
|
|
210
209
|
type: 'list',
|
|
211
210
|
name: 'id',
|
|
@@ -213,31 +212,24 @@ async function collectDeleteTimerXParams(methodMetadata, device) {
|
|
|
213
212
|
choices
|
|
214
213
|
}]);
|
|
215
214
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
name: 'id',
|
|
220
|
-
message: 'Timer ID',
|
|
221
|
-
validate: (value) => {
|
|
222
|
-
if (!value || value.trim() === '') {
|
|
223
|
-
return 'ID is required';
|
|
224
|
-
}
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
}]);
|
|
228
|
-
params.timerId = manualAnswer.id;
|
|
229
|
-
} else {
|
|
230
|
-
params.timerId = selected.id;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
params.channel = channel;
|
|
234
|
-
return params;
|
|
215
|
+
params.timerId = selected.id;
|
|
216
|
+
} else {
|
|
217
|
+
throw new Error(`No timers found on channel ${channel}. Nothing to delete.`);
|
|
235
218
|
}
|
|
219
|
+
|
|
220
|
+
params.channel = channel;
|
|
221
|
+
return params;
|
|
236
222
|
}
|
|
237
223
|
} catch (e) {
|
|
238
|
-
//
|
|
224
|
+
// If it's our "no timers" error, re-throw it
|
|
225
|
+
if (e.message && e.message.includes('No timers found')) {
|
|
226
|
+
throw e;
|
|
227
|
+
}
|
|
228
|
+
// If fetch fails, throw error
|
|
229
|
+
throw new Error('Unable to fetch timers from device. Please try again.');
|
|
239
230
|
}
|
|
240
231
|
|
|
232
|
+
// Fallback if timer feature is not available
|
|
241
233
|
return null;
|
|
242
234
|
}
|
|
243
235
|
|
|
@@ -132,7 +132,7 @@ async function collectSetTriggerXParams(methodMetadata, device) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* Collects parameters for
|
|
135
|
+
* Collects parameters for trigger.delete interactively.
|
|
136
136
|
*
|
|
137
137
|
* Displays existing triggers and allows selection from a list, or manual ID entry
|
|
138
138
|
* if no triggers are found. Returns null to fall back to generic collection when
|
|
@@ -148,10 +148,15 @@ async function collectDeleteTriggerXParams(methodMetadata, device) {
|
|
|
148
148
|
|
|
149
149
|
try {
|
|
150
150
|
if (device.trigger && typeof device.trigger.get === 'function') {
|
|
151
|
+
// Clear cache to force fresh fetch after potential deletions
|
|
152
|
+
if (device._triggerxStateByChannel) {
|
|
153
|
+
device._triggerxStateByChannel.delete(channel);
|
|
154
|
+
}
|
|
151
155
|
console.log(chalk.dim('Fetching existing triggers...'));
|
|
152
156
|
const response = await device.trigger.get({ channel });
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
const items = response && response.triggerx && Array.isArray(response.triggerx) ? response.triggerx : [];
|
|
158
|
+
|
|
159
|
+
if (items.length > 0) {
|
|
155
160
|
console.log(chalk.cyan(`\nExisting Triggers (Channel ${channel}):`));
|
|
156
161
|
items.forEach((item, index) => {
|
|
157
162
|
const durationSeconds = item.rule?.duration || 0;
|
|
@@ -172,12 +177,6 @@ async function collectDeleteTriggerXParams(methodMetadata, device) {
|
|
|
172
177
|
};
|
|
173
178
|
});
|
|
174
179
|
|
|
175
|
-
choices.push(new inquirer.Separator());
|
|
176
|
-
choices.push({
|
|
177
|
-
name: 'Enter ID Manually',
|
|
178
|
-
value: '__manual__'
|
|
179
|
-
});
|
|
180
|
-
|
|
181
180
|
const selected = await inquirer.prompt([{
|
|
182
181
|
type: 'list',
|
|
183
182
|
name: 'id',
|
|
@@ -185,31 +184,24 @@ async function collectDeleteTriggerXParams(methodMetadata, device) {
|
|
|
185
184
|
choices
|
|
186
185
|
}]);
|
|
187
186
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
name: 'id',
|
|
192
|
-
message: 'Trigger ID',
|
|
193
|
-
validate: (value) => {
|
|
194
|
-
if (!value || value.trim() === '') {
|
|
195
|
-
return 'ID is required';
|
|
196
|
-
}
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
}]);
|
|
200
|
-
params.triggerId = manualAnswer.id;
|
|
201
|
-
} else {
|
|
202
|
-
params.triggerId = selected.id;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
params.channel = channel;
|
|
206
|
-
return params;
|
|
187
|
+
params.triggerId = selected.id;
|
|
188
|
+
} else {
|
|
189
|
+
throw new Error(`No triggers found on channel ${channel}. Nothing to delete.`);
|
|
207
190
|
}
|
|
191
|
+
|
|
192
|
+
params.channel = channel;
|
|
193
|
+
return params;
|
|
208
194
|
}
|
|
209
195
|
} catch (e) {
|
|
210
|
-
//
|
|
196
|
+
// If it's our "no triggers" error, re-throw it
|
|
197
|
+
if (e.message && e.message.includes('No triggers found')) {
|
|
198
|
+
throw e;
|
|
199
|
+
}
|
|
200
|
+
// If fetch fails, throw error
|
|
201
|
+
throw new Error('Unable to fetch triggers from device. Please try again.');
|
|
211
202
|
}
|
|
212
203
|
|
|
204
|
+
// Fallback if trigger feature is not available
|
|
213
205
|
return null;
|
|
214
206
|
}
|
|
215
207
|
|
package/cli/commands/info.js
CHANGED
|
@@ -177,13 +177,21 @@ function _buildAbilityCategories(abilityNames) {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/**
|
|
180
|
-
* Displays device
|
|
180
|
+
* Displays device abilities (raw namespace list) when verbose mode is enabled.
|
|
181
181
|
*
|
|
182
182
|
* @param {Object} device - Device instance
|
|
183
|
+
* @param {Object} manager - ManagerMeross instance (to check verbose state)
|
|
183
184
|
*/
|
|
184
|
-
function
|
|
185
|
+
function _displayAbilities(device, manager) {
|
|
186
|
+
// Check verbose mode via environment variable or manager logger option
|
|
187
|
+
const isVerbose = process.env.MEROSS_VERBOSE === 'true' ||
|
|
188
|
+
(manager && manager.options && manager.options.logger !== null);
|
|
189
|
+
|
|
190
|
+
if (!isVerbose) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
185
194
|
if (!device.deviceConnected) {
|
|
186
|
-
console.log(`\n${chalk.yellow('Device is not connected. Connect to see capabilities.')}`);
|
|
187
195
|
return;
|
|
188
196
|
}
|
|
189
197
|
|
|
@@ -198,7 +206,7 @@ function _displayCapabilities(device) {
|
|
|
198
206
|
const abilityCount = abilityNames.length;
|
|
199
207
|
const categories = _buildAbilityCategories(abilityNames);
|
|
200
208
|
|
|
201
|
-
console.log(`\n${chalk.bold.underline('
|
|
209
|
+
console.log(`\n${chalk.bold.underline('Abilities (Raw Namespaces)')}`);
|
|
202
210
|
console.log(` Total: ${chalk.cyan(abilityCount)} abilities\n`);
|
|
203
211
|
|
|
204
212
|
Object.entries(categories).forEach(([category, items]) => {
|
|
@@ -215,6 +223,173 @@ function _displayCapabilities(device) {
|
|
|
215
223
|
}
|
|
216
224
|
}
|
|
217
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Displays device capabilities using the normalized capabilities map.
|
|
228
|
+
*
|
|
229
|
+
* @param {Object} device - Device instance
|
|
230
|
+
*/
|
|
231
|
+
function _displayCapabilities(device) {
|
|
232
|
+
if (!device.deviceConnected) {
|
|
233
|
+
console.log(`\n${chalk.yellow('Device is not connected. Connect to see capabilities.')}`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const capabilities = device.capabilities;
|
|
239
|
+
|
|
240
|
+
if (!capabilities) {
|
|
241
|
+
console.log(`\n${chalk.yellow('Capabilities not yet available. Device may need to connect first.')}`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log(`\n${chalk.bold.underline('Capabilities')}`);
|
|
246
|
+
|
|
247
|
+
// Display channel information
|
|
248
|
+
if (capabilities.channels) {
|
|
249
|
+
console.log(`\n ${chalk.white.bold('Channels')}:`);
|
|
250
|
+
console.log(` Count: ${chalk.cyan(capabilities.channels.count)}`);
|
|
251
|
+
console.log(` IDs: ${chalk.cyan(capabilities.channels.ids.join(', '))}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Display feature capabilities
|
|
255
|
+
const featureKeys = Object.keys(capabilities).filter(key => key !== 'channels');
|
|
256
|
+
if (featureKeys.length > 0) {
|
|
257
|
+
console.log(`\n ${chalk.white.bold('Features')}:`);
|
|
258
|
+
featureKeys.forEach(featureKey => {
|
|
259
|
+
const feature = capabilities[featureKey];
|
|
260
|
+
if (feature && feature.supported) {
|
|
261
|
+
let featureInfo = ` ${chalk.green('✓')} ${chalk.bold(featureKey)}`;
|
|
262
|
+
if (feature.channels) {
|
|
263
|
+
featureInfo += ` (channels: ${chalk.cyan(feature.channels.join(', '))})`;
|
|
264
|
+
}
|
|
265
|
+
if (feature.multiChannel) {
|
|
266
|
+
featureInfo += ` ${chalk.gray('[multi-channel]')}`;
|
|
267
|
+
}
|
|
268
|
+
if (feature.rgb || feature.luminance || feature.temperature) {
|
|
269
|
+
const lightFeatures = [];
|
|
270
|
+
if (feature.rgb) {lightFeatures.push('RGB');}
|
|
271
|
+
if (feature.luminance) {lightFeatures.push('brightness');}
|
|
272
|
+
if (feature.temperature) {lightFeatures.push('temperature');}
|
|
273
|
+
featureInfo += ` ${chalk.gray(`[${lightFeatures.join(', ')}]`)}`;
|
|
274
|
+
}
|
|
275
|
+
if (featureKey === 'thermostat') {
|
|
276
|
+
const thermostatFeatures = [];
|
|
277
|
+
if (feature.modeB) {thermostatFeatures.push('ModeB');}
|
|
278
|
+
if (feature.schedule) {thermostatFeatures.push('schedule');}
|
|
279
|
+
if (feature.windowOpened) {thermostatFeatures.push('window detection');}
|
|
280
|
+
if (feature.sensor) {thermostatFeatures.push('sensor selection');}
|
|
281
|
+
if (feature.summerMode) {thermostatFeatures.push('summer mode');}
|
|
282
|
+
if (feature.holdAction) {thermostatFeatures.push('hold action');}
|
|
283
|
+
if (feature.calibration) {thermostatFeatures.push('calibration');}
|
|
284
|
+
if (feature.deadZone) {thermostatFeatures.push('dead zone');}
|
|
285
|
+
if (feature.frost) {thermostatFeatures.push('frost protection');}
|
|
286
|
+
if (feature.overheat) {thermostatFeatures.push('overheat protection');}
|
|
287
|
+
if (thermostatFeatures.length > 0) {
|
|
288
|
+
featureInfo += ` ${chalk.gray(`[${thermostatFeatures.join(', ')}]`)}`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (feature.light !== undefined || feature.spray !== undefined) {
|
|
292
|
+
const diffuserFeatures = [];
|
|
293
|
+
if (feature.light) {diffuserFeatures.push('light');}
|
|
294
|
+
if (feature.spray) {diffuserFeatures.push('spray');}
|
|
295
|
+
featureInfo += ` ${chalk.gray(`[${diffuserFeatures.join(', ')}]`)}`;
|
|
296
|
+
}
|
|
297
|
+
if (feature.multiple || feature.upgrade) {
|
|
298
|
+
const controlFeatures = [];
|
|
299
|
+
if (feature.multiple) {controlFeatures.push('batch');}
|
|
300
|
+
if (feature.upgrade) {controlFeatures.push('upgrade');}
|
|
301
|
+
featureInfo += ` ${chalk.gray(`[${controlFeatures.join(', ')}]`)}`;
|
|
302
|
+
}
|
|
303
|
+
if (feature.subDeviceList || feature.battery) {
|
|
304
|
+
const hubFeatures = [];
|
|
305
|
+
if (feature.subDeviceList) {hubFeatures.push('subdevices');}
|
|
306
|
+
if (feature.battery) {hubFeatures.push('battery');}
|
|
307
|
+
featureInfo += ` ${chalk.gray(`[${hubFeatures.join(', ')}]`)}`;
|
|
308
|
+
}
|
|
309
|
+
if (featureKey === 'presence' && (feature.presenceEvents !== undefined || feature.lux !== undefined || feature.distance !== undefined)) {
|
|
310
|
+
const presenceFeatures = [];
|
|
311
|
+
if (feature.presenceEvents) {presenceFeatures.push('presence events');}
|
|
312
|
+
if (feature.lux) {presenceFeatures.push('LUX');}
|
|
313
|
+
if (feature.distance) {presenceFeatures.push('distance');}
|
|
314
|
+
featureInfo += ` ${chalk.gray(`[${presenceFeatures.join(', ')}]`)}`;
|
|
315
|
+
}
|
|
316
|
+
if (featureKey === 'sensor' && (feature.temperature !== undefined || feature.humidity !== undefined || feature.lux !== undefined || feature.waterLeak !== undefined || feature.smoke !== undefined)) {
|
|
317
|
+
const sensorFeatures = [];
|
|
318
|
+
if (feature.temperature) {sensorFeatures.push('temperature');}
|
|
319
|
+
if (feature.humidity) {sensorFeatures.push('humidity');}
|
|
320
|
+
if (feature.lux) {sensorFeatures.push('LUX');}
|
|
321
|
+
if (feature.waterLeak) {sensorFeatures.push('water leak');}
|
|
322
|
+
if (feature.smoke) {sensorFeatures.push('smoke');}
|
|
323
|
+
featureInfo += ` ${chalk.gray(`[${sensorFeatures.join(', ')}]`)}`;
|
|
324
|
+
}
|
|
325
|
+
console.log(featureInfo);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
} else {
|
|
329
|
+
console.log(`\n ${chalk.gray('No features detected')}`);
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
// Capabilities display is optional, continue without it
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Displays subdevice information for hub devices.
|
|
338
|
+
*
|
|
339
|
+
* @param {Object} device - Device instance (should be a hub)
|
|
340
|
+
*/
|
|
341
|
+
async function _displaySubdevices(device) {
|
|
342
|
+
// Check if device is a hub and has getSubdevices method
|
|
343
|
+
if (!device || typeof device.getSubdevices !== 'function') {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const subdevices = device.getSubdevices();
|
|
348
|
+
if (!subdevices || subdevices.length === 0) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
console.log(`\n${chalk.bold.underline('Subdevices')}`);
|
|
353
|
+
console.log(` Total: ${chalk.cyan(subdevices.length)} subdevice${subdevices.length !== 1 ? 's' : ''}\n`);
|
|
354
|
+
|
|
355
|
+
for (const subdevice of subdevices) {
|
|
356
|
+
console.log(` ${chalk.white.bold(subdevice.name || subdevice.subdeviceId)}`);
|
|
357
|
+
console.log(` Type: ${chalk.cyan(subdevice.type || 'unknown')}`);
|
|
358
|
+
console.log(` ID: ${chalk.cyan(subdevice.subdeviceId)}`);
|
|
359
|
+
|
|
360
|
+
// Display subdevice capabilities if available
|
|
361
|
+
if (subdevice.capabilities) {
|
|
362
|
+
const subCaps = subdevice.capabilities;
|
|
363
|
+
const subFeatureKeys = Object.keys(subCaps).filter(key => key !== 'channels');
|
|
364
|
+
if (subFeatureKeys.length > 0) {
|
|
365
|
+
const subFeatures = [];
|
|
366
|
+
subFeatureKeys.forEach(featureKey => {
|
|
367
|
+
const feature = subCaps[featureKey];
|
|
368
|
+
if (feature && feature.supported) {
|
|
369
|
+
if (featureKey === 'sensor') {
|
|
370
|
+
const sensorTypes = [];
|
|
371
|
+
if (feature.temperature) {sensorTypes.push('temperature');}
|
|
372
|
+
if (feature.humidity) {sensorTypes.push('humidity');}
|
|
373
|
+
if (feature.lux) {sensorTypes.push('LUX');}
|
|
374
|
+
if (feature.waterLeak) {sensorTypes.push('water leak');}
|
|
375
|
+
if (feature.smoke) {sensorTypes.push('smoke');}
|
|
376
|
+
if (sensorTypes.length > 0) {
|
|
377
|
+
subFeatures.push(`sensor [${sensorTypes.join(', ')}]`);
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
subFeatures.push(featureKey);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
if (subFeatures.length > 0) {
|
|
385
|
+
console.log(` Capabilities: ${chalk.gray(subFeatures.join(', '))}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
console.log();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
218
393
|
/**
|
|
219
394
|
* Displays comprehensive device information.
|
|
220
395
|
*
|
|
@@ -242,6 +417,8 @@ async function showDeviceInfo(manager, uuid) {
|
|
|
242
417
|
_displayChannels(device);
|
|
243
418
|
_displayHttpInfo(device);
|
|
244
419
|
_displayCapabilities(device);
|
|
420
|
+
_displayAbilities(device, manager);
|
|
421
|
+
await _displaySubdevices(device);
|
|
245
422
|
}
|
|
246
423
|
|
|
247
424
|
module.exports = { showDeviceInfo };
|
package/cli/tests/test-timer.js
CHANGED
|
@@ -181,7 +181,7 @@ async function runTests(context) {
|
|
|
181
181
|
|
|
182
182
|
let deleteResponse;
|
|
183
183
|
try {
|
|
184
|
-
deleteResponse = await testDevice.
|
|
184
|
+
deleteResponse = await testDevice.timer.delete({ timerId: createdTimerId, channel: 0 });
|
|
185
185
|
} catch (deleteError) {
|
|
186
186
|
results.push({
|
|
187
187
|
name: 'should delete timer',
|
|
@@ -176,7 +176,7 @@ async function runTests(context) {
|
|
|
176
176
|
// Test 2: Delete the trigger
|
|
177
177
|
if (createdTriggerId) {
|
|
178
178
|
try {
|
|
179
|
-
const deleteResponse = await testDevice.
|
|
179
|
+
const deleteResponse = await testDevice.trigger.delete({ triggerId: createdTriggerId, channel: 0 });
|
|
180
180
|
|
|
181
181
|
// Check what the DELETE response contains
|
|
182
182
|
const deleteResponseError = deleteResponse?.error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meross-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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.7.0",
|
|
28
28
|
"ora": "^5.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|