@mega-yfue/eufy-sdk 0.6.0-beta.45 → 0.6.0-beta.47
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/dist/core/contracts.d.ts +4 -4
- package/dist/index.js +440 -318
- package/dist/index.js.map +4 -4
- package/dist/model/capabilities/battery.d.ts +19 -3
- package/dist/model/capabilities/camera.d.ts +14 -22
- package/dist/model/capabilities/co.d.ts +0 -12
- package/dist/model/capabilities/contact.d.ts +122 -75
- package/dist/model/capabilities/index.d.ts +10 -7
- package/dist/model/capabilities/keypad.d.ts +2 -14
- package/dist/model/capabilities/leak.d.ts +47 -21
- package/dist/model/capabilities/link.d.ts +39 -0
- package/dist/model/capabilities/lock.d.ts +0 -12
- package/dist/model/capabilities/smoke.d.ts +0 -12
- package/dist/model/types.d.ts +1 -1
- package/dist/transport/p2p/command-router.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3471,11 +3471,11 @@ function propertiesOf(members, ctx) {
|
|
|
3471
3471
|
}
|
|
3472
3472
|
var BOOL_VALUES = /* @__PURE__ */ new Set([true, false, 0, 1, "0", "1", "true", "false"]);
|
|
3473
3473
|
function memberWrite(name, m, value, ctx) {
|
|
3474
|
-
if (!inDomain(m, value))
|
|
3475
|
-
throw new Error(rejection(name, m, value));
|
|
3474
|
+
if (!inDomain(m, value, ctx))
|
|
3475
|
+
throw new Error(rejection(name, m, value, ctx));
|
|
3476
3476
|
const cmd = m.write?.(value, ctx);
|
|
3477
3477
|
if (!cmd)
|
|
3478
|
-
throw new Error(rejection(name, m, value));
|
|
3478
|
+
throw new Error(rejection(name, m, value, ctx));
|
|
3479
3479
|
const reflected = m.observation?.reflects(value, ctx);
|
|
3480
3480
|
return m.observation && reflected ? observeCommand(cmd, {
|
|
3481
3481
|
event: m.observation.event,
|
|
@@ -3487,10 +3487,11 @@ function memberWrite(name, m, value, ctx) {
|
|
|
3487
3487
|
timeoutMs: m.observation.timeoutMs
|
|
3488
3488
|
}) : cmd;
|
|
3489
3489
|
}
|
|
3490
|
-
function writeDomain(m) {
|
|
3491
|
-
|
|
3490
|
+
function writeDomain(m, ctx) {
|
|
3491
|
+
const values = resolvedEnum(m, ctx).values;
|
|
3492
|
+
return m.args?.[0]?.values ?? m.decodedValues ?? (values && Object.keys(values).map(Number));
|
|
3492
3493
|
}
|
|
3493
|
-
function inDomain(m, value) {
|
|
3494
|
+
function inDomain(m, value, ctx) {
|
|
3494
3495
|
if (m.type === "bool")
|
|
3495
3496
|
return BOOL_VALUES.has(typeof value === "string" ? value.toLowerCase() : value);
|
|
3496
3497
|
if (typeof value === "number") {
|
|
@@ -3499,11 +3500,11 @@ function inDomain(m, value) {
|
|
|
3499
3500
|
if (m.max !== void 0 && value > m.max)
|
|
3500
3501
|
return false;
|
|
3501
3502
|
}
|
|
3502
|
-
const domain = writeDomain(m)?.filter((v) => typeof v === typeof value);
|
|
3503
|
+
const domain = writeDomain(m, ctx)?.filter((v) => typeof v === typeof value);
|
|
3503
3504
|
return !domain?.length || domain.includes(value);
|
|
3504
3505
|
}
|
|
3505
|
-
function rejection(name, m, value) {
|
|
3506
|
-
const options = writeDomain(m);
|
|
3506
|
+
function rejection(name, m, value, ctx) {
|
|
3507
|
+
const options = writeDomain(m, ctx);
|
|
3507
3508
|
const domain = options?.length ? `one of ${options.join("/")}` : m.min !== void 0 && m.max !== void 0 ? `in ${m.min}..${m.max}` : m.min !== void 0 ? `>= ${m.min}` : void 0;
|
|
3508
3509
|
return `${name}: ${JSON.stringify(value)} is not a valid value` + (domain ? ` (must be ${domain})` : "");
|
|
3509
3510
|
}
|
|
@@ -4817,8 +4818,10 @@ function cellGated(members) {
|
|
|
4817
4818
|
return Object.fromEntries(gated);
|
|
4818
4819
|
}
|
|
4819
4820
|
var notSensor = (ctx) => ctx.codec !== "sensor";
|
|
4820
|
-
function notOnSensor(members) {
|
|
4821
|
+
function notOnSensor(members, sensorOnly) {
|
|
4821
4822
|
const gated = Object.entries(members).map(([name, m]) => {
|
|
4823
|
+
if (sensorOnly.includes(name))
|
|
4824
|
+
return [name, m];
|
|
4822
4825
|
const own = m.available;
|
|
4823
4826
|
const available = own ? (ctx) => notSensor(ctx) && own(ctx) : notSensor;
|
|
4824
4827
|
return [name, { ...m, available }];
|
|
@@ -5054,12 +5057,26 @@ var CAMERA_BATTERY_MEMBERS = cellGated({
|
|
|
5054
5057
|
provenance: "apk",
|
|
5055
5058
|
unexposed: true,
|
|
5056
5059
|
description: "Raw GET_CAMERA_INFO value (1103), meaning unevidenced \u2014 read live as the constant 5 on a T8170 at 92% and a T8171 at 27%, so it is NOT a low-battery flag."
|
|
5060
|
+
},
|
|
5061
|
+
/**
|
|
5062
|
+
* A sensor's battery is low. The app reads the entry sensor's 1552 and the motion sensor's 1601 into the
|
|
5063
|
+
* same field (`sensor_is_low_power`, `asBoolean`), so one member reads whichever the sensor reports.
|
|
5064
|
+
* Present on the `sensor` codec only: a camera or a lock reports a level instead.
|
|
5065
|
+
*/
|
|
5066
|
+
batteryLow: {
|
|
5067
|
+
param: 1552,
|
|
5068
|
+
readAliases: [{ paramType: 1601 }],
|
|
5069
|
+
type: "bool",
|
|
5070
|
+
kind: "boolean",
|
|
5071
|
+
provenance: "verified",
|
|
5072
|
+
available: (ctx) => !notSensor(ctx),
|
|
5073
|
+
description: "Sensor battery low (1552 entry sensor, 1601 motion sensor; sensor_is_low_power)."
|
|
5057
5074
|
}
|
|
5058
5075
|
});
|
|
5059
|
-
var BATTERY_MEMBERS = notOnSensor(CAMERA_BATTERY_MEMBERS);
|
|
5076
|
+
var BATTERY_MEMBERS = notOnSensor(CAMERA_BATTERY_MEMBERS, ["batteryLow"]);
|
|
5060
5077
|
var BATTERY = {
|
|
5061
5078
|
capability: "battery",
|
|
5062
|
-
description: "Battery level, charging, health, temperature
|
|
5079
|
+
description: "Battery level, charging, health, temperature and solar input; a sensor's low-battery flag.",
|
|
5063
5080
|
members: BATTERY_MEMBERS,
|
|
5064
5081
|
properties: propertiesOf(BATTERY_MEMBERS),
|
|
5065
5082
|
/** A reported battery-level param (1101) is the verified proof the device is battery-powered. */
|
|
@@ -5104,186 +5121,6 @@ function resolveWorkingModeValue(model, name) {
|
|
|
5104
5121
|
return hit ? Number(hit[0]) : void 0;
|
|
5105
5122
|
}
|
|
5106
5123
|
|
|
5107
|
-
// dist/model/capabilities/contact.js
|
|
5108
|
-
var CONTACT_CMD = {
|
|
5109
|
-
/** Alarm sound/tone selection (app `APP_CMD_ALARM_SOUND_TYPE`). See {@link EntryAlarmTone}. */
|
|
5110
|
-
ALARM_SOUND_TYPE: 1507,
|
|
5111
|
-
/** Alarm volume (app `APP_CMD_ALARM_VOLUME_VALUE`). A device level 1-26, not a percentage. */
|
|
5112
|
-
ALARM_VOLUME: 1508
|
|
5113
|
-
};
|
|
5114
|
-
var EntryAlarmTone = {
|
|
5115
|
-
/** Silent — no alarm tone. */
|
|
5116
|
-
None: 0,
|
|
5117
|
-
Water: 1,
|
|
5118
|
-
Classic: 2,
|
|
5119
|
-
Light: 3,
|
|
5120
|
-
Ding: 4
|
|
5121
|
-
};
|
|
5122
|
-
var ALARM_VOLUME_MIN = 1;
|
|
5123
|
-
var ALARM_VOLUME_MAX = 26;
|
|
5124
|
-
var SENSOR_STATUS_CMD = 1829;
|
|
5125
|
-
var CONTACT_PARAM = 1550;
|
|
5126
|
-
function statusParam(signal, paramType) {
|
|
5127
|
-
if (signal.source !== "p2p-frame" || signal.json?.cmd !== SENSOR_STATUS_CMD)
|
|
5128
|
-
return void 0;
|
|
5129
|
-
const params = signal.json.payload?.params;
|
|
5130
|
-
if (!Array.isArray(params))
|
|
5131
|
-
return void 0;
|
|
5132
|
-
const hit = params.find((p) => p?.param_type === paramType);
|
|
5133
|
-
return hit?.param_value == null ? void 0 : String(hit.param_value);
|
|
5134
|
-
}
|
|
5135
|
-
function contactOpen(raw) {
|
|
5136
|
-
return raw == null || raw === "" ? {} : { open: String(raw) === "1" };
|
|
5137
|
-
}
|
|
5138
|
-
var CONTACT_MEMBERS = {
|
|
5139
|
-
/**
|
|
5140
|
-
* The headline read: `true` when the door or window is open. Polarity is the V6 app's own
|
|
5141
|
-
* `"1".equals(e)` — see `contactOpen`, which normalises every inbound source through one
|
|
5142
|
-
* predicate so the poll, the push and the station notify cannot disagree about what open means.
|
|
5143
|
-
* Reporting 1550 is also this capability's detection evidence, so a device with this getter is
|
|
5144
|
-
* confirmed to be a contact sensor.
|
|
5145
|
-
*/
|
|
5146
|
-
open: {
|
|
5147
|
-
param: CONTACT_PARAM,
|
|
5148
|
-
property: "contact",
|
|
5149
|
-
type: "bool",
|
|
5150
|
-
kind: "boolean",
|
|
5151
|
-
provenance: "verified",
|
|
5152
|
-
description: "Contact state, true=open (verified: param 1550 entry-sensor contact)."
|
|
5153
|
-
},
|
|
5154
|
-
/**
|
|
5155
|
-
* Unix seconds at which the sensor last checked in — the liveness read beside the contact state, and
|
|
5156
|
-
* the way to tell a genuinely closed door from a sensor that stopped reporting. A `timestamp` kind
|
|
5157
|
-
* takes no `unit`: it is an instant, not a duration.
|
|
5158
|
-
*/
|
|
5159
|
-
lastSeen: {
|
|
5160
|
-
param: 1551,
|
|
5161
|
-
type: "number",
|
|
5162
|
-
kind: "timestamp",
|
|
5163
|
-
provenance: "verified",
|
|
5164
|
-
description: "Last-seen unix timestamp, seconds (verified: param 1551)."
|
|
5165
|
-
},
|
|
5166
|
-
/**
|
|
5167
|
-
* Link quality to whichever radio the sensor is paired over, in dBm as the device measures it. The
|
|
5168
|
-
* same param 1141 the other sub-1G sensors report on, so the number is comparable across them —
|
|
5169
|
-
* unlike a bars mapping, which this SDK never applies.
|
|
5170
|
-
*/
|
|
5171
|
-
rssi: {
|
|
5172
|
-
param: 1141,
|
|
5173
|
-
type: "number",
|
|
5174
|
-
unit: "dBm",
|
|
5175
|
-
kind: "dbm",
|
|
5176
|
-
provenance: "verified",
|
|
5177
|
-
description: "Sub-1G/Wi-Fi signal strength (verified: param 1141 = RSSI)."
|
|
5178
|
-
},
|
|
5179
|
-
/**
|
|
5180
|
-
* Rejected, not clamped, outside the tone enum: a fire-and-forget write of a bogus tone would look like
|
|
5181
|
-
* it worked. The wire is a direct-binary scalar `[ch][value][acct]` at signCode 8 — the app's own
|
|
5182
|
-
* captured frame.
|
|
5183
|
-
*/
|
|
5184
|
-
alarmSoundType: {
|
|
5185
|
-
param: CONTACT_CMD.ALARM_SOUND_TYPE,
|
|
5186
|
-
type: "enum",
|
|
5187
|
-
kind: "enum",
|
|
5188
|
-
enumValues: enumLabels(EntryAlarmTone),
|
|
5189
|
-
provenance: "verified",
|
|
5190
|
-
requires: [CONTACT_CMD.ALARM_SOUND_TYPE],
|
|
5191
|
-
args: [{ name: "tone", kind: "enum", description: "Silent (0) or one of the four chimes." }],
|
|
5192
|
-
description: "Entry-sensor alarm tone (1507): 0=None,1=Water,2=Classic,3=Light,4=Ding. Write verified live on a T90E0 (direct-binary [ch][value][acct]). See CONTACT_CMD.ALARM_SOUND_TYPE.",
|
|
5193
|
-
write: (v, ctx) => {
|
|
5194
|
-
const tone = coerceEnumValue(EntryAlarmTone, v);
|
|
5195
|
-
return tone === void 0 ? void 0 : setScalar(CONTACT_CMD.ALARM_SOUND_TYPE, tone, ctx, "auto");
|
|
5196
|
-
}
|
|
5197
|
-
},
|
|
5198
|
-
/**
|
|
5199
|
-
* `1350` SET_PAYLOAD, `mChannel` = the device channel, `mValue3` 0, payload carrying the channel and a
|
|
5200
|
-
* transaction stamp — the byte-shape of the app's own captured frame. Out of range is refused rather
|
|
5201
|
-
* than clamped: the app's slider has no values outside it, so one is a caller error, not a nudge.
|
|
5202
|
-
*
|
|
5203
|
-
* Level-2 only, though the `mValue3` 0 would allow `"auto"`: an entry sensor's session IS its
|
|
5204
|
-
* HomeBase's, which always holds a key — the second of `setPayload`'s two conditions, not an oversight
|
|
5205
|
-
* of the first.
|
|
5206
|
-
*/
|
|
5207
|
-
alarmVolume: {
|
|
5208
|
-
param: CONTACT_CMD.ALARM_VOLUME,
|
|
5209
|
-
type: "number",
|
|
5210
|
-
kind: "scalar",
|
|
5211
|
-
provenance: "verified",
|
|
5212
|
-
requires: [CONTACT_CMD.ALARM_VOLUME],
|
|
5213
|
-
min: ALARM_VOLUME_MIN,
|
|
5214
|
-
max: ALARM_VOLUME_MAX,
|
|
5215
|
-
args: [
|
|
5216
|
-
{
|
|
5217
|
-
name: "level",
|
|
5218
|
-
kind: "scalar",
|
|
5219
|
-
min: ALARM_VOLUME_MIN,
|
|
5220
|
-
max: ALARM_VOLUME_MAX,
|
|
5221
|
-
description: "A device level, not a percentage."
|
|
5222
|
-
}
|
|
5223
|
-
],
|
|
5224
|
-
description: "Entry-sensor alarm volume (1508), a device level 1-26 (NOT a percentage). Write verified live on a T90E0 (1350 SET_PAYLOAD {channel,volume,transaction}). See CONTACT_CMD.ALARM_VOLUME.",
|
|
5225
|
-
write: (v, ctx) => {
|
|
5226
|
-
const level = Number(v);
|
|
5227
|
-
return Number.isInteger(level) && level >= ALARM_VOLUME_MIN && level <= ALARM_VOLUME_MAX ? setPayload(CONTACT_CMD.ALARM_VOLUME, { channel: ctx.channel, volume: level, transaction: String(Date.now()) }, ctx, 0) : void 0;
|
|
5228
|
-
}
|
|
5229
|
-
}
|
|
5230
|
-
};
|
|
5231
|
-
var CONTACT = {
|
|
5232
|
-
capability: "contact",
|
|
5233
|
-
description: "Entry (door/window) sensor: contact state, last-seen, and the built-in alarm tone/volume.",
|
|
5234
|
-
members: CONTACT_MEMBERS,
|
|
5235
|
-
properties: propertiesOf(CONTACT_MEMBERS),
|
|
5236
|
-
/** A reported entry-sensor contact param (1550) is the verified proof of a contact sensor. */
|
|
5237
|
-
detection: { evidenceParams: [1550] },
|
|
5238
|
-
/**
|
|
5239
|
-
* Inbound `contactState`: the contact param (1550) changing on a cloud poll (open/closed), AND the FCM
|
|
5240
|
-
* door-sensor push (`CusPushEvent.DOOR_SENSOR` = 3) — verified live on a T8900 (both fire).
|
|
5241
|
-
*
|
|
5242
|
-
* Both sources carry the state under a different key, so each normalises to `open`: the poll's `to` is
|
|
5243
|
-
* the 1550 param value, and the push's is the wire's single-letter `e`. Polarity for the push is the
|
|
5244
|
-
* V6 app's own `CusPushMode.isSensorOpen()`, which is exactly `"1".equals(e)` — anything else,
|
|
5245
|
-
* including absent, is closed — read under its `a == PUSH_DOOR_SENSOR_EVT` branch to choose the
|
|
5246
|
-
* "sensor is opened" vs "sensor is closed" notification string.
|
|
5247
|
-
*/
|
|
5248
|
-
events: [
|
|
5249
|
-
{
|
|
5250
|
-
source: "poll",
|
|
5251
|
-
match: 1550,
|
|
5252
|
-
emit: "contactState",
|
|
5253
|
-
derive: (s) => contactOpen(s.source === "poll" ? s.to : void 0)
|
|
5254
|
-
},
|
|
5255
|
-
{
|
|
5256
|
-
source: "push",
|
|
5257
|
-
match: CusPushEvent.DOOR_SENSOR,
|
|
5258
|
-
emit: "contactState",
|
|
5259
|
-
derive: (s) => contactOpen(s.source === "push" ? s.payload?.["e"] : void 0)
|
|
5260
|
-
}
|
|
5261
|
-
],
|
|
5262
|
-
/**
|
|
5263
|
-
* The contact is a settled state reported by three transports, so one door movement would otherwise
|
|
5264
|
-
* be announced up to three times — the station notify, then the same value as an FCM push ~2 s
|
|
5265
|
-
* later, then again on the next cloud poll.
|
|
5266
|
-
*/
|
|
5267
|
-
stateEvents: [{ event: "contactState", field: "open" }],
|
|
5268
|
-
/**
|
|
5269
|
-
* The station's own notify — the fastest contact signal there is, and the only one that needs no
|
|
5270
|
-
* cloud at all (see {@link SENSOR_STATUS_CMD} for the measured lead over the push).
|
|
5271
|
-
*
|
|
5272
|
-
* A declarative `events` row can't express this: those match a push event id or a poll param id,
|
|
5273
|
-
* while the state here is nested inside a frame's `params` array under an inner command id. Hence
|
|
5274
|
-
* the decode hook, which is the documented escape hatch for exactly that.
|
|
5275
|
-
*
|
|
5276
|
-
* Emits nothing when the notify carries no contact param, so a status frame reporting only battery
|
|
5277
|
-
* or signal strength doesn't fabricate a contact event.
|
|
5278
|
-
*/
|
|
5279
|
-
decodeEvent(signal) {
|
|
5280
|
-
const raw = statusParam(signal, CONTACT_PARAM);
|
|
5281
|
-
if (raw === void 0)
|
|
5282
|
-
return null;
|
|
5283
|
-
return { event: "contactState", payload: contactOpen(raw) };
|
|
5284
|
-
}
|
|
5285
|
-
};
|
|
5286
|
-
|
|
5287
5124
|
// dist/model/device-family.js
|
|
5288
5125
|
var INDOOR_CAMERA_TYPES = /* @__PURE__ */ new Set([
|
|
5289
5126
|
DeviceType.INDOOR_CAMERA,
|
|
@@ -5927,7 +5764,7 @@ var CAMERA_CMD = {
|
|
|
5927
5764
|
* (1013). (Some models omit full colour; enum in {@link NightVision}.)
|
|
5928
5765
|
*/
|
|
5929
5766
|
NIGHT_VISION_TYPE: 1277,
|
|
5930
|
-
/**
|
|
5767
|
+
/** Night vision on a device reporting 2002: direct level-1 [channel][value]. */
|
|
5931
5768
|
IC_NIGHT_VISION_TYPE: 1013,
|
|
5932
5769
|
/**
|
|
5933
5770
|
* Push-notification STYLE — how much a detection push carries: text alone, a thumbnail, or text
|
|
@@ -6038,10 +5875,12 @@ var SoundDetectionType = {
|
|
|
6038
5875
|
var NightVision = {
|
|
6039
5876
|
/** Off — never use infrared. */
|
|
6040
5877
|
Off: 0,
|
|
6041
|
-
/** Infrared / "B&W Auto" — black-and-white night vision. */
|
|
5878
|
+
/** Infrared / "B&W Auto" — black-and-white night vision, switched on when it gets dark. */
|
|
6042
5879
|
Infrared: 1,
|
|
6043
5880
|
/** Full colour night vision (models with a spotlight / starlight sensor). */
|
|
6044
|
-
FullColor: 2
|
|
5881
|
+
FullColor: 2,
|
|
5882
|
+
/** Infrared held on — black-and-white night vision, not switched by the light level. */
|
|
5883
|
+
InfraredOn: 3
|
|
6045
5884
|
};
|
|
6046
5885
|
var RecordingQuality = {
|
|
6047
5886
|
HD720: "HD (720P)",
|
|
@@ -6119,8 +5958,8 @@ function powerValue(on, ctx) {
|
|
|
6119
5958
|
function isT8410HomeBase3(ctx) {
|
|
6120
5959
|
return ctx.model === "T8410" && ctx.stationSerial?.startsWith("T8030") === true;
|
|
6121
5960
|
}
|
|
6122
|
-
function
|
|
6123
|
-
return ctx.
|
|
5961
|
+
function readsNightVisionOn2002(ctx) {
|
|
5962
|
+
return ctx.paramIds?.has(2002) === true && !ctx.paramIds.has(CAMERA_CMD.NIGHT_VISION_TYPE);
|
|
6124
5963
|
}
|
|
6125
5964
|
function usesHomeBasePowerPayload(ctx) {
|
|
6126
5965
|
if (!isT8410HomeBase3(ctx) || !ctx.firmwareVersion || !/^\d+(?:\.\d+){2,3}$/.test(ctx.firmwareVersion))
|
|
@@ -6335,36 +6174,27 @@ var CAMERA_MEMBERS = {
|
|
|
6335
6174
|
return style == null ? void 0 : setJson(CAMERA_CMD.PUSH_NOTIFY_TYPE, { value: style, transaction: String(Date.now()) }, ctx);
|
|
6336
6175
|
}
|
|
6337
6176
|
},
|
|
6338
|
-
/**
|
|
6339
|
-
* Three modes, and `FullColor` is the conditional one: a model without a spotlight or starlight
|
|
6340
|
-
* sensor omits it, and the wire accepts the value regardless — the device's own reported set is the
|
|
6341
|
-
* only statement of which of the three it has. `coerceEnumValue` rejects a value outside
|
|
6342
|
-
* {@link NightVision} rather than coercing it. The payload key on the wire is `night_sion`.
|
|
6343
|
-
*
|
|
6344
|
-
* 1277 carries more than these three on some models: the V6 app's per-model mappings put modes on
|
|
6345
|
-
* 3, 5, 6 and 8 as well, and which model takes which mapping is not in the app bundle. A reported value
|
|
6346
|
-
* outside the published options reads as `undefined` rather than as an unnamed number.
|
|
6347
|
-
*/
|
|
6177
|
+
/** Modes come from the thing description (`thing`); unlisted values read as `undefined`. */
|
|
6348
6178
|
nightVision: {
|
|
6349
6179
|
param: CAMERA_CMD.NIGHT_VISION_TYPE,
|
|
6350
6180
|
type: "enum",
|
|
6351
6181
|
kind: "enum",
|
|
6352
|
-
enumValues: { 0: "Off", 1: "Infrared", 2: "Full Color" },
|
|
6353
|
-
|
|
6182
|
+
enumValues: { 0: "Off", 1: "Infrared", 2: "Full Color", 3: "Infrared On" },
|
|
6183
|
+
thing: ["nightvision_type_new", "nightvision_type"],
|
|
6354
6184
|
provenance: "verified",
|
|
6355
6185
|
available: isCameraCodec,
|
|
6356
|
-
readAvailable: (ctx) => !
|
|
6357
|
-
readAliases: [{ paramType: 2002, available:
|
|
6358
|
-
|
|
6186
|
+
readAvailable: (ctx) => !readsNightVisionOn2002(ctx),
|
|
6187
|
+
readAliases: [{ paramType: 2002, available: readsNightVisionOn2002 }],
|
|
6188
|
+
coerce: (raw) => raw === "true" ? 1 : raw === "false" ? 0 : Number(raw),
|
|
6189
|
+
description: "Night-vision mode: 0 = off, 1 = infrared by light level, 2 = full colour, 3 = infrared held on. Written on 1277 (1350 SET_PAYLOAD, night_sion), or on 1013 as a bare [channel][value] when the device reports 2002.",
|
|
6359
6190
|
write: (v, ctx) => {
|
|
6360
6191
|
const nv = coerceEnumValue(NightVision, v);
|
|
6361
6192
|
if (nv == null)
|
|
6362
6193
|
return void 0;
|
|
6363
|
-
if (
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
}
|
|
6194
|
+
if (readsNightVisionOn2002(ctx))
|
|
6195
|
+
return setScalar(CAMERA_CMD.IC_NIGHT_VISION_TYPE, nv, ctx, "int-pair");
|
|
6196
|
+
if (nv === NightVision.InfraredOn)
|
|
6197
|
+
throw new Error("nightVision: Infrared On has no verified write on this model");
|
|
6368
6198
|
return setPayload(CAMERA_CMD.NIGHT_VISION_TYPE, { channel: ctx.channel, night_sion: nv }, ctx, 0, 0, "auto");
|
|
6369
6199
|
}
|
|
6370
6200
|
},
|
|
@@ -7598,41 +7428,330 @@ var PTZ = {
|
|
|
7598
7428
|
}
|
|
7599
7429
|
};
|
|
7600
7430
|
|
|
7431
|
+
// dist/model/capabilities/contact.js
|
|
7432
|
+
var CONTACT_CMD = {
|
|
7433
|
+
/** Contact state (`APP_CMD_ENTRY_SENSOR_STATUS`, identifier `sensor_door_status`). */
|
|
7434
|
+
STATUS: 1550,
|
|
7435
|
+
/** Removed from its mount (`APP_CMD_DEV_DISASSEMBLE`, identifier `sensor_disassemble_status`). */
|
|
7436
|
+
TAMPER: 61015,
|
|
7437
|
+
/** Bypassed by the alarm (`APP_CMD_DEV_ENTRY_BYPASS`, identifier `sensor_door_bypass_status`). */
|
|
7438
|
+
BYPASS: 61016,
|
|
7439
|
+
/** Left-open alert (`APP_CMD_SENSOR_OPEN_STATUS_ALERT`, identifier `sensor_door_open_status_alert_config`). */
|
|
7440
|
+
OPEN_ALERT: 1290,
|
|
7441
|
+
/** Daily state check (`APP_CMD_SENSOR_DOOR_DAILY_CHECK`, identifier `sensor_door_daily_check_status_config`). */
|
|
7442
|
+
DAILY_CHECK: 1291,
|
|
7443
|
+
/** Alarm tone (`APP_CMD_ALARM_SOUND_TYPE`, identifier `device_alarm_sound_type`). */
|
|
7444
|
+
ALARM_SOUND_TYPE: 1507,
|
|
7445
|
+
/** Alarm volume (`APP_CMD_ALARM_VOLUME_VALUE`, identifier `alarm_volume_value`). */
|
|
7446
|
+
ALARM_VOLUME: 1508
|
|
7447
|
+
};
|
|
7448
|
+
var SENSOR_STATUS_CMD = 1829;
|
|
7449
|
+
var OPEN_ALERT_MINUTES = [5, 10, 15, 30, 60];
|
|
7450
|
+
var ALARM_TONES = { None: 0, Water: 1, Classics: 2, Light: 3, Ding: 4 };
|
|
7451
|
+
var ALARM_VOLUME_MIN = 1;
|
|
7452
|
+
var ALARM_VOLUME_MAX = 26;
|
|
7453
|
+
var described = (identifier) => (ctx) => !ctx.things || identifier in ctx.things.properties;
|
|
7454
|
+
function statusParam(signal, paramType) {
|
|
7455
|
+
if (signal.source !== "p2p-frame" || signal.json?.cmd !== SENSOR_STATUS_CMD)
|
|
7456
|
+
return void 0;
|
|
7457
|
+
const params = signal.json.payload?.params;
|
|
7458
|
+
if (!Array.isArray(params))
|
|
7459
|
+
return void 0;
|
|
7460
|
+
const hit = params.find((p) => p?.param_type === paramType);
|
|
7461
|
+
return hit?.param_value == null ? void 0 : String(hit.param_value);
|
|
7462
|
+
}
|
|
7463
|
+
function contactOpen(raw) {
|
|
7464
|
+
return raw == null || raw === "" ? {} : { open: String(raw) === "1" };
|
|
7465
|
+
}
|
|
7466
|
+
function config(raw) {
|
|
7467
|
+
if (typeof raw !== "string" || !raw)
|
|
7468
|
+
return void 0;
|
|
7469
|
+
try {
|
|
7470
|
+
const parsed = JSON.parse(Buffer.from(raw, "base64").toString("utf8"));
|
|
7471
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
7472
|
+
} catch {
|
|
7473
|
+
return void 0;
|
|
7474
|
+
}
|
|
7475
|
+
}
|
|
7476
|
+
var hhmm = (minutes) => `${String(Math.floor(minutes / 60)).padStart(2, "0")}:${String(minutes % 60).padStart(2, "0")}`;
|
|
7477
|
+
function minutesOf(time) {
|
|
7478
|
+
const m = /^(\d{1,2}):(\d{2})$/.exec(time);
|
|
7479
|
+
if (!m)
|
|
7480
|
+
return void 0;
|
|
7481
|
+
const [h, min] = [Number(m[1]), Number(m[2])];
|
|
7482
|
+
return h < 24 && min < 60 ? h * 60 + min : void 0;
|
|
7483
|
+
}
|
|
7484
|
+
var CONTACT_MEMBERS = {
|
|
7485
|
+
/**
|
|
7486
|
+
* The headline read: `true` when the door or window is open. Every inbound source normalises through
|
|
7487
|
+
* `contactOpen`, so the poll, the push and the station notify cannot disagree about what open means.
|
|
7488
|
+
* Reporting 1550 is also this capability's detection evidence.
|
|
7489
|
+
*/
|
|
7490
|
+
open: {
|
|
7491
|
+
param: CONTACT_CMD.STATUS,
|
|
7492
|
+
property: "contact",
|
|
7493
|
+
type: "bool",
|
|
7494
|
+
kind: "boolean",
|
|
7495
|
+
provenance: "verified",
|
|
7496
|
+
description: "Contact state, true=open (1550, sensor_door_status)."
|
|
7497
|
+
},
|
|
7498
|
+
/** The sensor was removed from its mount. Reported by a T8900; the meaning of a set value is the product bundle's. */
|
|
7499
|
+
tampered: {
|
|
7500
|
+
param: CONTACT_CMD.TAMPER,
|
|
7501
|
+
type: "bool",
|
|
7502
|
+
kind: "boolean",
|
|
7503
|
+
provenance: "apk",
|
|
7504
|
+
description: "Removed from its mount (61015, sensor_disassemble_status)."
|
|
7505
|
+
},
|
|
7506
|
+
/** The alarm currently skips this sensor. Reported by a T8900; the meaning of a set value is the product bundle's. */
|
|
7507
|
+
bypassed: {
|
|
7508
|
+
param: CONTACT_CMD.BYPASS,
|
|
7509
|
+
type: "bool",
|
|
7510
|
+
kind: "boolean",
|
|
7511
|
+
provenance: "apk",
|
|
7512
|
+
description: "Bypassed by the alarm (61016, sensor_door_bypass_status)."
|
|
7513
|
+
},
|
|
7514
|
+
/**
|
|
7515
|
+
* The left-open alert config, whole as the sensor stores it (base64 JSON). Its two fields are the
|
|
7516
|
+
* members below; both are set together by `setOpenAlert`, since one frame carries both.
|
|
7517
|
+
*/
|
|
7518
|
+
openAlertConfig: {
|
|
7519
|
+
param: CONTACT_CMD.OPEN_ALERT,
|
|
7520
|
+
type: "string",
|
|
7521
|
+
kind: "text",
|
|
7522
|
+
provenance: "verified",
|
|
7523
|
+
unexposed: true,
|
|
7524
|
+
writtenElsewhere: true,
|
|
7525
|
+
description: "Left-open alert config, base64 JSON {door_enable,door_time_point} (1290)."
|
|
7526
|
+
},
|
|
7527
|
+
/** Whether a notification fires when the door stays open longer than {@link CONTACT_MEMBERS.openAlertMinutes}. */
|
|
7528
|
+
openAlert: {
|
|
7529
|
+
readsFrom: "openAlertConfig",
|
|
7530
|
+
type: "bool",
|
|
7531
|
+
kind: "boolean",
|
|
7532
|
+
provenance: "verified",
|
|
7533
|
+
decode: (raw) => {
|
|
7534
|
+
const c = config(raw);
|
|
7535
|
+
return c === void 0 ? void 0 : asBool(c.door_enable);
|
|
7536
|
+
},
|
|
7537
|
+
description: "Left-open alert on (1290 door_enable). Set with setOpenAlert."
|
|
7538
|
+
},
|
|
7539
|
+
/** How long the door may stay open before the alert fires: one of 5, 10, 15, 30 or 60 minutes. */
|
|
7540
|
+
openAlertMinutes: {
|
|
7541
|
+
readsFrom: "openAlertConfig",
|
|
7542
|
+
type: "number",
|
|
7543
|
+
kind: "scalar",
|
|
7544
|
+
provenance: "verified",
|
|
7545
|
+
decode: (raw) => {
|
|
7546
|
+
const n = Number(config(raw)?.door_time_point);
|
|
7547
|
+
return Number.isInteger(n) ? n : void 0;
|
|
7548
|
+
},
|
|
7549
|
+
description: "Left-open alert delay, minutes: 5, 10, 15, 30 or 60 (1290 door_time_point)."
|
|
7550
|
+
},
|
|
7551
|
+
/** The daily state check config, whole (base64 JSON); its fields are the members below. */
|
|
7552
|
+
dailyCheckConfig: {
|
|
7553
|
+
param: CONTACT_CMD.DAILY_CHECK,
|
|
7554
|
+
type: "string",
|
|
7555
|
+
kind: "text",
|
|
7556
|
+
provenance: "verified",
|
|
7557
|
+
unexposed: true,
|
|
7558
|
+
writtenElsewhere: true,
|
|
7559
|
+
description: "Daily check config, base64 JSON {door_enable,door_time_interval} (1291)."
|
|
7560
|
+
},
|
|
7561
|
+
/** Whether the sensor's state is notified at each of {@link CONTACT_MEMBERS.dailyCheckTimes}. */
|
|
7562
|
+
dailyCheck: {
|
|
7563
|
+
readsFrom: "dailyCheckConfig",
|
|
7564
|
+
type: "bool",
|
|
7565
|
+
kind: "boolean",
|
|
7566
|
+
provenance: "verified",
|
|
7567
|
+
decode: (raw) => {
|
|
7568
|
+
const c = config(raw);
|
|
7569
|
+
return c === void 0 ? void 0 : asBool(c.door_enable);
|
|
7570
|
+
},
|
|
7571
|
+
description: "Daily state check on (1291 door_enable). Set with setDailyCheck."
|
|
7572
|
+
},
|
|
7573
|
+
/** The times of day the state is notified, `HH:MM` comma-separated in the order the sensor holds them. */
|
|
7574
|
+
dailyCheckTimes: {
|
|
7575
|
+
readsFrom: "dailyCheckConfig",
|
|
7576
|
+
type: "string",
|
|
7577
|
+
kind: "text",
|
|
7578
|
+
provenance: "verified",
|
|
7579
|
+
decode: (raw) => {
|
|
7580
|
+
const times = config(raw)?.door_time_interval;
|
|
7581
|
+
return Array.isArray(times) ? times.map((t) => hhmm(Number(t) || 0)).join(",") : void 0;
|
|
7582
|
+
},
|
|
7583
|
+
description: "Daily check times, HH:MM comma-separated (1291 door_time_interval, minutes since midnight)."
|
|
7584
|
+
},
|
|
7585
|
+
/**
|
|
7586
|
+
* Turn the left-open alert on or off, with its delay. One frame carries both: `1350` SET_PAYLOAD,
|
|
7587
|
+
* sub-command 1290, `{door_enable, door_time_point}` — the product's settings bundle's own frame.
|
|
7588
|
+
* Verified live on a T8900: param 1290 read back the written value and the app showed it.
|
|
7589
|
+
*/
|
|
7590
|
+
setOpenAlert: method(({ ctx, sink }) => async (on, minutes) => {
|
|
7591
|
+
if (!OPEN_ALERT_MINUTES.includes(minutes)) {
|
|
7592
|
+
throw new Error(`openAlertMinutes: ${String(minutes)} is not one of ${OPEN_ALERT_MINUTES.join(", ")}`);
|
|
7593
|
+
}
|
|
7594
|
+
await sink.dispatch(setPayload(CONTACT_CMD.OPEN_ALERT, { door_enable: asBool(on) ? 1 : 0, door_time_point: minutes }, ctx, 0));
|
|
7595
|
+
}, "Set the left-open alert and its delay (5, 10, 15, 30 or 60 minutes), which share one wire."),
|
|
7596
|
+
/**
|
|
7597
|
+
* Turn the daily state check on or off, with its times of day. One frame carries both: `1350`
|
|
7598
|
+
* SET_PAYLOAD, sub-command 1291, `{door_enable, door_time_interval}` with each time as minutes since
|
|
7599
|
+
* midnight — the product's settings bundle's own frame.
|
|
7600
|
+
* Verified live on a T8900: param 1291 read back the written times and the app showed them.
|
|
7601
|
+
*/
|
|
7602
|
+
setDailyCheck: method(({ ctx, sink }) => async (on, times) => {
|
|
7603
|
+
const minutes = times.map(minutesOf);
|
|
7604
|
+
if (minutes.some((m) => m === void 0)) {
|
|
7605
|
+
throw new Error(`dailyCheckTimes: ${times.join(",")} must all be HH:MM`);
|
|
7606
|
+
}
|
|
7607
|
+
await sink.dispatch(setPayload(CONTACT_CMD.DAILY_CHECK, { door_enable: asBool(on) ? 1 : 0, door_time_interval: minutes }, ctx, 0));
|
|
7608
|
+
}, "Set the daily state check and its times of day (HH:MM), which share one wire."),
|
|
7609
|
+
/**
|
|
7610
|
+
* The built-in alarm tone, offered where the product's description lists it: a sensor may report the
|
|
7611
|
+
* param while the app offers no such setting on it. Rejected, not clamped, outside the listed tones.
|
|
7612
|
+
* Write: a direct-binary scalar `[ch][value][acct]` at signCode 8, captured live on a T90E0.
|
|
7613
|
+
*/
|
|
7614
|
+
alarmSoundType: {
|
|
7615
|
+
param: CONTACT_CMD.ALARM_SOUND_TYPE,
|
|
7616
|
+
type: "enum",
|
|
7617
|
+
kind: "enum",
|
|
7618
|
+
enumValues: enumLabels(ALARM_TONES),
|
|
7619
|
+
thing: ["device_alarm_sound_type"],
|
|
7620
|
+
provenance: "verified",
|
|
7621
|
+
available: described("device_alarm_sound_type"),
|
|
7622
|
+
args: [{ name: "tone", kind: "enum", description: "Silent (0) or one of the four tones." }],
|
|
7623
|
+
description: "Alarm tone (1507, device_alarm_sound_type): 0=None,1=Water,2=Classics,3=Light,4=Ding.",
|
|
7624
|
+
write: (v, ctx) => {
|
|
7625
|
+
const tone = coerceEnumValue(ALARM_TONES, v);
|
|
7626
|
+
return tone === void 0 ? void 0 : setScalar(CONTACT_CMD.ALARM_SOUND_TYPE, tone, ctx, "auto");
|
|
7627
|
+
}
|
|
7628
|
+
},
|
|
7629
|
+
/**
|
|
7630
|
+
* The built-in alarm volume, a device level 1-26, offered where the product's description lists it.
|
|
7631
|
+
* Write: `1350` SET_PAYLOAD with `mChannel` the device channel, `mValue3` 0, payload `{channel, volume,
|
|
7632
|
+
* transaction}` — the app's own captured frame on a T90E0. Out of range is refused, not clamped.
|
|
7633
|
+
*/
|
|
7634
|
+
alarmVolume: {
|
|
7635
|
+
param: CONTACT_CMD.ALARM_VOLUME,
|
|
7636
|
+
type: "number",
|
|
7637
|
+
kind: "scalar",
|
|
7638
|
+
provenance: "verified",
|
|
7639
|
+
available: described("alarm_volume_value"),
|
|
7640
|
+
min: ALARM_VOLUME_MIN,
|
|
7641
|
+
max: ALARM_VOLUME_MAX,
|
|
7642
|
+
args: [
|
|
7643
|
+
{ name: "level", kind: "scalar", min: ALARM_VOLUME_MIN, max: ALARM_VOLUME_MAX, description: "A device level." }
|
|
7644
|
+
],
|
|
7645
|
+
description: "Alarm volume (1508, alarm_volume_value), a device level 1-26, not a percentage.",
|
|
7646
|
+
write: (v, ctx) => {
|
|
7647
|
+
const level = Number(v);
|
|
7648
|
+
return Number.isInteger(level) && level >= ALARM_VOLUME_MIN && level <= ALARM_VOLUME_MAX ? setPayload(CONTACT_CMD.ALARM_VOLUME, { channel: ctx.channel, volume: level, transaction: String(Date.now()) }, ctx, 0) : void 0;
|
|
7649
|
+
}
|
|
7650
|
+
}
|
|
7651
|
+
};
|
|
7652
|
+
var CONTACT = {
|
|
7653
|
+
capability: "contact",
|
|
7654
|
+
description: "Entry (door/window) sensor: contact state, tamper and bypass, the left-open alert and the daily state check, and the built-in alarm where the product has one.",
|
|
7655
|
+
members: CONTACT_MEMBERS,
|
|
7656
|
+
properties: propertiesOf(CONTACT_MEMBERS),
|
|
7657
|
+
/** A reported entry-sensor contact param (1550) is the verified proof of a contact sensor. */
|
|
7658
|
+
detection: { evidenceParams: [CONTACT_CMD.STATUS] },
|
|
7659
|
+
/**
|
|
7660
|
+
* Inbound `contactState`: the contact param changing on a cloud poll, and the FCM door-sensor push
|
|
7661
|
+
* (`CusPushEvent.DOOR_SENSOR`), whose state is the single-letter `e` — both verified live on a T8900.
|
|
7662
|
+
*/
|
|
7663
|
+
events: [
|
|
7664
|
+
{
|
|
7665
|
+
source: "poll",
|
|
7666
|
+
match: CONTACT_CMD.STATUS,
|
|
7667
|
+
emit: "contactState",
|
|
7668
|
+
derive: (s) => contactOpen(s.source === "poll" ? s.to : void 0)
|
|
7669
|
+
},
|
|
7670
|
+
{
|
|
7671
|
+
source: "push",
|
|
7672
|
+
match: CusPushEvent.DOOR_SENSOR,
|
|
7673
|
+
emit: "contactState",
|
|
7674
|
+
derive: (s) => contactOpen(s.source === "push" ? s.payload?.["e"] : void 0)
|
|
7675
|
+
}
|
|
7676
|
+
],
|
|
7677
|
+
/**
|
|
7678
|
+
* One door movement reaches the SDK up to three times — the station notify, the same value as a push
|
|
7679
|
+
* ~2 s later, then again on the next poll — so `contactState` is announced on a change only.
|
|
7680
|
+
*/
|
|
7681
|
+
stateEvents: [{ event: "contactState", field: "open" }],
|
|
7682
|
+
/**
|
|
7683
|
+
* The station's own notify, the fastest contact signal and the only one that needs no cloud. A
|
|
7684
|
+
* declarative `events` row matches a push id or a poll param, not a param nested in a frame's
|
|
7685
|
+
* `params` array under an inner command, hence the decode hook. A notify without the contact param
|
|
7686
|
+
* (battery or signal only) emits nothing.
|
|
7687
|
+
*/
|
|
7688
|
+
decodeEvent(signal) {
|
|
7689
|
+
const raw = statusParam(signal, CONTACT_CMD.STATUS);
|
|
7690
|
+
if (raw === void 0)
|
|
7691
|
+
return null;
|
|
7692
|
+
return { event: "contactState", payload: contactOpen(raw) };
|
|
7693
|
+
}
|
|
7694
|
+
};
|
|
7695
|
+
|
|
7601
7696
|
// dist/model/capabilities/leak.js
|
|
7697
|
+
var LEAK_PARAM = {
|
|
7698
|
+
/** Water detected (`APP_CMD_WATER_SENSOR_WATER_STATE`, identifier `sensor_water_status`). */
|
|
7699
|
+
WATER: 1820,
|
|
7700
|
+
/** Temperature below the threshold (`APP_CMD_WATER_SENSOR_TEMPERATE_LOW`, `sensor_temperature_low_status`). */
|
|
7701
|
+
LOW_TEMPERATURE: 1822,
|
|
7702
|
+
/** Low-temperature alarm threshold, °F (`APP_CMD_WATER_SENSOR_TEMPERATURE_ALARM`). */
|
|
7703
|
+
LOW_TEMPERATURE_THRESHOLD: 1873
|
|
7704
|
+
};
|
|
7602
7705
|
var LEAK_MEMBERS = {
|
|
7603
7706
|
/**
|
|
7604
|
-
*
|
|
7605
|
-
*
|
|
7606
|
-
* leave this getter uninstalled. The push event is the reliable channel for a leak; this read is
|
|
7607
|
-
* here so the flag has a home once a capture pins a real id.
|
|
7707
|
+
* Water is detected. The app's own `event_sensor_water_detected_notify` is this param changing, so
|
|
7708
|
+
* the state and the alert are one fact.
|
|
7608
7709
|
*/
|
|
7609
7710
|
leakDetected: {
|
|
7610
|
-
param:
|
|
7711
|
+
param: LEAK_PARAM.WATER,
|
|
7611
7712
|
type: "bool",
|
|
7612
7713
|
kind: "boolean",
|
|
7613
|
-
provenance: "
|
|
7614
|
-
description: "Water
|
|
7714
|
+
provenance: "apk",
|
|
7715
|
+
description: "Water detected (1820, sensor_water_status)."
|
|
7716
|
+
},
|
|
7717
|
+
/** The temperature is below {@link LEAK_MEMBERS.lowTemperatureThreshold}: the freeze alarm. */
|
|
7718
|
+
lowTemperature: {
|
|
7719
|
+
param: LEAK_PARAM.LOW_TEMPERATURE,
|
|
7720
|
+
type: "bool",
|
|
7721
|
+
kind: "boolean",
|
|
7722
|
+
provenance: "apk",
|
|
7723
|
+
description: "Temperature below the alarm threshold (1822, sensor_temperature_low_status)."
|
|
7615
7724
|
},
|
|
7616
7725
|
/**
|
|
7617
|
-
*
|
|
7618
|
-
*
|
|
7619
|
-
*
|
|
7726
|
+
* The temperature, in °F, below which the freeze alarm fires: one of 32, 33, 35, 37 or 40 as the
|
|
7727
|
+
* product's description lists them. The write is the bundle's `1350` SET_PAYLOAD on 1873, not yet run
|
|
7728
|
+
* on a device, so no setter is installed.
|
|
7620
7729
|
*/
|
|
7621
|
-
|
|
7622
|
-
param:
|
|
7730
|
+
lowTemperatureThreshold: {
|
|
7731
|
+
param: LEAK_PARAM.LOW_TEMPERATURE_THRESHOLD,
|
|
7623
7732
|
type: "number",
|
|
7624
|
-
kind: "
|
|
7625
|
-
|
|
7626
|
-
|
|
7733
|
+
kind: "enum",
|
|
7734
|
+
enumValues: { 32: "32\xB0F", 33: "33\xB0F", 35: "35\xB0F", 37: "37\xB0F", 40: "40\xB0F" },
|
|
7735
|
+
thing: ["sensor_low_temperature_alarm_threshold"],
|
|
7736
|
+
provenance: "apk",
|
|
7737
|
+
unverified: true,
|
|
7738
|
+
description: "Freeze alarm threshold, \xB0F: 32, 33, 35, 37 or 40 (1873, sensor_low_temperature_alarm_threshold)."
|
|
7627
7739
|
}
|
|
7628
7740
|
};
|
|
7629
7741
|
var LEAK = {
|
|
7630
7742
|
capability: "leak",
|
|
7631
|
-
description: "Water-leak
|
|
7743
|
+
description: "Water-leak and freeze sensor: water and low-temperature state, and the freeze threshold.",
|
|
7632
7744
|
members: LEAK_MEMBERS,
|
|
7633
7745
|
properties: propertiesOf(LEAK_MEMBERS),
|
|
7634
|
-
|
|
7635
|
-
|
|
7746
|
+
/**
|
|
7747
|
+
* A reported water-state param (1820) proves a water sensor, as does a product description listing
|
|
7748
|
+
* `sensor_water_status` — the app's own definition of one, which holds before the sensor has reported.
|
|
7749
|
+
* Never the device's name: that is what its owner typed.
|
|
7750
|
+
*/
|
|
7751
|
+
detection: {
|
|
7752
|
+
evidenceParams: [LEAK_PARAM.WATER],
|
|
7753
|
+
detect: (rec) => rec.things !== void 0 && "sensor_water_status" in rec.things.properties
|
|
7754
|
+
}
|
|
7636
7755
|
};
|
|
7637
7756
|
|
|
7638
7757
|
// dist/model/capabilities/smoke.js
|
|
@@ -7649,18 +7768,6 @@ var SMOKE_MEMBERS = {
|
|
|
7649
7768
|
kind: "boolean",
|
|
7650
7769
|
provenance: "guessed",
|
|
7651
7770
|
description: "Smoke detected. UNVERIFIED: placeholder id pending capture verification."
|
|
7652
|
-
},
|
|
7653
|
-
/**
|
|
7654
|
-
* Unix seconds at which the detector last checked in — param 1551, the same last-seen id the other
|
|
7655
|
-
* sensor capabilities read. A `timestamp` kind takes no `unit`: the number is an instant, not a
|
|
7656
|
-
* duration, and declaring `unit: "s"` beside it fails the value-kind spec.
|
|
7657
|
-
*/
|
|
7658
|
-
lastSeen: {
|
|
7659
|
-
param: 1551,
|
|
7660
|
-
type: "number",
|
|
7661
|
-
kind: "timestamp",
|
|
7662
|
-
provenance: "verified",
|
|
7663
|
-
description: "Last-seen unix timestamp, seconds (verified: param 1551)."
|
|
7664
7771
|
}
|
|
7665
7772
|
};
|
|
7666
7773
|
var SMOKE = {
|
|
@@ -7686,11 +7793,23 @@ var CO_MEMBERS = {
|
|
|
7686
7793
|
kind: "boolean",
|
|
7687
7794
|
provenance: "guessed",
|
|
7688
7795
|
description: "Carbon-monoxide detected. UNVERIFIED: placeholder id pending capture verification."
|
|
7689
|
-
}
|
|
7796
|
+
}
|
|
7797
|
+
};
|
|
7798
|
+
var CO = {
|
|
7799
|
+
capability: "co",
|
|
7800
|
+
description: "Carbon-monoxide (CO) detector alarm state.",
|
|
7801
|
+
members: CO_MEMBERS,
|
|
7802
|
+
properties: propertiesOf(CO_MEMBERS),
|
|
7803
|
+
// CO detectors expose no stable state param; the model/name (co/carbon) is the signal.
|
|
7804
|
+
detection: { modelHints: [/co\b|carbon/i] }
|
|
7805
|
+
};
|
|
7806
|
+
|
|
7807
|
+
// dist/model/capabilities/link.js
|
|
7808
|
+
var LINK_MEMBERS = {
|
|
7690
7809
|
/**
|
|
7691
|
-
* Unix seconds at which the
|
|
7692
|
-
*
|
|
7693
|
-
*
|
|
7810
|
+
* Unix seconds at which the device last checked in, as the device itself reports it — beside
|
|
7811
|
+
* `deviceState(sn).lastSeenMs`, which the SDK derives from the cloud's own update times. A
|
|
7812
|
+
* `timestamp` takes no `unit`: it is an instant, not a duration.
|
|
7694
7813
|
*/
|
|
7695
7814
|
lastSeen: {
|
|
7696
7815
|
param: 1551,
|
|
@@ -7698,15 +7817,23 @@ var CO_MEMBERS = {
|
|
|
7698
7817
|
kind: "timestamp",
|
|
7699
7818
|
provenance: "verified",
|
|
7700
7819
|
description: "Last-seen unix timestamp, seconds (verified: param 1551)."
|
|
7820
|
+
},
|
|
7821
|
+
/** Link quality in dBm as the device measures it, never normalised to a bar count. */
|
|
7822
|
+
rssi: {
|
|
7823
|
+
param: 1141,
|
|
7824
|
+
type: "number",
|
|
7825
|
+
unit: "dBm",
|
|
7826
|
+
kind: "dbm",
|
|
7827
|
+
provenance: "verified",
|
|
7828
|
+
description: "Sub-1G/Wi-Fi signal strength (verified: param 1141 = RSSI)."
|
|
7701
7829
|
}
|
|
7702
7830
|
};
|
|
7703
|
-
var
|
|
7704
|
-
capability: "
|
|
7705
|
-
description: "
|
|
7706
|
-
members:
|
|
7707
|
-
properties: propertiesOf(
|
|
7708
|
-
|
|
7709
|
-
detection: { modelHints: [/co\b|carbon/i] }
|
|
7831
|
+
var LINK = {
|
|
7832
|
+
capability: "link",
|
|
7833
|
+
description: "Check-in timestamp and signal strength, as the device reports them.",
|
|
7834
|
+
members: LINK_MEMBERS,
|
|
7835
|
+
properties: propertiesOf(LINK_MEMBERS),
|
|
7836
|
+
detection: { evidenceParams: [1551, 1141] }
|
|
7710
7837
|
};
|
|
7711
7838
|
|
|
7712
7839
|
// dist/model/capabilities/lock.js
|
|
@@ -7822,18 +7949,6 @@ var LOCK_MEMBERS = {
|
|
|
7822
7949
|
provenance: "verified",
|
|
7823
7950
|
description: "Lock battery level 0-100 (verified: param 1101, alias 6001)."
|
|
7824
7951
|
},
|
|
7825
|
-
/**
|
|
7826
|
-
* Link quality in dBm as the lock measures it, on the shared param 1141. On the `ff09` wire the
|
|
7827
|
-
* actuations are fire-and-forget, so a weak link is silent rather than an error.
|
|
7828
|
-
*/
|
|
7829
|
-
rssi: {
|
|
7830
|
-
param: 1141,
|
|
7831
|
-
type: "number",
|
|
7832
|
-
unit: "dBm",
|
|
7833
|
-
kind: "dbm",
|
|
7834
|
-
provenance: "verified",
|
|
7835
|
-
description: "Lock signal strength (verified: param 1141 = RSSI)."
|
|
7836
|
-
},
|
|
7837
7952
|
/**
|
|
7838
7953
|
* Lock the deadbolt/door. Resolves after sending, without confirming physical state — except on the
|
|
7839
7954
|
* classic Wi-Fi lock, which answers: there it rejects on a device refusal or no reply as well.
|
|
@@ -8053,18 +8168,6 @@ var KEYPAD_MEMBERS = {
|
|
|
8053
8168
|
provenance: "apk",
|
|
8054
8169
|
coerce: isChargingState,
|
|
8055
8170
|
description: "Keypad charging \u2014 derived from APP_CMD_KEYPAD_BATTERY_CHARGER_STATE (1655) as `value \u2209 {0,2}`, matching the app's own decode."
|
|
8056
|
-
},
|
|
8057
|
-
/**
|
|
8058
|
-
* The keypad's link quality in dBm, on the same param 1141 the other sub-1G sensors report signal
|
|
8059
|
-
* strength on. Reported as the device measures it: raw dBm, never normalised to a bar count.
|
|
8060
|
-
*/
|
|
8061
|
-
rssi: {
|
|
8062
|
-
param: 1141,
|
|
8063
|
-
type: "number",
|
|
8064
|
-
unit: "dBm",
|
|
8065
|
-
kind: "dbm",
|
|
8066
|
-
provenance: "verified",
|
|
8067
|
-
description: "Keypad signal strength (verified: param 1141 = RSSI)."
|
|
8068
8171
|
}
|
|
8069
8172
|
};
|
|
8070
8173
|
var KEYPAD = {
|
|
@@ -8206,16 +8309,16 @@ function armingCommand(mode, ctx) {
|
|
|
8206
8309
|
}
|
|
8207
8310
|
return setPayload(ARMING_CMD.SET_ARMING, { mode_type: ARMING_MODE_WIRE[mode], user_name: ctx.accountName }, ctx, 0, void 0, "auto");
|
|
8208
8311
|
}
|
|
8209
|
-
function alarmDelayCommand(mode,
|
|
8312
|
+
function alarmDelayCommand(mode, config2, ctx) {
|
|
8210
8313
|
const data = {
|
|
8211
8314
|
mode_id: ARMING_MODE_WIRE[mode],
|
|
8212
8315
|
count_down_alarm: {
|
|
8213
|
-
channel_list:
|
|
8214
|
-
delay_time:
|
|
8316
|
+
channel_list: config2.countDownAlarm.channelList,
|
|
8317
|
+
delay_time: config2.countDownAlarm.delaySeconds
|
|
8215
8318
|
},
|
|
8216
|
-
count_down_arm: { channel_list:
|
|
8217
|
-
devices:
|
|
8218
|
-
siren_sensor_action:
|
|
8319
|
+
count_down_arm: { channel_list: config2.countDownArm.channelList, delay_time: config2.countDownArm.delaySeconds },
|
|
8320
|
+
devices: config2.devices.map((d) => ({ action: d.action, device_channel: d.deviceChannel })),
|
|
8321
|
+
siren_sensor_action: config2.sirenSensorAction.map((d) => ({ action: d.action, device_channel: d.deviceChannel }))
|
|
8219
8322
|
};
|
|
8220
8323
|
return setJsonRaw(ARMING_CMD.SET_ALL_ACTION, data, ctx, STATION_CHANNEL2);
|
|
8221
8324
|
}
|
|
@@ -8285,9 +8388,9 @@ var ARMING_MEMBERS = {
|
|
|
8285
8388
|
* that integer in `mode_id` with no runtime validation and no readback, so a mode outside this union
|
|
8286
8389
|
* would be the same unverified guess `setMode` refuses.
|
|
8287
8390
|
*/
|
|
8288
|
-
setAlarmDelayConfig: method(({ ctx, sink }) => (mode,
|
|
8391
|
+
setAlarmDelayConfig: method(({ ctx, sink }) => (mode, config2) => {
|
|
8289
8392
|
try {
|
|
8290
|
-
return sink.dispatch(alarmDelayCommand(mode,
|
|
8393
|
+
return sink.dispatch(alarmDelayCommand(mode, config2, ctx));
|
|
8291
8394
|
} catch (e) {
|
|
8292
8395
|
return Promise.reject(e);
|
|
8293
8396
|
}
|
|
@@ -11591,6 +11694,7 @@ var MODULES = [
|
|
|
11591
11694
|
LOCATE,
|
|
11592
11695
|
SMART_DROP,
|
|
11593
11696
|
DISPLAY,
|
|
11697
|
+
LINK,
|
|
11594
11698
|
INFO
|
|
11595
11699
|
];
|
|
11596
11700
|
var BY_CAP = new Map(MODULES.map((m) => [m.capability, m]));
|
|
@@ -12909,6 +13013,16 @@ var SECURITY_PARAMS = {
|
|
|
12909
13013
|
type: "bool",
|
|
12910
13014
|
provenance: "apk"
|
|
12911
13015
|
},
|
|
13016
|
+
1820: {
|
|
13017
|
+
name: "waterSensorWaterState",
|
|
13018
|
+
type: "bool",
|
|
13019
|
+
provenance: "apk"
|
|
13020
|
+
},
|
|
13021
|
+
1822: {
|
|
13022
|
+
name: "waterSensorTemperatureLow",
|
|
13023
|
+
type: "bool",
|
|
13024
|
+
provenance: "apk"
|
|
13025
|
+
},
|
|
12912
13026
|
1825: {
|
|
12913
13027
|
name: "sirenSensorSetAlarmVol",
|
|
12914
13028
|
type: "number",
|
|
@@ -12919,6 +13033,11 @@ var SECURITY_PARAMS = {
|
|
|
12919
13033
|
type: "bool",
|
|
12920
13034
|
provenance: "apk"
|
|
12921
13035
|
},
|
|
13036
|
+
1873: {
|
|
13037
|
+
name: "waterSensorTemperatureAlarm",
|
|
13038
|
+
type: "number",
|
|
13039
|
+
provenance: "apk"
|
|
13040
|
+
},
|
|
12922
13041
|
2001: {
|
|
12923
13042
|
name: "paramTypeOpenDevice",
|
|
12924
13043
|
type: "string",
|
|
@@ -22158,7 +22277,7 @@ var Fmp4Muxer = class {
|
|
|
22158
22277
|
return box("stbl", box("stsd", Buffer.concat([u32(0), u32(1), this.sampleEntry()])), box("stts", Buffer.concat([u32(0), u32(0)])), box("stsc", Buffer.concat([u32(0), u32(0)])), box("stsz", Buffer.concat([u32(0), u32(0), u32(0)])), box("stco", Buffer.concat([u32(0), u32(0)])));
|
|
22159
22278
|
}
|
|
22160
22279
|
sampleEntry() {
|
|
22161
|
-
const { sample, config } = CODEC_BOXES[this.codec];
|
|
22280
|
+
const { sample, config: config2 } = CODEC_BOXES[this.codec];
|
|
22162
22281
|
const head = Buffer.alloc(78);
|
|
22163
22282
|
head.writeUInt16BE(1, 6);
|
|
22164
22283
|
head.writeUInt16BE(this.width, 24);
|
|
@@ -22168,7 +22287,7 @@ var Fmp4Muxer = class {
|
|
|
22168
22287
|
head.writeUInt16BE(1, 40);
|
|
22169
22288
|
head.writeUInt16BE(24, 74);
|
|
22170
22289
|
head.writeInt16BE(-1, 76);
|
|
22171
|
-
const cfg = box(
|
|
22290
|
+
const cfg = box(config2, this.codec === "h264" ? this.avcC() : this.hvcC());
|
|
22172
22291
|
return box(sample, Buffer.concat([head, cfg]));
|
|
22173
22292
|
}
|
|
22174
22293
|
avcC() {
|
|
@@ -22315,8 +22434,8 @@ var Fmp4Muxer = class {
|
|
|
22315
22434
|
return box("mp4a", head, this.esds());
|
|
22316
22435
|
}
|
|
22317
22436
|
esds() {
|
|
22318
|
-
const
|
|
22319
|
-
const specific = descriptor(5,
|
|
22437
|
+
const config2 = this.audioConfig ?? Buffer.from([20, 8]);
|
|
22438
|
+
const specific = descriptor(5, config2);
|
|
22320
22439
|
const decoder = descriptor(4, Buffer.concat([Buffer.from([64, 21, 0, 0, 0]), u32(64e3), u32(32e3), specific]));
|
|
22321
22440
|
const sl = descriptor(6, Buffer.from([2]));
|
|
22322
22441
|
return box("esds", u32(0), descriptor(3, Buffer.concat([u16(2), Buffer.from([0]), decoder, sl])));
|
|
@@ -22549,6 +22668,7 @@ function stationChannels(devices) {
|
|
|
22549
22668
|
|
|
22550
22669
|
// dist/transport/p2p/command-router.js
|
|
22551
22670
|
var DIRECT_CMD_SENDS = 5;
|
|
22671
|
+
var STATION_TABLE_QUERY = Buffer.from("ff00000087030000", "hex");
|
|
22552
22672
|
var LEVEL2_GRACE_MS = 25e3;
|
|
22553
22673
|
var KEEP_LEVEL1_SESSION_MODELS = /* @__PURE__ */ new Set(["T8423"]);
|
|
22554
22674
|
var P2P_STATION_WAITS = {
|
|
@@ -22933,7 +23053,10 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
22933
23053
|
return;
|
|
22934
23054
|
this.tearDownStation(stationSn);
|
|
22935
23055
|
});
|
|
22936
|
-
session.on("level2Ready", ({ cipherId }) =>
|
|
23056
|
+
session.on("level2Ready", ({ cipherId }) => {
|
|
23057
|
+
session.sendRawLevel2Bytes(STATION_TABLE_QUERY, 0, P2P_ENVELOPE.GET_CAMERA_INFO, 8);
|
|
23058
|
+
this.deps.onLevel2Ready(stationSn, cipherId);
|
|
23059
|
+
});
|
|
22937
23060
|
session.on("peerAnswer", (atMs) => {
|
|
22938
23061
|
if (this.manager.get(stationSn) === session)
|
|
22939
23062
|
this.deps.onPeerAnswer?.(stationSn, atMs);
|
|
@@ -23769,11 +23892,11 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
23769
23892
|
* Resolve a scalar `"set-param"` intent to a concrete P2P frame — the ONE place that maps a
|
|
23770
23893
|
* capability's *what* (param + value + {@link ScalarForm}) to the *how* (encryption level + wire):
|
|
23771
23894
|
* `"auto"` defers the level to {@link sendBySessionLevel} — the ONE decision point — while
|
|
23772
|
-
* `"int-string"`
|
|
23895
|
+
* `"int-string"` and `"int-pair"` pin L1 and `"direct-binary"` pins L2.
|
|
23773
23896
|
*/
|
|
23774
23897
|
async resolveScalarParam(sn, param, value, form) {
|
|
23775
|
-
if (form === "int-string") {
|
|
23776
|
-
await this.sendIntStringCommand(sn, param, value);
|
|
23898
|
+
if (form === "int-string" || form === "int-pair") {
|
|
23899
|
+
await this.sendIntStringCommand(sn, param, value, form);
|
|
23777
23900
|
return;
|
|
23778
23901
|
}
|
|
23779
23902
|
if (form === "direct-binary") {
|
|
@@ -24400,10 +24523,10 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
24400
24523
|
* SoloCam-spotlight / Cam2C-3). No level-2 key needed (standalone level-1 ECB). Fire-and-forget,
|
|
24401
24524
|
* repeated for RF resilience.
|
|
24402
24525
|
*/
|
|
24403
|
-
async sendIntStringCommand(sn, outerCmd, value) {
|
|
24526
|
+
async sendIntStringCommand(sn, outerCmd, value, form) {
|
|
24404
24527
|
const { session, channel, accountId } = await this.resolveSession(sn);
|
|
24405
24528
|
for (let i = 0; i < DIRECT_CMD_SENDS; i++) {
|
|
24406
|
-
session.sendIntStringCommand(outerCmd, value, channel, accountId, channel);
|
|
24529
|
+
session.sendIntStringCommand(outerCmd, value, channel, form === "int-pair" ? "" : accountId, channel);
|
|
24407
24530
|
await sleep2(200);
|
|
24408
24531
|
}
|
|
24409
24532
|
}
|
|
@@ -24423,9 +24546,8 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
24423
24546
|
mValue3: 0,
|
|
24424
24547
|
payload: { switch: enabled ? 1 : 0 }
|
|
24425
24548
|
}), "utf-8");
|
|
24426
|
-
const camInfoPre = Buffer.from("ff00000087030000", "hex");
|
|
24427
24549
|
const gap = () => sleep2(150);
|
|
24428
|
-
session.sendRawLevel2Bytes(
|
|
24550
|
+
session.sendRawLevel2Bytes(STATION_TABLE_QUERY, 255, P2P_ENVELOPE.GET_CAMERA_INFO, 8);
|
|
24429
24551
|
await gap();
|
|
24430
24552
|
for (let i = 0; i < 2; i++) {
|
|
24431
24553
|
session.sendRawLevel2Bytes(setJson2, 0, P2P_ENVELOPE.SET_PAYLOAD, 8);
|
|
@@ -24435,7 +24557,7 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
24435
24557
|
session.sendRawLevel2Bytes(setJson2, channel, P2P_ENVELOPE.SET_PAYLOAD, 8);
|
|
24436
24558
|
await gap();
|
|
24437
24559
|
}
|
|
24438
|
-
session.sendRawLevel2Bytes(
|
|
24560
|
+
session.sendRawLevel2Bytes(STATION_TABLE_QUERY, 0, P2P_ENVELOPE.GET_CAMERA_INFO, 8);
|
|
24439
24561
|
}
|
|
24440
24562
|
/**
|
|
24441
24563
|
* Route a control command (`{commandType, data}`) to a device over P2P: HomeBase-attached →
|
|
@@ -25188,7 +25310,7 @@ async function loadNativePeerFactory() {
|
|
|
25188
25310
|
const message = UNBUILT_ARCHES.has(process.arch) ? `the WebRTC transport for a HomeBase S1 Pro (T9000) needs 'node-datachannel', which has no build for ${process.arch}; a 64-bit OS is needed to drive a T9000` : "the WebRTC transport for a HomeBase S1 Pro (T9000) could not load 'node-datachannel'";
|
|
25189
25311
|
throw new Error(message, { cause: err });
|
|
25190
25312
|
}
|
|
25191
|
-
return (name,
|
|
25313
|
+
return (name, config2) => new ndc.PeerConnection(name, config2);
|
|
25192
25314
|
}
|
|
25193
25315
|
var ANSWER_TIMEOUT_MS = 15e3;
|
|
25194
25316
|
var RtcPeer = class extends EventEmitter9 {
|
|
@@ -25220,13 +25342,13 @@ var RtcPeer = class extends EventEmitter9 {
|
|
|
25220
25342
|
if (this.pc)
|
|
25221
25343
|
return;
|
|
25222
25344
|
const createPeer = this.opts.createPeer ?? await loadNativePeerFactory();
|
|
25223
|
-
const
|
|
25345
|
+
const config2 = {
|
|
25224
25346
|
iceServers: turnServers(turn),
|
|
25225
25347
|
iceTransportPolicy: this.opts.iceTransportPolicy ?? "relay",
|
|
25226
25348
|
maxMessageSize: ANKER_MAX_MESSAGE_SIZE,
|
|
25227
25349
|
enableIceTcp: true
|
|
25228
25350
|
};
|
|
25229
|
-
const pc = createPeer("eufy-sdk",
|
|
25351
|
+
const pc = createPeer("eufy-sdk", config2);
|
|
25230
25352
|
this.pc = pc;
|
|
25231
25353
|
pc.onLocalDescription((sdp, type) => {
|
|
25232
25354
|
if (String(type).toLowerCase() === "answer" && this.localAnswer) {
|
|
@@ -27805,15 +27927,15 @@ var TuyaClient = class {
|
|
|
27805
27927
|
endpoint;
|
|
27806
27928
|
http;
|
|
27807
27929
|
session;
|
|
27808
|
-
constructor(
|
|
27809
|
-
this.signer =
|
|
27810
|
-
this.env =
|
|
27811
|
-
this.endpoint =
|
|
27812
|
-
this.http =
|
|
27930
|
+
constructor(config2 = {}) {
|
|
27931
|
+
this.signer = config2.signer ?? new HmacSigner();
|
|
27932
|
+
this.env = config2.env ?? DEFAULT_TUYA_ENV;
|
|
27933
|
+
this.endpoint = config2.endpoint;
|
|
27934
|
+
this.http = config2.http;
|
|
27813
27935
|
this.session = {
|
|
27814
|
-
sid:
|
|
27815
|
-
deviceId:
|
|
27816
|
-
chKey:
|
|
27936
|
+
sid: config2.sid ?? "",
|
|
27937
|
+
deviceId: config2.deviceId ?? genDeviceId(),
|
|
27938
|
+
chKey: config2.chKey ?? TUYA_CHKEY
|
|
27817
27939
|
};
|
|
27818
27940
|
}
|
|
27819
27941
|
/** The current per-install session identity (sid empty until {@link login}). */
|
|
@@ -27976,9 +28098,9 @@ var TuyaCommandRouter = class {
|
|
|
27976
28098
|
* `virtualId`, or the device's serial when the record carries none of them.
|
|
27977
28099
|
*/
|
|
27978
28100
|
snMap = /* @__PURE__ */ new Map();
|
|
27979
|
-
constructor(
|
|
27980
|
-
this.allowUnverified =
|
|
27981
|
-
this.http =
|
|
28101
|
+
constructor(config2 = {}) {
|
|
28102
|
+
this.allowUnverified = config2.allowUnverified ?? false;
|
|
28103
|
+
this.http = config2.http;
|
|
27982
28104
|
}
|
|
27983
28105
|
/**
|
|
27984
28106
|
* Supply credentials for lazy Tuya login. Called by the facade after a successful mega login.
|
|
@@ -33705,7 +33827,6 @@ export {
|
|
|
33705
33827
|
EMPTY_DP_CATALOG,
|
|
33706
33828
|
EUFYLIFE_LOCAL_KEY_HEX,
|
|
33707
33829
|
EUFY_MEGA_LOCAL_KEY_HEX,
|
|
33708
|
-
EntryAlarmTone,
|
|
33709
33830
|
EufyCloudErrorCode,
|
|
33710
33831
|
EufyMega,
|
|
33711
33832
|
FCM,
|
|
@@ -33722,6 +33843,7 @@ export {
|
|
|
33722
33843
|
LEAK_MEMBERS,
|
|
33723
33844
|
LIFE_PARAMS,
|
|
33724
33845
|
LIGHT_MEMBERS,
|
|
33846
|
+
LINK_MEMBERS,
|
|
33725
33847
|
LIVE_TRACE_MESSAGE,
|
|
33726
33848
|
LOCATE_MEMBERS,
|
|
33727
33849
|
LOCK_MEMBERS,
|