meross-iot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +153 -0
  4. package/index.d.ts +2344 -0
  5. package/index.js +131 -0
  6. package/lib/controller/device.js +1317 -0
  7. package/lib/controller/features/alarm-feature.js +89 -0
  8. package/lib/controller/features/child-lock-feature.js +61 -0
  9. package/lib/controller/features/config-feature.js +54 -0
  10. package/lib/controller/features/consumption-feature.js +210 -0
  11. package/lib/controller/features/control-feature.js +62 -0
  12. package/lib/controller/features/diffuser-feature.js +411 -0
  13. package/lib/controller/features/digest-timer-feature.js +22 -0
  14. package/lib/controller/features/digest-trigger-feature.js +22 -0
  15. package/lib/controller/features/dnd-feature.js +79 -0
  16. package/lib/controller/features/electricity-feature.js +144 -0
  17. package/lib/controller/features/encryption-feature.js +259 -0
  18. package/lib/controller/features/garage-feature.js +337 -0
  19. package/lib/controller/features/hub-feature.js +687 -0
  20. package/lib/controller/features/light-feature.js +408 -0
  21. package/lib/controller/features/presence-sensor-feature.js +297 -0
  22. package/lib/controller/features/roller-shutter-feature.js +456 -0
  23. package/lib/controller/features/runtime-feature.js +74 -0
  24. package/lib/controller/features/screen-feature.js +67 -0
  25. package/lib/controller/features/sensor-history-feature.js +47 -0
  26. package/lib/controller/features/smoke-config-feature.js +50 -0
  27. package/lib/controller/features/spray-feature.js +166 -0
  28. package/lib/controller/features/system-feature.js +269 -0
  29. package/lib/controller/features/temp-unit-feature.js +55 -0
  30. package/lib/controller/features/thermostat-feature.js +804 -0
  31. package/lib/controller/features/timer-feature.js +507 -0
  32. package/lib/controller/features/toggle-feature.js +223 -0
  33. package/lib/controller/features/trigger-feature.js +333 -0
  34. package/lib/controller/hub-device.js +185 -0
  35. package/lib/controller/subdevice.js +1537 -0
  36. package/lib/device-factory.js +463 -0
  37. package/lib/error-budget.js +138 -0
  38. package/lib/http-api.js +766 -0
  39. package/lib/manager.js +1609 -0
  40. package/lib/model/channel-info.js +79 -0
  41. package/lib/model/constants.js +119 -0
  42. package/lib/model/enums.js +819 -0
  43. package/lib/model/exception.js +363 -0
  44. package/lib/model/http/device.js +215 -0
  45. package/lib/model/http/error-codes.js +121 -0
  46. package/lib/model/http/exception.js +151 -0
  47. package/lib/model/http/subdevice.js +133 -0
  48. package/lib/model/push/alarm.js +112 -0
  49. package/lib/model/push/bind.js +97 -0
  50. package/lib/model/push/common.js +282 -0
  51. package/lib/model/push/diffuser-light.js +100 -0
  52. package/lib/model/push/diffuser-spray.js +83 -0
  53. package/lib/model/push/factory.js +229 -0
  54. package/lib/model/push/generic.js +115 -0
  55. package/lib/model/push/hub-battery.js +59 -0
  56. package/lib/model/push/hub-mts100-all.js +64 -0
  57. package/lib/model/push/hub-mts100-mode.js +59 -0
  58. package/lib/model/push/hub-mts100-temperature.js +62 -0
  59. package/lib/model/push/hub-online.js +59 -0
  60. package/lib/model/push/hub-sensor-alert.js +61 -0
  61. package/lib/model/push/hub-sensor-all.js +59 -0
  62. package/lib/model/push/hub-sensor-smoke.js +110 -0
  63. package/lib/model/push/hub-sensor-temphum.js +62 -0
  64. package/lib/model/push/hub-subdevicelist.js +50 -0
  65. package/lib/model/push/hub-togglex.js +60 -0
  66. package/lib/model/push/index.js +81 -0
  67. package/lib/model/push/online.js +53 -0
  68. package/lib/model/push/presence-study.js +61 -0
  69. package/lib/model/push/sensor-latestx.js +106 -0
  70. package/lib/model/push/timerx.js +63 -0
  71. package/lib/model/push/togglex.js +78 -0
  72. package/lib/model/push/triggerx.js +62 -0
  73. package/lib/model/push/unbind.js +34 -0
  74. package/lib/model/push/water-leak.js +107 -0
  75. package/lib/model/states/diffuser-light-state.js +119 -0
  76. package/lib/model/states/diffuser-spray-state.js +58 -0
  77. package/lib/model/states/garage-door-state.js +71 -0
  78. package/lib/model/states/index.js +38 -0
  79. package/lib/model/states/light-state.js +134 -0
  80. package/lib/model/states/presence-sensor-state.js +239 -0
  81. package/lib/model/states/roller-shutter-state.js +82 -0
  82. package/lib/model/states/spray-state.js +58 -0
  83. package/lib/model/states/thermostat-state.js +297 -0
  84. package/lib/model/states/timer-state.js +192 -0
  85. package/lib/model/states/toggle-state.js +105 -0
  86. package/lib/model/states/trigger-state.js +155 -0
  87. package/lib/subscription.js +587 -0
  88. package/lib/utilities/conversion.js +62 -0
  89. package/lib/utilities/debug.js +165 -0
  90. package/lib/utilities/mqtt.js +152 -0
  91. package/lib/utilities/network.js +53 -0
  92. package/lib/utilities/options.js +64 -0
  93. package/lib/utilities/request-queue.js +161 -0
  94. package/lib/utilities/ssid.js +37 -0
  95. package/lib/utilities/state-changes.js +66 -0
  96. package/lib/utilities/stats.js +687 -0
  97. package/lib/utilities/timer.js +310 -0
  98. package/lib/utilities/trigger.js +286 -0
  99. package/package.json +73 -0
