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,297 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PresenceSensorState = require('../../model/states/presence-sensor-state');
|
|
4
|
+
const { normalizeChannel } = require('../../utilities/options');
|
|
5
|
+
const { buildStateChanges } = require('../../utilities/state-changes');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Presence sensor feature module.
|
|
9
|
+
* Provides access to presence detection and light sensor data for devices that support it.
|
|
10
|
+
*/
|
|
11
|
+
module.exports = {
|
|
12
|
+
/**
|
|
13
|
+
* Updates internal presence sensor state from LatestX notification data.
|
|
14
|
+
*
|
|
15
|
+
* Called automatically when LatestX push notifications are received or responses are processed.
|
|
16
|
+
* Extracts presence and light data from the notification and updates the cached state.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object|Array} latestData - Latest sensor readings (single object or array)
|
|
19
|
+
* @param {string} [source='response'] - Source of the update ('push' | 'poll' | 'response')
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
_updatePresenceState(latestData, source = 'response') {
|
|
23
|
+
if (!latestData) {return;}
|
|
24
|
+
|
|
25
|
+
const latestArray = Array.isArray(latestData) ? latestData : [latestData];
|
|
26
|
+
|
|
27
|
+
for (const entry of latestArray) {
|
|
28
|
+
if (!entry || !entry.data) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const channel = entry.channel !== undefined ? entry.channel : 0;
|
|
33
|
+
|
|
34
|
+
const oldState = this._presenceSensorStateByChannel.get(channel);
|
|
35
|
+
const oldValue = oldState ? {
|
|
36
|
+
isPresent: oldState.isPresent,
|
|
37
|
+
distance: oldState.distanceRaw,
|
|
38
|
+
light: oldState.lightLux
|
|
39
|
+
} : undefined;
|
|
40
|
+
|
|
41
|
+
let state = this._presenceSensorStateByChannel.get(channel);
|
|
42
|
+
if (!state) {
|
|
43
|
+
state = new PresenceSensorState({ channel });
|
|
44
|
+
this._presenceSensorStateByChannel.set(channel, state);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const stateUpdate = { channel };
|
|
48
|
+
|
|
49
|
+
if (entry.data.presence && Array.isArray(entry.data.presence) && entry.data.presence.length > 0) {
|
|
50
|
+
const presenceData = entry.data.presence[0];
|
|
51
|
+
stateUpdate.presence = {
|
|
52
|
+
value: presenceData.value,
|
|
53
|
+
distance: presenceData.distance,
|
|
54
|
+
timestamp: presenceData.timestamp,
|
|
55
|
+
times: presenceData.times
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (entry.data.light && Array.isArray(entry.data.light) && entry.data.light.length > 0) {
|
|
60
|
+
const lightData = entry.data.light[0];
|
|
61
|
+
stateUpdate.light = {
|
|
62
|
+
value: lightData.value,
|
|
63
|
+
timestamp: lightData.timestamp
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
state.update(stateUpdate);
|
|
68
|
+
|
|
69
|
+
const newValue = buildStateChanges(oldValue, {
|
|
70
|
+
isPresent: state.isPresent,
|
|
71
|
+
distance: state.distanceRaw,
|
|
72
|
+
light: state.lightLux
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (Object.keys(newValue).length > 0) {
|
|
76
|
+
this.emit('stateChange', {
|
|
77
|
+
type: 'presence',
|
|
78
|
+
channel,
|
|
79
|
+
value: newValue,
|
|
80
|
+
oldValue,
|
|
81
|
+
source,
|
|
82
|
+
timestamp: Date.now()
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets the cached presence sensor state for a channel.
|
|
90
|
+
*
|
|
91
|
+
* Returns the cached state without making a request to the device. Use {@link getLatestSensorReadings}
|
|
92
|
+
* to fetch fresh data from the device.
|
|
93
|
+
*
|
|
94
|
+
* @param {number} [channel=0] - Channel to get state for (default: 0)
|
|
95
|
+
* @returns {import('../lib/model/states/presence-sensor-state').PresenceSensorState|null} Presence sensor state object or null if no cached state
|
|
96
|
+
* @throws {Error} If state has not been initialized (call refreshState() first)
|
|
97
|
+
*/
|
|
98
|
+
getCachedPresenceSensorState(channel = 0) {
|
|
99
|
+
this.validateState();
|
|
100
|
+
return this._presenceSensorStateByChannel.get(channel);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Gets the latest presence detection data.
|
|
105
|
+
*
|
|
106
|
+
* Returns formatted presence data from cached state. Returns null if no presence data
|
|
107
|
+
* is available for the channel.
|
|
108
|
+
*
|
|
109
|
+
* @param {number} [channel=0] - Channel to get presence for (default: 0)
|
|
110
|
+
* @returns {Object|null} Presence data object with value, isPresent (boolean), state ('presence'|'absence'), distance (in meters), distanceRaw (in mm), timestamp, and times, or null if no data
|
|
111
|
+
* @throws {Error} If state has not been initialized (call refreshState() first)
|
|
112
|
+
*/
|
|
113
|
+
getPresence(channel = 0) {
|
|
114
|
+
const state = this.getCachedPresenceSensorState(channel);
|
|
115
|
+
if (!state || state.presenceValue === undefined) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
value: state.presenceValue,
|
|
120
|
+
isPresent: state.isPresent,
|
|
121
|
+
state: state.presenceState,
|
|
122
|
+
distance: state.distanceMeters,
|
|
123
|
+
distanceRaw: state.distanceRaw,
|
|
124
|
+
timestamp: state.presenceTimestamp,
|
|
125
|
+
times: state.presenceTimes
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Checks if presence is currently detected.
|
|
131
|
+
*
|
|
132
|
+
* @param {number} [channel=0] - Channel to check (default: 0)
|
|
133
|
+
* @returns {boolean|null} True if presence detected, false if absence detected, null if no data
|
|
134
|
+
* @throws {Error} If state has not been initialized (call refreshState() first)
|
|
135
|
+
*/
|
|
136
|
+
isPresent(channel = 0) {
|
|
137
|
+
const state = this.getCachedPresenceSensorState(channel);
|
|
138
|
+
return state ? state.isPresent : null;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Gets the latest light/illuminance reading.
|
|
143
|
+
*
|
|
144
|
+
* Returns formatted light data from cached state. Returns null if no light data
|
|
145
|
+
* is available for the channel.
|
|
146
|
+
*
|
|
147
|
+
* @param {number} [channel=0] - Channel to get light for (default: 0)
|
|
148
|
+
* @returns {Object|null} Light data object with value and timestamp, or null if no data
|
|
149
|
+
* @throws {Error} If state has not been initialized (call refreshState() first)
|
|
150
|
+
*/
|
|
151
|
+
getLight(channel = 0) {
|
|
152
|
+
const state = this.getCachedPresenceSensorState(channel);
|
|
153
|
+
if (!state || state.lightLux === undefined) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
value: state.lightLux,
|
|
158
|
+
timestamp: state.lightTimestamp
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets all sensor readings (presence and light).
|
|
164
|
+
*
|
|
165
|
+
* Returns both presence and light data from cached state in a single object.
|
|
166
|
+
*
|
|
167
|
+
* @param {number} [channel=0] - Channel to get readings for (default: 0)
|
|
168
|
+
* @returns {Object} Object containing all sensor readings with presence and light properties
|
|
169
|
+
* @throws {Error} If state has not been initialized (call refreshState() first)
|
|
170
|
+
*/
|
|
171
|
+
getAllSensorReadings(channel = 0) {
|
|
172
|
+
const state = this.getCachedPresenceSensorState(channel);
|
|
173
|
+
return {
|
|
174
|
+
presence: state ? {
|
|
175
|
+
value: state.presenceValue,
|
|
176
|
+
isPresent: state.isPresent,
|
|
177
|
+
state: state.presenceState,
|
|
178
|
+
distance: state.distanceMeters,
|
|
179
|
+
distanceRaw: state.distanceRaw,
|
|
180
|
+
timestamp: state.presenceTimestamp,
|
|
181
|
+
times: state.presenceTimes
|
|
182
|
+
} : null,
|
|
183
|
+
light: state ? {
|
|
184
|
+
value: state.lightLux,
|
|
185
|
+
timestamp: state.lightTimestamp
|
|
186
|
+
} : null
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Gets latest sensor readings from the device.
|
|
192
|
+
*
|
|
193
|
+
* Queries the device for the most recent sensor readings. Automatically updates the cached
|
|
194
|
+
* state when the response is received.
|
|
195
|
+
*
|
|
196
|
+
* @param {Object} [options={}] - Get options
|
|
197
|
+
* @param {Array<string>} [options.dataTypes=['presence', 'light']] - Array of data types to request
|
|
198
|
+
* @returns {Promise<Object>} Promise that resolves with latest sensor data containing `latest` array
|
|
199
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
200
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
201
|
+
*/
|
|
202
|
+
async getLatestSensorReadings(options = {}) {
|
|
203
|
+
const dataTypes = options.dataTypes || ['presence', 'light'];
|
|
204
|
+
const payload = {
|
|
205
|
+
latest: [{
|
|
206
|
+
channel: 0,
|
|
207
|
+
data: dataTypes
|
|
208
|
+
}]
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const response = await this.publishMessage('GET', 'Appliance.Control.Sensor.LatestX', payload);
|
|
212
|
+
|
|
213
|
+
if (response && response.latest) {
|
|
214
|
+
this._updatePresenceState(response.latest, 'response');
|
|
215
|
+
this._lastFullUpdateTimestamp = Date.now();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return response;
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Gets presence configuration from the device.
|
|
223
|
+
*
|
|
224
|
+
* @param {Object} [options={}] - Get options
|
|
225
|
+
* @param {number} [options.channel=0] - Channel to get config for (default: 0)
|
|
226
|
+
* @returns {Promise<Object>} Promise that resolves with presence configuration containing `config` array
|
|
227
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
228
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
229
|
+
*/
|
|
230
|
+
async getPresenceConfig(options = {}) {
|
|
231
|
+
const channel = normalizeChannel(options);
|
|
232
|
+
const payload = {
|
|
233
|
+
config: [{
|
|
234
|
+
channel
|
|
235
|
+
}]
|
|
236
|
+
};
|
|
237
|
+
return await this.publishMessage('GET', 'Appliance.Control.Presence.Config', payload);
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Controls the presence sensor configuration.
|
|
242
|
+
*
|
|
243
|
+
* @param {Object|Array<Object>} configData - Config data object or array of config items
|
|
244
|
+
* @param {number} [configData.channel] - Channel to configure (default: 0)
|
|
245
|
+
* @param {Object} [configData.mode] - Mode configuration
|
|
246
|
+
* @param {number} [configData.mode.workMode] - Work mode (0=Unknown, 1=Biological detection only, 2=Security)
|
|
247
|
+
* @param {number} [configData.mode.testMode] - Test mode value
|
|
248
|
+
* @param {Object} [configData.noBodyTime] - No body detection time configuration
|
|
249
|
+
* @param {number} [configData.noBodyTime.time] - Time in seconds before absence is detected
|
|
250
|
+
* @param {Object} [configData.distance] - Distance configuration
|
|
251
|
+
* @param {number} [configData.distance.value] - Distance threshold in millimeters
|
|
252
|
+
* @param {Object} [configData.sensitivity] - Sensitivity configuration
|
|
253
|
+
* @param {number} [configData.sensitivity.level] - Sensitivity level (1=Anti-Interference, 2=Balance, 3=Responsive)
|
|
254
|
+
* @param {Object} [configData.mthx] - Motion threshold configuration
|
|
255
|
+
* @param {number} [configData.mthx.mth1] - Motion threshold 1
|
|
256
|
+
* @param {number} [configData.mthx.mth2] - Motion threshold 2
|
|
257
|
+
* @param {number} [configData.mthx.mth3] - Motion threshold 3
|
|
258
|
+
* @returns {Promise<Object>} Response from the device
|
|
259
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
260
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
261
|
+
*/
|
|
262
|
+
async setPresenceConfig(configData) {
|
|
263
|
+
const payload = { config: Array.isArray(configData) ? configData : [configData] };
|
|
264
|
+
return await this.publishMessage('SET', 'Appliance.Control.Presence.Config', payload);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Gets presence study/calibration status from the device.
|
|
269
|
+
*
|
|
270
|
+
* @returns {Promise<Object>} Promise that resolves with presence study data
|
|
271
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
272
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
273
|
+
*/
|
|
274
|
+
async getPresenceStudy() {
|
|
275
|
+
return await this.publishMessage('GET', 'Appliance.Control.Presence.Study', {});
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Controls the presence study/calibration mode.
|
|
280
|
+
*
|
|
281
|
+
* Used to start or stop the presence sensor study/calibration process, which helps
|
|
282
|
+
* the device learn its environment for better detection accuracy.
|
|
283
|
+
*
|
|
284
|
+
* @param {Object|Array<Object>} studyData - Study data object or array of study items
|
|
285
|
+
* @param {number} [studyData.channel] - Channel to configure (default: 0)
|
|
286
|
+
* @param {number} [studyData.value] - Study mode value (typically 1-3)
|
|
287
|
+
* @param {number} [studyData.status] - Study status (0 = stop/inactive, 1 = start/active)
|
|
288
|
+
* @returns {Promise<Object>} Response from the device
|
|
289
|
+
* @throws {import('../lib/errors/errors').UnconnectedError} If device is not connected
|
|
290
|
+
* @throws {import('../lib/errors/errors').CommandTimeoutError} If command times out
|
|
291
|
+
*/
|
|
292
|
+
async setPresenceStudy(studyData) {
|
|
293
|
+
const payload = { study: Array.isArray(studyData) ? studyData : [studyData] };
|
|
294
|
+
return await this.publishMessage('SET', 'Appliance.Control.Presence.Study', payload);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|