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.
- package/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/index.d.ts +2344 -0
- package/index.js +131 -0
- package/lib/controller/device.js +1317 -0
- package/lib/controller/features/alarm-feature.js +89 -0
- package/lib/controller/features/child-lock-feature.js +61 -0
- package/lib/controller/features/config-feature.js +54 -0
- package/lib/controller/features/consumption-feature.js +210 -0
- package/lib/controller/features/control-feature.js +62 -0
- package/lib/controller/features/diffuser-feature.js +411 -0
- package/lib/controller/features/digest-timer-feature.js +22 -0
- package/lib/controller/features/digest-trigger-feature.js +22 -0
- package/lib/controller/features/dnd-feature.js +79 -0
- package/lib/controller/features/electricity-feature.js +144 -0
- package/lib/controller/features/encryption-feature.js +259 -0
- package/lib/controller/features/garage-feature.js +337 -0
- package/lib/controller/features/hub-feature.js +687 -0
- package/lib/controller/features/light-feature.js +408 -0
- package/lib/controller/features/presence-sensor-feature.js +297 -0
- package/lib/controller/features/roller-shutter-feature.js +456 -0
- package/lib/controller/features/runtime-feature.js +74 -0
- package/lib/controller/features/screen-feature.js +67 -0
- package/lib/controller/features/sensor-history-feature.js +47 -0
- package/lib/controller/features/smoke-config-feature.js +50 -0
- package/lib/controller/features/spray-feature.js +166 -0
- package/lib/controller/features/system-feature.js +269 -0
- package/lib/controller/features/temp-unit-feature.js +55 -0
- package/lib/controller/features/thermostat-feature.js +804 -0
- package/lib/controller/features/timer-feature.js +507 -0
- package/lib/controller/features/toggle-feature.js +223 -0
- package/lib/controller/features/trigger-feature.js +333 -0
- package/lib/controller/hub-device.js +185 -0
- package/lib/controller/subdevice.js +1537 -0
- package/lib/device-factory.js +463 -0
- package/lib/error-budget.js +138 -0
- package/lib/http-api.js +766 -0
- package/lib/manager.js +1609 -0
- package/lib/model/channel-info.js +79 -0
- package/lib/model/constants.js +119 -0
- package/lib/model/enums.js +819 -0
- package/lib/model/exception.js +363 -0
- package/lib/model/http/device.js +215 -0
- package/lib/model/http/error-codes.js +121 -0
- package/lib/model/http/exception.js +151 -0
- package/lib/model/http/subdevice.js +133 -0
- package/lib/model/push/alarm.js +112 -0
- package/lib/model/push/bind.js +97 -0
- package/lib/model/push/common.js +282 -0
- package/lib/model/push/diffuser-light.js +100 -0
- package/lib/model/push/diffuser-spray.js +83 -0
- package/lib/model/push/factory.js +229 -0
- package/lib/model/push/generic.js +115 -0
- package/lib/model/push/hub-battery.js +59 -0
- package/lib/model/push/hub-mts100-all.js +64 -0
- package/lib/model/push/hub-mts100-mode.js +59 -0
- package/lib/model/push/hub-mts100-temperature.js +62 -0
- package/lib/model/push/hub-online.js +59 -0
- package/lib/model/push/hub-sensor-alert.js +61 -0
- package/lib/model/push/hub-sensor-all.js +59 -0
- package/lib/model/push/hub-sensor-smoke.js +110 -0
- package/lib/model/push/hub-sensor-temphum.js +62 -0
- package/lib/model/push/hub-subdevicelist.js +50 -0
- package/lib/model/push/hub-togglex.js +60 -0
- package/lib/model/push/index.js +81 -0
- package/lib/model/push/online.js +53 -0
- package/lib/model/push/presence-study.js +61 -0
- package/lib/model/push/sensor-latestx.js +106 -0
- package/lib/model/push/timerx.js +63 -0
- package/lib/model/push/togglex.js +78 -0
- package/lib/model/push/triggerx.js +62 -0
- package/lib/model/push/unbind.js +34 -0
- package/lib/model/push/water-leak.js +107 -0
- package/lib/model/states/diffuser-light-state.js +119 -0
- package/lib/model/states/diffuser-spray-state.js +58 -0
- package/lib/model/states/garage-door-state.js +71 -0
- package/lib/model/states/index.js +38 -0
- package/lib/model/states/light-state.js +134 -0
- package/lib/model/states/presence-sensor-state.js +239 -0
- package/lib/model/states/roller-shutter-state.js +82 -0
- package/lib/model/states/spray-state.js +58 -0
- package/lib/model/states/thermostat-state.js +297 -0
- package/lib/model/states/timer-state.js +192 -0
- package/lib/model/states/toggle-state.js +105 -0
- package/lib/model/states/trigger-state.js +155 -0
- package/lib/subscription.js +587 -0
- package/lib/utilities/conversion.js +62 -0
- package/lib/utilities/debug.js +165 -0
- package/lib/utilities/mqtt.js +152 -0
- package/lib/utilities/network.js +53 -0
- package/lib/utilities/options.js +64 -0
- package/lib/utilities/request-queue.js +161 -0
- package/lib/utilities/ssid.js +37 -0
- package/lib/utilities/state-changes.js +66 -0
- package/lib/utilities/stats.js +687 -0
- package/lib/utilities/timer.js +310 -0
- package/lib/utilities/trigger.js +286 -0
- package/package.json +73 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeChannel } = require('../../utilities/options');
|
|
4
|
+
|
|
5
|
+
const MAX_ALARM_EVENTS_MEMORY = 10;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Alarm feature module.
|
|
9
|
+
* Handles alarm status queries and maintains a buffer of recent alarm events.
|
|
10
|
+
*/
|
|
11
|
+
module.exports = {
|
|
12
|
+
/**
|
|
13
|
+
* Initializes alarm events storage.
|
|
14
|
+
*
|
|
15
|
+
* Called lazily when the first alarm event is received to avoid unnecessary initialization.
|
|
16
|
+
*
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
_initializeAlarmEvents() {
|
|
20
|
+
if (!this._lastAlarmEvents) {
|
|
21
|
+
this._lastAlarmEvents = [];
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets the current alarm status from the device.
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} [options={}] - Get options
|
|
29
|
+
* @param {number} [options.channel=0] - Channel to get alarm status for (default: 0)
|
|
30
|
+
* @returns {Promise<Object>} Alarm status response with `alarm` array
|
|
31
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
32
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
33
|
+
*/
|
|
34
|
+
async getAlarmStatus(options = {}) {
|
|
35
|
+
const channel = normalizeChannel(options);
|
|
36
|
+
const payload = { 'alarm': [{ channel }] };
|
|
37
|
+
return await this.publishMessage('GET', 'Appliance.Control.Alarm', payload);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gets a copy of the most recent alarm events.
|
|
42
|
+
*
|
|
43
|
+
* Alarm events are automatically stored when push notifications are received.
|
|
44
|
+
* The library maintains up to MAX_ALARM_EVENTS_MEMORY most recent events.
|
|
45
|
+
*
|
|
46
|
+
* @returns {Array<Object>} Copy of the alarm events array (most recent first)
|
|
47
|
+
*/
|
|
48
|
+
getLastAlarmEvents() {
|
|
49
|
+
this._initializeAlarmEvents();
|
|
50
|
+
return [...this._lastAlarmEvents];
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Updates the alarm events buffer from push notification data.
|
|
55
|
+
*
|
|
56
|
+
* Called automatically when alarm push notifications are received. Maintains a rolling
|
|
57
|
+
* buffer of the most recent events, discarding older ones when the limit is exceeded.
|
|
58
|
+
*
|
|
59
|
+
* @param {Object|Array} alarmData - Alarm data (single object or array)
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
_updateAlarmEvents(alarmData, source = 'push') {
|
|
63
|
+
if (!alarmData) {return;}
|
|
64
|
+
|
|
65
|
+
this._initializeAlarmEvents();
|
|
66
|
+
|
|
67
|
+
const alarmArray = Array.isArray(alarmData) ? alarmData : [alarmData];
|
|
68
|
+
|
|
69
|
+
for (const alarmEvent of alarmArray) {
|
|
70
|
+
const oldEvents = [...this._lastAlarmEvents];
|
|
71
|
+
this._lastAlarmEvents.unshift(alarmEvent);
|
|
72
|
+
|
|
73
|
+
if (this._lastAlarmEvents.length > MAX_ALARM_EVENTS_MEMORY) {
|
|
74
|
+
this._lastAlarmEvents = this._lastAlarmEvents.slice(0, MAX_ALARM_EVENTS_MEMORY);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const channel = alarmEvent.channel !== undefined ? alarmEvent.channel : 0;
|
|
78
|
+
this.emit('stateChange', {
|
|
79
|
+
type: 'alarm',
|
|
80
|
+
channel,
|
|
81
|
+
value: alarmEvent,
|
|
82
|
+
oldValue: oldEvents.length > 0 ? oldEvents[0] : undefined,
|
|
83
|
+
source,
|
|
84
|
+
timestamp: Date.now()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeChannel } = require('../../utilities/options');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Child lock feature module.
|
|
7
|
+
* Provides control over physical control lock functionality.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = {
|
|
10
|
+
/**
|
|
11
|
+
* Gets the child lock status from the device.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} [options={}] - Get options
|
|
14
|
+
* @param {number} [options.channel=0] - Channel to get lock status for (default: 0)
|
|
15
|
+
* @param {string} [options.subId=null] - Optional subdevice ID
|
|
16
|
+
* @returns {Promise<Object>} Response containing lock status with `lock` 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 getChildLock(options = {}) {
|
|
21
|
+
const channel = normalizeChannel(options);
|
|
22
|
+
const payload = {
|
|
23
|
+
lock: [{
|
|
24
|
+
channel
|
|
25
|
+
}]
|
|
26
|
+
};
|
|
27
|
+
if (options.subId) {
|
|
28
|
+
payload.lock[0].subId = options.subId;
|
|
29
|
+
}
|
|
30
|
+
return await this.publishMessage('GET', 'Appliance.Control.PhysicalLock', payload);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Controls the child lock status.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} options - Child lock options
|
|
37
|
+
* @param {Object|Array<Object>} [options.lockData] - Lock data object or array of lock items (if provided, used directly)
|
|
38
|
+
* @param {number} [options.channel] - Channel to control
|
|
39
|
+
* @param {string} [options.subId] - Optional subdevice ID
|
|
40
|
+
* @param {number} [options.onoff] - Lock on (1) or off (0)
|
|
41
|
+
* @returns {Promise<Object>} Response from the device
|
|
42
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
43
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
44
|
+
*/
|
|
45
|
+
async setChildLock(options = {}) {
|
|
46
|
+
let lockData;
|
|
47
|
+
if (options.lockData) {
|
|
48
|
+
lockData = Array.isArray(options.lockData) ? options.lockData : [options.lockData];
|
|
49
|
+
} else {
|
|
50
|
+
const channel = normalizeChannel(options);
|
|
51
|
+
lockData = [{
|
|
52
|
+
channel,
|
|
53
|
+
subId: options.subId,
|
|
54
|
+
onoff: options.onoff
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
57
|
+
const payload = { lock: lockData };
|
|
58
|
+
return await this.publishMessage('SET', 'Appliance.Control.PhysicalLock', payload);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration feature module.
|
|
5
|
+
* Provides access to device configuration settings such as over-temperature protection.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = {
|
|
8
|
+
/**
|
|
9
|
+
* Gets the over-temperature protection configuration from the device.
|
|
10
|
+
* @param {Object} [options={}] - Get options
|
|
11
|
+
* @returns {Promise<Object>} Response containing over-temperature protection config
|
|
12
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
13
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
14
|
+
*/
|
|
15
|
+
async getConfigOverTemp(_options = {}) {
|
|
16
|
+
return await this.publishMessage('GET', 'Appliance.Config.OverTemp', {});
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Controls the over-temperature protection configuration.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options - Over-temperature config options
|
|
23
|
+
* @param {boolean} options.enable - Enable state (true = on, false = off)
|
|
24
|
+
* @param {number} [options.type] - Protection type (1 = early warning, 2 = early warning and shutdown). If not provided, preserves current type
|
|
25
|
+
* @returns {Promise<Object>} Response from the device
|
|
26
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
27
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
28
|
+
*/
|
|
29
|
+
async setConfigOverTemp(options = {}) {
|
|
30
|
+
if (options.enable === undefined) {
|
|
31
|
+
const { CommandError } = require('../../model/exception');
|
|
32
|
+
throw new CommandError('enable is required', { options }, this.uuid);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const enableValue = options.enable ? 1 : 2;
|
|
36
|
+
let overTempData;
|
|
37
|
+
|
|
38
|
+
if (options.type !== undefined) {
|
|
39
|
+
overTempData = { enable: enableValue, type: options.type };
|
|
40
|
+
} else {
|
|
41
|
+
try {
|
|
42
|
+
const currentConfig = await this.getConfigOverTemp();
|
|
43
|
+
const currentType = currentConfig?.overTemp?.type;
|
|
44
|
+
overTempData = { enable: enableValue, type: currentType !== undefined ? currentType : 1 };
|
|
45
|
+
} catch (e) {
|
|
46
|
+
overTempData = { enable: enableValue, type: 1 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const payload = { overTemp: overTempData };
|
|
51
|
+
return await this.publishMessage('SET', 'Appliance.Config.OverTemp', payload);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeChannel } = require('../../utilities/options');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Consumption feature module.
|
|
7
|
+
* Provides access to historical power consumption data and consumption configuration settings.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = {
|
|
10
|
+
/**
|
|
11
|
+
* Initializes consumption cache.
|
|
12
|
+
*
|
|
13
|
+
* Called lazily when consumption data is first accessed to avoid unnecessary initialization.
|
|
14
|
+
*
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
_initializeConsumptionCache() {
|
|
18
|
+
if (this._channelCachedConsumption === undefined) {
|
|
19
|
+
this._channelCachedConsumption = new Map();
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets daily power consumption data from the device.
|
|
25
|
+
*
|
|
26
|
+
* Returns parsed consumption data with Date objects and converted units (kWh). Date strings
|
|
27
|
+
* are parsed from YYYY-MM-DD format or standard Date string format. Use {@link getRawPowerConsumption}
|
|
28
|
+
* to get the raw API response without parsing.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} [options={}] - Get options
|
|
31
|
+
* @param {number} [options.channel=0] - Channel to read data from (default: 0)
|
|
32
|
+
* @returns {Promise<Array<{date: Date, totalConsumptionKwh: number}>>} Historical consumption data with date and totalConsumptionKwh
|
|
33
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
34
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
35
|
+
*/
|
|
36
|
+
async getPowerConsumption(options = {}) {
|
|
37
|
+
const channel = normalizeChannel(options);
|
|
38
|
+
this._initializeConsumptionCache();
|
|
39
|
+
|
|
40
|
+
const result = await this.publishMessage('GET', 'Appliance.Control.Consumption', { channel });
|
|
41
|
+
const data = result && result.consumption ? result.consumption : [];
|
|
42
|
+
|
|
43
|
+
this._updateConsumptionState({ channel, consumption: data }, 'response');
|
|
44
|
+
|
|
45
|
+
return this._channelCachedConsumption.get(channel) || null;
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets daily power consumption data from the device (X version).
|
|
50
|
+
*
|
|
51
|
+
* Returns parsed consumption data with Date objects and converted units (kWh). This is an
|
|
52
|
+
* alternative consumption endpoint that may provide different data or formatting. Use
|
|
53
|
+
* {@link getRawPowerConsumptionX} to get the raw API response without parsing.
|
|
54
|
+
*
|
|
55
|
+
* @param {Object} [options={}] - Get options
|
|
56
|
+
* @param {number} [options.channel=0] - Channel to read data from (default: 0)
|
|
57
|
+
* @returns {Promise<Array<{date: Date, totalConsumptionKwh: number}>>} Historical consumption data with date and totalConsumptionKwh
|
|
58
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
59
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
60
|
+
*/
|
|
61
|
+
async getPowerConsumptionX(options = {}) {
|
|
62
|
+
const channel = normalizeChannel(options);
|
|
63
|
+
const result = await this.publishMessage('GET', 'Appliance.Control.ConsumptionX', { channel });
|
|
64
|
+
const data = result && result.consumptionx ? result.consumptionx : [];
|
|
65
|
+
|
|
66
|
+
const DATE_FORMAT = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
67
|
+
return data.map(x => {
|
|
68
|
+
const dateStr = x.date;
|
|
69
|
+
let date;
|
|
70
|
+
if (DATE_FORMAT.test(dateStr)) {
|
|
71
|
+
const [, year, month, day] = dateStr.match(DATE_FORMAT);
|
|
72
|
+
date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
73
|
+
} else {
|
|
74
|
+
date = new Date(dateStr);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
date,
|
|
79
|
+
totalConsumptionKwh: parseFloat(x.value) / 1000
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets cached consumption data
|
|
86
|
+
*
|
|
87
|
+
* Returns the most recently fetched consumption data without making a request.
|
|
88
|
+
* Use {@link getPowerConsumption} to fetch fresh data from the device.
|
|
89
|
+
*
|
|
90
|
+
* @param {number} [channel=0] - Channel to get data for (default: 0)
|
|
91
|
+
* @returns {Array<{date: Date, totalConsumptionKwh: number}>|null} Cached consumption data or null if not available
|
|
92
|
+
*/
|
|
93
|
+
getCachedConsumption(channel = 0) {
|
|
94
|
+
this._initializeConsumptionCache();
|
|
95
|
+
return this._channelCachedConsumption.get(channel) || null;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Gets the raw power consumption response from the device without parsing or unit conversion.
|
|
100
|
+
*
|
|
101
|
+
* For parsed data with Date objects and converted units, use {@link getPowerConsumption} instead.
|
|
102
|
+
*
|
|
103
|
+
* @param {Object} [options={}] - Get options
|
|
104
|
+
* @param {number} [options.channel=0] - Channel to read data from (default: 0)
|
|
105
|
+
* @returns {Promise<Object>} Raw API response containing consumption data
|
|
106
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
107
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
108
|
+
*/
|
|
109
|
+
async getRawPowerConsumption(options = {}) {
|
|
110
|
+
const channel = normalizeChannel(options);
|
|
111
|
+
return await this.publishMessage('GET', 'Appliance.Control.Consumption', { channel });
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Gets the raw power consumption X response from the device without parsing or unit conversion.
|
|
116
|
+
*
|
|
117
|
+
* For parsed data with Date objects and converted units, use {@link getPowerConsumptionX} instead.
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} [options={}] - Get options
|
|
120
|
+
* @param {number} [options.channel=0] - Channel to read data from (default: 0)
|
|
121
|
+
* @returns {Promise<Object>} Raw API response containing consumption X data
|
|
122
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
123
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
124
|
+
*/
|
|
125
|
+
async getRawPowerConsumptionX(options = {}) {
|
|
126
|
+
const channel = normalizeChannel(options);
|
|
127
|
+
return await this.publishMessage('GET', 'Appliance.Control.ConsumptionX', { channel });
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Gets the consumption configuration from the device.
|
|
132
|
+
*
|
|
133
|
+
* Returns voltage and current calibration coefficients used for power consumption calculations.
|
|
134
|
+
*
|
|
135
|
+
* @returns {Promise<Object>} Response containing consumption configuration
|
|
136
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
137
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
138
|
+
*/
|
|
139
|
+
async getConsumptionConfig() {
|
|
140
|
+
return await this.publishMessage('GET', 'Appliance.Control.ConsumptionConfig', {});
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Updates the cached consumption state from consumption data.
|
|
145
|
+
*
|
|
146
|
+
* Called automatically when consumption data is received. Parses raw device data,
|
|
147
|
+
* converts dates and units, and emits stateChange events when data changes.
|
|
148
|
+
*
|
|
149
|
+
* @param {Object} consumptionData - Raw consumption data from device
|
|
150
|
+
* @param {number} consumptionData.channel - Channel index
|
|
151
|
+
* @param {Array} consumptionData.consumption - Array of consumption entries with date and value
|
|
152
|
+
* @param {string} [source='response'] - Source of the update ('push' | 'poll' | 'response')
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
_updateConsumptionState(consumptionData, source = 'response') {
|
|
156
|
+
if (!consumptionData) {return;}
|
|
157
|
+
|
|
158
|
+
this._initializeConsumptionCache();
|
|
159
|
+
|
|
160
|
+
const channelIndex = consumptionData.channel;
|
|
161
|
+
if (channelIndex === undefined || channelIndex === null) {return;}
|
|
162
|
+
|
|
163
|
+
const data = consumptionData.consumption || [];
|
|
164
|
+
|
|
165
|
+
const DATE_FORMAT = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
166
|
+
const parsedData = data.map(x => {
|
|
167
|
+
const dateStr = x.date;
|
|
168
|
+
let date;
|
|
169
|
+
if (DATE_FORMAT.test(dateStr)) {
|
|
170
|
+
const [, year, month, day] = dateStr.match(DATE_FORMAT);
|
|
171
|
+
date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
172
|
+
} else {
|
|
173
|
+
date = new Date(dateStr);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
date,
|
|
178
|
+
totalConsumptionKwh: parseFloat(x.value) / 1000
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const oldData = this._channelCachedConsumption.get(channelIndex);
|
|
183
|
+
this._channelCachedConsumption.set(channelIndex, parsedData);
|
|
184
|
+
|
|
185
|
+
let hasChanges = false;
|
|
186
|
+
if (!oldData || oldData.length !== parsedData.length) {
|
|
187
|
+
hasChanges = true;
|
|
188
|
+
} else {
|
|
189
|
+
for (let i = 0; i < parsedData.length; i++) {
|
|
190
|
+
if (!oldData[i] ||
|
|
191
|
+
oldData[i].totalConsumptionKwh !== parsedData[i].totalConsumptionKwh ||
|
|
192
|
+
oldData[i].date.getTime() !== parsedData[i].date.getTime()) {
|
|
193
|
+
hasChanges = true;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (hasChanges) {
|
|
200
|
+
this.emit('stateChange', {
|
|
201
|
+
type: 'consumption',
|
|
202
|
+
channel: channelIndex,
|
|
203
|
+
value: parsedData,
|
|
204
|
+
oldValue: oldData || undefined,
|
|
205
|
+
source,
|
|
206
|
+
timestamp: Date.now()
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Control feature module.
|
|
5
|
+
* Provides advanced control capabilities including batch commands, event acknowledgments, and firmware upgrades.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = {
|
|
8
|
+
/**
|
|
9
|
+
* Executes multiple commands simultaneously.
|
|
10
|
+
*
|
|
11
|
+
* Allows sending multiple commands in a single request to reduce network overhead and improve
|
|
12
|
+
* efficiency when controlling multiple aspects of a device at once.
|
|
13
|
+
*
|
|
14
|
+
* @param {Array<Object>} commands - Array of command objects
|
|
15
|
+
* @param {string} commands[].namespace - Namespace for the command (e.g., "Appliance.Control.ToggleX")
|
|
16
|
+
* @param {string} commands[].method - Method for the command (e.g., "SET", "GET")
|
|
17
|
+
* @param {Object} commands[].payload - Payload for the command
|
|
18
|
+
* @returns {Promise<Object>} Response containing results for each command
|
|
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 setMultiple(commands) {
|
|
23
|
+
const payload = {
|
|
24
|
+
multiple: commands.map(cmd => ({
|
|
25
|
+
header: {
|
|
26
|
+
namespace: cmd.namespace,
|
|
27
|
+
method: cmd.method
|
|
28
|
+
},
|
|
29
|
+
payload: cmd.payload
|
|
30
|
+
}))
|
|
31
|
+
};
|
|
32
|
+
return await this.publishMessage('SET', 'Appliance.Control.Multiple', payload);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Acknowledges an over-temperature event.
|
|
37
|
+
*
|
|
38
|
+
* Over-temperature events are typically initiated by the device via SET. This method sends
|
|
39
|
+
* a SETACK response to acknowledge receipt of the event.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<Object>} Response from the device
|
|
42
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
43
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
44
|
+
*/
|
|
45
|
+
async acknowledgeControlOverTemp() {
|
|
46
|
+
return await this.publishMessage('SETACK', 'Appliance.Control.OverTemp', {});
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initiates a device firmware upgrade.
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} upgradeData - Upgrade data object containing upgrade parameters
|
|
53
|
+
* @returns {Promise<Object>} Response from the device
|
|
54
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
55
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
56
|
+
*/
|
|
57
|
+
async setUpgrade(upgradeData) {
|
|
58
|
+
const payload = { upgrade: upgradeData };
|
|
59
|
+
return await this.publishMessage('SET', 'Appliance.Control.Upgrade', payload);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|