@@ -0,0 +1,456 @@
1
+ 'use strict';
2
+
3
+ const RollerShutterState = require('../../model/states/roller-shutter-state');
4
+ const { RollerShutterStatus } = require('../../model/enums');
5
+ const { normalizeChannel } = require('../../utilities/options');
6
+
7
+ /**
8
+ * Roller shutter feature module.
9
+ * Provides control over roller shutter/blind position and movement state.
10
+ */
11
+ module.exports = {
12
+ /**
13
+ * Sets the roller shutter position.
14
+ *
15
+ * @param {Object} options - Roller shutter position options
16
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
17
+ * @param {number} options.position - Position value (0-100 for open/close, -1 for stop)
18
+ * @returns {Promise<Object>} Response from the device containing the updated position
19
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
20
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
21
+ */
22
+ async setRollerShutterPosition(options = {}) {
23
+ if (options.position === undefined) {
24
+ throw new Error('position is required');
25
+ }
26
+ const channel = normalizeChannel(options);
27
+ const payload = { 'position': { position: options.position, channel } };
28
+ const response = await this.publishMessage('SET', 'Appliance.RollerShutter.Position', payload);
29
+
30
+ if (response && response.position) {
31
+ this._updateRollerShutterPosition(response.position, 'response');
32
+ this._lastFullUpdateTimestamp = Date.now();
33
+ } else {
34
+ this._updateRollerShutterPosition([{ channel, position: options.position }], 'response');
35
+ this._lastFullUpdateTimestamp = Date.now();
36
+ }
37
+
38
+ return response;
39
+ },
40
+
41
+ /**
42
+ * Opens the roller shutter (moves to position 100).
43
+ *
44
+ * Convenience method that calls {@link setRollerShutterPosition} with position 100.
45
+ *
46
+ * @param {Object} [options={}] - Open options
47
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
48
+ * @returns {Promise<Object>} Response from the device
49
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
50
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
51
+ * @see closeRollerShutter
52
+ * @see stopRollerShutter
53
+ */
54
+ async setRollerShutterUp(options = {}) {
55
+ return await this.setRollerShutterPosition({ ...options, position: 100 });
56
+ },
57
+
58
+ /**
59
+ * Closes the roller shutter (moves to position 0).
60
+ *
61
+ * Convenience method that calls {@link setRollerShutterPosition} with position 0.
62
+ *
63
+ * @param {Object} [options={}] - Close options
64
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
65
+ * @returns {Promise<Object>} Response from the device
66
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
67
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
68
+ * @see openRollerShutter
69
+ * @see stopRollerShutter
70
+ */
71
+ async setRollerShutterDown(options = {}) {
72
+ return await this.setRollerShutterPosition({ ...options, position: 0 });
73
+ },
74
+
75
+ /**
76
+ * Stops the roller shutter movement.
77
+ *
78
+ * Convenience method that calls {@link setRollerShutterPosition} with position -1.
79
+ *
80
+ * @param {Object} [options={}] - Stop options
81
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
82
+ * @returns {Promise<Object>} Response from the device
83
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
84
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
85
+ * @see openRollerShutter
86
+ * @see closeRollerShutter
87
+ */
88
+ async setRollerShutterStop(options = {}) {
89
+ return await this.setRollerShutterPosition({ ...options, position: -1 });
90
+ },
91
+
92
+ /**
93
+ * Opens the roller shutter (moves to position 100).
94
+ *
95
+ * Alias for {@link setRollerShutterUp}.
96
+ *
97
+ * @param {Object} [options={}] - Open options
98
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
99
+ * @returns {Promise<Object>} Response from the device
100
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
101
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
102
+ * @see closeRollerShutter
103
+ * @see stopRollerShutter
104
+ */
105
+ async openRollerShutter(options = {}) {
106
+ return await this.setRollerShutterPosition({ ...options, position: 100 });
107
+ },
108
+
109
+ /**
110
+ * Closes the roller shutter (moves to position 0).
111
+ *
112
+ * Alias for {@link setRollerShutterDown}.
113
+ *
114
+ * @param {Object} [options={}] - Close options
115
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
116
+ * @returns {Promise<Object>} Response from the device
117
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
118
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
119
+ * @see openRollerShutter
120
+ * @see stopRollerShutter
121
+ */
122
+ async closeRollerShutter(options = {}) {
123
+ return await this.setRollerShutterPosition({ ...options, position: 0 });
124
+ },
125
+
126
+ /**
127
+ * Stops the roller shutter movement.
128
+ *
129
+ * Alias for {@link setRollerShutterStop}.
130
+ *
131
+ * @param {Object} [options={}] - Stop options
132
+ * @param {number} [options.channel=0] - Channel to control (default: 0)
133
+ * @returns {Promise<Object>} Response from the device
134
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
135
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
136
+ * @see openRollerShutter
137
+ * @see closeRollerShutter
138
+ */
139
+ async stopRollerShutter(options = {}) {
140
+ return await this.setRollerShutterPosition({ ...options, position: -1 });
141
+ },
142
+
143
+
144
+ /**
145
+ * Gets the current roller shutter state from the device.
146
+ *
147
+ * Use {@link getCachedRollerShutterState} to get cached state without making a request.
148
+ * @param {Object} [options={}] - Get options
149
+ * @returns {Promise<Object>} Response containing roller shutter state with `state` array
150
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
151
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
152
+ */
153
+ async getRollerShutterState(_options = {}) {
154
+ const response = await this.publishMessage('GET', 'Appliance.RollerShutter.State', {});
155
+ if (response && response.state) {
156
+ this._updateRollerShutterState(response.state, 'response');
157
+ this._lastFullUpdateTimestamp = Date.now();
158
+ }
159
+ return response;
160
+ },
161
+
162
+ /**
163
+ * Gets the current roller shutter position from the device.
164
+ *
165
+ * Use {@link getRollerShutterPosition} (getter) to get cached position without making a request.
166
+ * @param {Object} [options={}] - Get options
167
+ * @returns {Promise<Object>} Response containing roller shutter position with `position` array
168
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
169
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
170
+ */
171
+ async getRollerShutterPosition(_options = {}) {
172
+ const response = await this.publishMessage('GET', 'Appliance.RollerShutter.Position', {});
173
+ if (response && response.position) {
174
+ this._updateRollerShutterPosition(response.position, 'response');
175
+ }
176
+ return response;
177
+ },
178
+
179
+ /**
180
+ * Gets the roller shutter configuration from the device.
181
+ *
182
+ * Use {@link getRollerShutterConfig} (getter) to get cached config without making a request.
183
+ * @param {Object} [options={}] - Get options
184
+ * @returns {Promise<Object>} Response containing roller shutter config with `config` array
185
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
186
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
187
+ */
188
+ async getRollerShutterConfig(_options = {}) {
189
+ const response = await this.publishMessage('GET', 'Appliance.RollerShutter.Config', {});
190
+ if (response && response.config) {
191
+ this._updateRollerShutterConfig(response.config);
192
+ }
193
+ return response;
194
+ },
195
+
196
+ /**
197
+ * Controls the roller shutter configuration.
198
+ *
199
+ * @param {Object} options - Roller shutter config options
200
+ * @param {Object|Array} options.config - Configuration object or array of configuration objects
201
+ * @returns {Promise<Object>} Response from the device
202
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
203
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
204
+ */
205
+ async setRollerShutterConfig(options = {}) {
206
+ if (!options.config) {
207
+ throw new Error('config is required');
208
+ }
209
+ const payload = { config: options.config };
210
+ const response = await this.publishMessage('SET', 'Appliance.RollerShutter.Config', payload);
211
+ if (response && response.config) {
212
+ this._updateRollerShutterConfig(response.config);
213
+ } else if (options.config) {
214
+ const configArray = Array.isArray(options.config) ? options.config : [options.config];
215
+ this._updateRollerShutterConfig(configArray);
216
+ }
217
+ return response;
218
+ },
219
+
220
+ /**
221
+ * Gets the roller shutter adjustment settings from the device.
222
+ * @param {Object} [options={}] - Get options
223
+ * @returns {Promise<Object>} Response containing roller shutter adjustment data
224
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
225
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
226
+ */
227
+ async getRollerShutterAdjust(_options = {}) {
228
+ return await this.publishMessage('GET', 'Appliance.RollerShutter.Adjust', {});
229
+ },
230
+
231
+ /**
232
+ * Gets the cached roller shutter state for the specified channel.
233
+ *
234
+ * Returns cached state without making a request. Use {@link getRollerShutterState} to fetch
235
+ * fresh state from the device. State is automatically updated when commands are sent or
236
+ * push notifications are received.
237
+ *
238
+ * @param {number} [channel=0] - Channel to get state for (default: 0)
239
+ * @returns {import('../lib/model/states/roller-shutter-state').RollerShutterState|undefined} Cached roller shutter state or undefined if not available
240
+ * @throws {Error} If state has not been initialized (call refreshState() first)
241
+ */
242
+ getCachedRollerShutterState(channel = 0) {
243
+ this.validateState();
244
+ return this._rollerShutterStateByChannel.get(channel);
245
+ },
246
+
247
+ /**
248
+ * Gets the roller shutter state value for the specified channel (cached).
249
+ *
250
+ * Returns the status enum (IDLE, OPENING, CLOSING) from cached state. Use {@link getRawRollerShutterState}
251
+ * to get the raw numeric value, or {@link getRollerShutterState} (async method) to fetch from device.
252
+ *
253
+ * @param {number} [channel=0] - Channel to get state for (default: 0)
254
+ * @returns {import('../lib/enums').RollerShutterStatus|undefined} RollerShutterStatus enum object (e.g., RollerShutterStatus.IDLE) or undefined if not available
255
+ * @throws {Error} If state has not been initialized (call refreshState() first)
256
+ * @see getRawRollerShutterState
257
+ */
258
+ getRollerShutterState(channel = 0) {
259
+ this.validateState();
260
+ const state = this._rollerShutterStateByChannel.get(channel);
261
+ if (state && state.state !== undefined && state.state !== null) {
262
+ const enumKey = Object.keys(RollerShutterStatus).find(key => RollerShutterStatus[key] === state.state);
263
+ return enumKey ? RollerShutterStatus[enumKey] : undefined;
264
+ }
265
+ return undefined;
266
+ },
267
+
268
+ /**
269
+ * Gets the raw numeric roller shutter state value for the specified channel (cached).
270
+ *
271
+ * Returns the raw numeric status value. For enum object, use {@link getRollerShutterState} instead.
272
+ *
273
+ * @param {number} [channel=0] - Channel to get state for (default: 0)
274
+ * @returns {number|undefined} Raw numeric state value or undefined if not available
275
+ * @throws {Error} If state has not been initialized (call refreshState() first)
276
+ * @see getRollerShutterState
277
+ */
278
+ getRawRollerShutterState(channel = 0) {
279
+ this.validateState();
280
+ const state = this._rollerShutterStateByChannel.get(channel);
281
+ if (state) {
282
+ return state.state;
283
+ }
284
+ return undefined;
285
+ },
286
+
287
+ /**
288
+ * Gets the roller shutter position for the specified channel (cached).
289
+ *
290
+ * Returns position (0-100) from cached state. Use {@link getRollerShutterPosition} (async method)
291
+ * to fetch fresh position from the device.
292
+ *
293
+ * @param {number} [channel=0] - Channel to get position for (default: 0)
294
+ * @returns {number|undefined} Position value (0-100, where 0 is closed and 100 is fully open) or undefined if not available
295
+ * @throws {Error} If state has not been initialized (call refreshState() first)
296
+ */
297
+ getRollerShutterPosition(channel = 0) {
298
+ this.validateState();
299
+ const position = this._rollerShutterPositionByChannel.get(channel);
300
+ if (position !== undefined && position !== null) {
301
+ return position;
302
+ }
303
+ const state = this._rollerShutterStateByChannel.get(channel);
304
+ if (state) {
305
+ return state.position;
306
+ }
307
+ return undefined;
308
+ },
309
+
310
+ /**
311
+ * Gets the roller shutter configuration for the specified channel (cached).
312
+ *
313
+ * Returns cached configuration without making a request. Use {@link getRollerShutterConfig} (async method)
314
+ * to fetch fresh configuration from the device.
315
+ *
316
+ * @param {number} [channel=0] - Channel to get config for (default: 0)
317
+ * @returns {Object|undefined} Roller shutter config or undefined if not available
318
+ * @throws {Error} If state has not been initialized (call refreshState() first)
319
+ */
320
+ getRollerShutterConfig(channel = 0) {
321
+ this.validateState();
322
+ return this._rollerShutterConfigByChannel.get(channel);
323
+ },
324
+
325
+ /**
326
+ * Updates the cached roller shutter state from state data.
327
+ *
328
+ * Called automatically when roller shutter push notifications are received or commands complete.
329
+ * Handles both single objects and arrays of state data.
330
+ *
331
+ * @param {Object|Array} stateData - State data (single object or array)
332
+ * @private
333
+ */
334
+ _updateRollerShutterState(stateData, source = 'response') {
335
+ if (!stateData) {return;}
336
+
337
+ const stateArray = Array.isArray(stateData) ? stateData : [stateData];
338
+
339
+ for (const stateItem of stateArray) {
340
+ const channelIndex = stateItem.channel;
341
+ if (channelIndex === undefined || channelIndex === null) {continue;}
342
+
343
+ const oldState = this._rollerShutterStateByChannel.get(channelIndex);
344
+ const oldValue = oldState ? {
345
+ state: oldState.state,
346
+ position: oldState.position
347
+ } : undefined;
348
+
349
+ let state = this._rollerShutterStateByChannel.get(channelIndex);
350
+ if (!state) {
351
+ state = new RollerShutterState(stateItem);
352
+ this._rollerShutterStateByChannel.set(channelIndex, state);
353
+ } else {
354
+ state.update(stateItem);
355
+ }
356
+
357
+ const newValue = {};
358
+ if (oldValue === undefined || oldValue.state !== state.state) {
359
+ newValue.state = state.state;
360
+ }
361
+ if (oldValue === undefined || oldValue.position !== state.position) {
362
+ newValue.position = state.position;
363
+ }
364
+
365
+ if (Object.keys(newValue).length > 0) {
366
+ this.emit('stateChange', {
367
+ type: 'rollerShutter',
368
+ channel: channelIndex,
369
+ value: newValue,
370
+ oldValue,
371
+ source,
372
+ timestamp: Date.now()
373
+ });
374
+ }
375
+ }
376
+ },
377
+
378
+ /**
379
+ * Updates the cached roller shutter position from position data.
380
+ *
381
+ * Called automatically when roller shutter position responses are received. Updates both
382
+ * the position cache and the state cache.
383
+ *
384
+ * @param {Object|Array} positionData - Position data (single object or array)
385
+ * @private
386
+ */
387
+ _updateRollerShutterPosition(positionData, source = 'response') {
388
+ if (!positionData) {return;}
389
+
390
+ const positionArray = Array.isArray(positionData) ? positionData : [positionData];
391
+
392
+ for (const positionItem of positionArray) {
393
+ const channelIndex = positionItem.channel;
394
+ if (channelIndex === undefined || channelIndex === null) {continue;}
395
+
396
+ const oldPosition = this._rollerShutterPositionByChannel.get(channelIndex);
397
+ const oldState = this._rollerShutterStateByChannel.get(channelIndex);
398
+ const oldValue = oldState ? {
399
+ state: oldState.state,
400
+ position: oldState.position
401
+ } : (oldPosition !== undefined ? { position: oldPosition } : undefined);
402
+
403
+ this._rollerShutterPositionByChannel.set(channelIndex, positionItem.position);
404
+
405
+ let state = this._rollerShutterStateByChannel.get(channelIndex);
406
+ if (!state) {
407
+ state = new RollerShutterState(positionItem);
408
+ this._rollerShutterStateByChannel.set(channelIndex, state);
409
+ } else {
410
+ state.update(positionItem);
411
+ }
412
+
413
+ const newValue = {};
414
+ if (oldValue === undefined || oldValue.state !== state.state) {
415
+ newValue.state = state.state;
416
+ }
417
+ if (oldValue === undefined || oldValue.position !== state.position) {
418
+ newValue.position = state.position;
419
+ }
420
+
421
+ if (Object.keys(newValue).length > 0) {
422
+ this.emit('stateChange', {
423
+ type: 'rollerShutter',
424
+ channel: channelIndex,
425
+ value: newValue,
426
+ oldValue,
427
+ source,
428
+ timestamp: Date.now()
429
+ });
430
+ }
431
+ }
432
+ },
433
+
434
+ /**
435
+ * Updates the cached roller shutter configuration from config data.
436
+ *
437
+ * Called automatically when roller shutter configuration responses are received.
438
+ * Handles both single objects and arrays of config data.
439
+ *
440
+ * @param {Object|Array} configData - Config data (single object or array)
441
+ * @private
442
+ */
443
+ _updateRollerShutterConfig(configData) {
444
+ if (!configData) {return;}
445
+
446
+ const configArray = Array.isArray(configData) ? configData : [configData];
447
+
448
+ for (const configItem of configArray) {
449
+ const channelIndex = configItem.channel;
450
+ if (channelIndex === undefined || channelIndex === null) {continue;}
451
+
452
+ this._rollerShutterConfigByChannel.set(channelIndex, configItem);
453
+ }
454
+ }
455
+ };
456
+
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Runtime feature module.
5
+ * Provides access to device runtime information such as uptime and system statistics.
6
+ */
7
+ module.exports = {
8
+ /**
9
+ * Initializes runtime info storage.
10
+ *
11
+ * Called lazily to ensure the runtime info object exists before use.
12
+ *
13
+ * @private
14
+ */
15
+ _initializeRuntimeInfo() {
16
+ if (this._runtimeInfo === undefined) {
17
+ this._runtimeInfo = {};
18
+ }
19
+ },
20
+
21
+ /**
22
+ * Gets the latest runtime information from the device.
23
+ *
24
+ * Runtime information may vary over time as Meross adds, removes, or changes runtime
25
+ * data fields in firmware updates. Use {@link cachedSystemRuntimeInfo} to access cached
26
+ * runtime info without making a request.
27
+ *
28
+ * @returns {Promise<Object>} Runtime information object
29
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
30
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
31
+ */
32
+ async updateRuntimeInfo() {
33
+ this._initializeRuntimeInfo();
34
+ const result = await this.publishMessage('GET', 'Appliance.System.Runtime', {});
35
+ const data = result && result.runtime ? result.runtime : {};
36
+ this._runtimeInfo = data;
37
+ return data;
38
+ },
39
+
40
+ /**
41
+ * Gets the cached runtime information.
42
+ *
43
+ * Returns the most recently fetched runtime info without making a request. For fresh
44
+ * data, use {@link updateRuntimeInfo} instead.
45
+ *
46
+ * @returns {Object|null} Cached runtime info or null if not yet fetched
47
+ */
48
+ get cachedSystemRuntimeInfo() {
49
+ this._initializeRuntimeInfo();
50
+ return this._runtimeInfo;
51
+ },
52
+
53
+ /**
54
+ * Refreshes device state including runtime information.
55
+ *
56
+ * Calls the base refreshState implementation (getSystemAllData) and then updates
57
+ * runtime info to ensure all state is current.
58
+ *
59
+ * @returns {Promise<void>}
60
+ * @throws {import('../lib/errors/errors').UnknownDeviceTypeError} If device does not support refreshState
61
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
62
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
63
+ */
64
+ async refreshState() {
65
+ if (typeof this.getSystemAllData === 'function') {
66
+ await this.getSystemAllData();
67
+ } else {
68
+ const { UnknownDeviceTypeError } = require('../../model/exception');
69
+ throw new UnknownDeviceTypeError('Device does not support refreshState()', this.deviceType);
70
+ }
71
+ await this.updateRuntimeInfo();
72
+ }
73
+ };
74
+
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const { normalizeChannel } = require('../../utilities/options');
4
+
5
+ /**
6
+ * Screen feature module.
7
+ * Provides control over device screen brightness settings for different operational states.
8
+ */
9
+ module.exports = {
10
+ /**
11
+ * Gets the screen brightness configuration from the device.
12
+ *
13
+ * @param {Object} [options={}] - Get options
14
+ * @param {number} [options.channel=0] - Channel to get brightness for (default: 0)
15
+ * @param {string} [options.subId=null] - Optional subdevice ID
16
+ * @returns {Promise<Object>} Response containing brightness configuration with `brightness` array
17
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
18
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
19
+ */
20
+ async getScreenBrightness(options = {}) {
21
+ const channel = normalizeChannel(options);
22
+ const payload = {
23
+ brightness: [{
24
+ channel
25
+ }]
26
+ };
27
+ if (options.subId) {
28
+ payload.brightness[0].subId = options.subId;
29
+ }
30
+ return await this.publishMessage('GET', 'Appliance.Control.Screen.Brightness', payload);
31
+ },
32
+
33
+ /**
34
+ * Controls the screen brightness configuration.
35
+ *
36
+ * Allows setting different brightness levels for standby, operation, and standby view modes.
37
+ *
38
+ * @param {Object} options - Screen brightness options
39
+ * @param {Object|Array<Object>} [options.brightnessData] - Brightness data object or array of brightness items (if provided, used directly)
40
+ * @param {number} [options.channel] - Channel to configure
41
+ * @param {string} [options.subId] - Optional subdevice ID
42
+ * @param {number} [options.standby] - Standby brightness level
43
+ * @param {number} [options.operation] - Operation brightness level
44
+ * @param {number} [options.standbyView] - Standby view brightness level
45
+ * @returns {Promise<Object>} Response from the device
46
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
47
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
48
+ */
49
+ async setScreenBrightness(options = {}) {
50
+ let brightnessData;
51
+ if (options.brightnessData) {
52
+ brightnessData = Array.isArray(options.brightnessData) ? options.brightnessData : [options.brightnessData];
53
+ } else {
54
+ const channel = normalizeChannel(options);
55
+ brightnessData = [{
56
+ channel,
57
+ subId: options.subId,
58
+ standby: options.standby,
59
+ operation: options.operation,
60
+ standbyView: options.standbyView
61
+ }];
62
+ }
63
+ const payload = { brightness: brightnessData };
64
+ return await this.publishMessage('SET', 'Appliance.Control.Screen.Brightness', payload);
65
+ }
66
+ };
67
+
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ const { normalizeChannel, validateRequired } = require('../../utilities/options');
4
+
5
+ /**
6
+ * Sensor history feature module.
7
+ * Provides access to historical sensor data and the ability to delete stored history.
8
+ */
9
+ module.exports = {
10
+ /**
11
+ * Gets sensor history data from the device.
12
+ *
13
+ * @param {Object} options - Get options
14
+ * @param {number} [options.channel=0] - Channel to get history for (default: 0)
15
+ * @param {number} options.capacity - Data collection type (see API docs for capacity values)
16
+ * @returns {Promise<Object>} Response containing sensor history data with `history` array
17
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
18
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
19
+ */
20
+ async getSensorHistory(options = {}) {
21
+ const channel = normalizeChannel(options);
22
+ validateRequired(options, ['capacity']);
23
+ const payload = {
24
+ history: [{
25
+ channel,
26
+ capacity: options.capacity
27
+ }]
28
+ };
29
+ return await this.publishMessage('GET', 'Appliance.Control.Sensor.History', payload);
30
+ },
31
+
32
+ /**
33
+ * Deletes sensor history data from the device.
34
+ *
35
+ * @param {Object|Array<Object>} historyData - History data object or array of history items
36
+ * @param {number} [historyData.channel] - Channel to delete history for
37
+ * @param {number} [historyData.capacity] - Data collection type to delete
38
+ * @returns {Promise<Object>} Response from the device
39
+ * @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
40
+ * @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
41
+ */
42
+ async deleteSensorHistory(historyData) {
43
+ const payload = { history: Array.isArray(historyData) ? historyData : [historyData] };
44
+ return await this.publishMessage('DELETE', 'Appliance.Control.Sensor.History', payload);
45
+ }
46
+ };
47
+