@riddix/hamh 2.0.57 → 2.0.58
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/README.md +69 -31
- package/dist/backend/cli.js +2235 -445
- package/dist/frontend/assets/index-BNKv4cEH.js +466 -0
- package/dist/frontend/index.html +1 -1
- package/package.json +5 -5
- package/dist/frontend/assets/index-Ctbzf3aL.js +0 -466
package/dist/backend/cli.js
CHANGED
|
@@ -3358,8 +3358,8 @@ var init_Logger = __esm({
|
|
|
3358
3358
|
* @param maskChar character to mask with
|
|
3359
3359
|
* @param unmaskedLength number of characters to show unmasked in the beginning
|
|
3360
3360
|
*/
|
|
3361
|
-
static maskString(
|
|
3362
|
-
return
|
|
3361
|
+
static maskString(str2, maskChar = "*", unmaskedLength) {
|
|
3362
|
+
return str2.substring(0, unmaskedLength ?? 0) + str2.substring(unmaskedLength ?? 0).replace(/./g, maskChar);
|
|
3363
3363
|
}
|
|
3364
3364
|
/**
|
|
3365
3365
|
* Perform operations in a nested logging context. Messages will be
|
|
@@ -15172,12 +15172,12 @@ var init_HttpSharedEndpoint = __esm({
|
|
|
15172
15172
|
}
|
|
15173
15173
|
}
|
|
15174
15174
|
};
|
|
15175
|
-
target.ws = async (request,
|
|
15175
|
+
target.ws = async (request, connect2) => {
|
|
15176
15176
|
let connected = false;
|
|
15177
15177
|
for (const instance of this.#instances) {
|
|
15178
15178
|
await instance.ws?.(request, async () => {
|
|
15179
15179
|
connected = true;
|
|
15180
|
-
return
|
|
15180
|
+
return connect2();
|
|
15181
15181
|
});
|
|
15182
15182
|
if (connected) {
|
|
15183
15183
|
break;
|
|
@@ -63789,9 +63789,9 @@ var init_SecureSession = __esm({
|
|
|
63789
63789
|
isSecure = true;
|
|
63790
63790
|
};
|
|
63791
63791
|
((SecureSession2) => {
|
|
63792
|
-
function assert(session,
|
|
63792
|
+
function assert(session, errorText2) {
|
|
63793
63793
|
if (!session?.isSecure) {
|
|
63794
|
-
throw new MatterFlowError(
|
|
63794
|
+
throw new MatterFlowError(errorText2 ?? "Unsecured session in secure context");
|
|
63795
63795
|
}
|
|
63796
63796
|
}
|
|
63797
63797
|
SecureSession2.assert = assert;
|
|
@@ -64116,9 +64116,9 @@ var init_NodeSession = __esm({
|
|
|
64116
64116
|
}
|
|
64117
64117
|
};
|
|
64118
64118
|
((NodeSession2) => {
|
|
64119
|
-
function assert(session,
|
|
64119
|
+
function assert(session, errorText2) {
|
|
64120
64120
|
if (!is(session)) {
|
|
64121
|
-
throw new MatterFlowError(
|
|
64121
|
+
throw new MatterFlowError(errorText2 ?? "Unsecured session in secure context");
|
|
64122
64122
|
}
|
|
64123
64123
|
}
|
|
64124
64124
|
NodeSession2.assert = assert;
|
|
@@ -66890,9 +66890,9 @@ var init_FabricManager = __esm({
|
|
|
66890
66890
|
fabric = this.#fabrics.get("fabricIndex", identifier);
|
|
66891
66891
|
}
|
|
66892
66892
|
if (fabric === void 0 || fabric.isDeleting) {
|
|
66893
|
-
const
|
|
66893
|
+
const str2 = typeof identifier === "bigint" ? GlobalFabricId.strOf(identifier) : `#${identifier}`;
|
|
66894
66894
|
throw new FabricNotFoundError(
|
|
66895
|
-
`Fabric index ${
|
|
66895
|
+
`Fabric index ${str2} ${fabric === void 0 ? "does not exist" : "is deleted"}`
|
|
66896
66896
|
);
|
|
66897
66897
|
}
|
|
66898
66898
|
return fabric;
|
|
@@ -67384,9 +67384,9 @@ var init_GroupSession = __esm({
|
|
|
67384
67384
|
}
|
|
67385
67385
|
};
|
|
67386
67386
|
((GroupSession2) => {
|
|
67387
|
-
function assert(session,
|
|
67387
|
+
function assert(session, errorText2) {
|
|
67388
67388
|
if (!is(session)) {
|
|
67389
|
-
throw new MatterFlowError(
|
|
67389
|
+
throw new MatterFlowError(errorText2 ?? "Unsecured session in secure context");
|
|
67390
67390
|
}
|
|
67391
67391
|
}
|
|
67392
67392
|
GroupSession2.assert = assert;
|
|
@@ -72480,11 +72480,11 @@ var init_errors5 = __esm({
|
|
|
72480
72480
|
};
|
|
72481
72481
|
DatatypeError = class extends ValidateError {
|
|
72482
72482
|
constructor(path14, type, value, code) {
|
|
72483
|
-
let
|
|
72484
|
-
if (
|
|
72485
|
-
|
|
72483
|
+
let str2 = `${value}`;
|
|
72484
|
+
if (str2.length > 60) {
|
|
72485
|
+
str2 = `${str2.substring(60)}\u2026`;
|
|
72486
72486
|
}
|
|
72487
|
-
super(path14, `Value "${
|
|
72487
|
+
super(path14, `Value "${str2}" is not ${type}`, code);
|
|
72488
72488
|
}
|
|
72489
72489
|
};
|
|
72490
72490
|
ConstraintError = class extends ValidateError {
|
|
@@ -85817,23 +85817,23 @@ var init_CommissioningConnectionPool = __esm({
|
|
|
85817
85817
|
addDevices(devices) {
|
|
85818
85818
|
const devicesByKey = /* @__PURE__ */ new Map();
|
|
85819
85819
|
for (const device of devices) {
|
|
85820
|
-
const
|
|
85821
|
-
if (this.#invalidCredentialDevices.has(
|
|
85820
|
+
const deviceKey2 = device.deviceIdentifier;
|
|
85821
|
+
if (this.#invalidCredentialDevices.has(deviceKey2)) {
|
|
85822
85822
|
continue;
|
|
85823
85823
|
}
|
|
85824
|
-
const existing = devicesByKey.get(
|
|
85824
|
+
const existing = devicesByKey.get(deviceKey2);
|
|
85825
85825
|
if (existing === void 0) {
|
|
85826
|
-
devicesByKey.set(
|
|
85826
|
+
devicesByKey.set(deviceKey2, { ...device, addresses: [...device.addresses] });
|
|
85827
85827
|
} else {
|
|
85828
85828
|
existing.addresses = [...existing.addresses, ...device.addresses];
|
|
85829
|
-
devicesByKey.set(
|
|
85829
|
+
devicesByKey.set(deviceKey2, { ...existing, ...device, addresses: existing.addresses });
|
|
85830
85830
|
}
|
|
85831
85831
|
}
|
|
85832
|
-
for (const [
|
|
85833
|
-
let state = this.#devices.get(
|
|
85832
|
+
for (const [deviceKey2, device] of devicesByKey.entries()) {
|
|
85833
|
+
let state = this.#devices.get(deviceKey2);
|
|
85834
85834
|
if (state === void 0) {
|
|
85835
85835
|
state = { device, addresses: /* @__PURE__ */ new Map() };
|
|
85836
|
-
this.#devices.set(
|
|
85836
|
+
this.#devices.set(deviceKey2, state);
|
|
85837
85837
|
} else {
|
|
85838
85838
|
state.device = { ...state.device, ...device, addresses: device.addresses };
|
|
85839
85839
|
}
|
|
@@ -85847,9 +85847,9 @@ var init_CommissioningConnectionPool = __esm({
|
|
|
85847
85847
|
* Permanently drop a device whose passcode is wrong. All its addresses are already in-flight and will
|
|
85848
85848
|
* receive the per-device abort signal; new attempts for this device will be skipped.
|
|
85849
85849
|
*/
|
|
85850
|
-
markInvalidCredentials(
|
|
85851
|
-
this.#invalidCredentialDevices.add(
|
|
85852
|
-
this.#devices.delete(
|
|
85850
|
+
markInvalidCredentials(deviceKey2) {
|
|
85851
|
+
this.#invalidCredentialDevices.add(deviceKey2);
|
|
85852
|
+
this.#devices.delete(deviceKey2);
|
|
85853
85853
|
}
|
|
85854
85854
|
/**
|
|
85855
85855
|
* Returns all viable (device, address) pairs, optionally excluding attempt keys already in-flight.
|
|
@@ -85858,11 +85858,11 @@ var init_CommissioningConnectionPool = __esm({
|
|
|
85858
85858
|
*/
|
|
85859
85859
|
availableCandidates(inFlight) {
|
|
85860
85860
|
const result = [];
|
|
85861
|
-
for (const [
|
|
85861
|
+
for (const [deviceKey2, state] of this.#devices) {
|
|
85862
85862
|
for (const [addressUrl, address] of state.addresses) {
|
|
85863
|
-
const attemptKey = `${
|
|
85863
|
+
const attemptKey = `${deviceKey2}:${addressUrl}`;
|
|
85864
85864
|
if (inFlight?.has(attemptKey)) continue;
|
|
85865
|
-
result.push({ attemptKey, deviceKey, device: state.device, address });
|
|
85865
|
+
result.push({ attemptKey, deviceKey: deviceKey2, device: state.device, address });
|
|
85866
85866
|
}
|
|
85867
85867
|
}
|
|
85868
85868
|
return result;
|
|
@@ -85883,11 +85883,11 @@ async function CommissioningConnection(options) {
|
|
|
85883
85883
|
const inFlight = /* @__PURE__ */ new Set();
|
|
85884
85884
|
const pending2 = /* @__PURE__ */ new Set();
|
|
85885
85885
|
const deviceAborts = /* @__PURE__ */ new Map();
|
|
85886
|
-
const getDeviceAbort = (
|
|
85887
|
-
let ac = deviceAborts.get(
|
|
85886
|
+
const getDeviceAbort = (deviceKey2) => {
|
|
85887
|
+
let ac = deviceAborts.get(deviceKey2);
|
|
85888
85888
|
if (ac === void 0) {
|
|
85889
85889
|
ac = new AbortController();
|
|
85890
|
-
deviceAborts.set(
|
|
85890
|
+
deviceAborts.set(deviceKey2, ac);
|
|
85891
85891
|
}
|
|
85892
85892
|
return ac;
|
|
85893
85893
|
};
|
|
@@ -88120,10 +88120,10 @@ var init_NetworkProfile = __esm({
|
|
|
88120
88120
|
for (const key of Object.keys(options)) {
|
|
88121
88121
|
const override = options[key];
|
|
88122
88122
|
if (override !== void 0) {
|
|
88123
|
-
const { connect, probeAddress, ...rest } = override;
|
|
88123
|
+
const { connect: connect2, probeAddress, ...rest } = override;
|
|
88124
88124
|
const merged = merge(base[key], rest);
|
|
88125
|
-
if (
|
|
88126
|
-
merged.connect = merge(base[key].connect ?? {},
|
|
88125
|
+
if (connect2 !== void 0) {
|
|
88126
|
+
merged.connect = merge(base[key].connect ?? {}, connect2);
|
|
88127
88127
|
}
|
|
88128
88128
|
if (probeAddress !== void 0) {
|
|
88129
88129
|
merged.probeAddress = merge(
|
|
@@ -88574,7 +88574,7 @@ async function PeerConnection(peer, context, options) {
|
|
|
88574
88574
|
}
|
|
88575
88575
|
const addressAbort = new Abort({ abort: overallAbort });
|
|
88576
88576
|
lastAttemptAt = Time.nowMs;
|
|
88577
|
-
const finished2 =
|
|
88577
|
+
const finished2 = connect2(address, addressAbort).finally(() => {
|
|
88578
88578
|
try {
|
|
88579
88579
|
if (attempts.get(address)?.finished === finished2) {
|
|
88580
88580
|
attempts.delete(address);
|
|
@@ -88757,7 +88757,7 @@ async function PeerConnection(peer, context, options) {
|
|
|
88757
88757
|
__callDispose28(_stack2, _error2, _hasError2);
|
|
88758
88758
|
}
|
|
88759
88759
|
}
|
|
88760
|
-
async function
|
|
88760
|
+
async function connect2(address, addressAbort) {
|
|
88761
88761
|
var _stack2 = [];
|
|
88762
88762
|
try {
|
|
88763
88763
|
const addrNo = ++addrsAttempted;
|
|
@@ -92204,9 +92204,9 @@ var init_Agent = __esm({
|
|
|
92204
92204
|
/**
|
|
92205
92205
|
* Create a new {@link Agent} that supports the specified behaviors.
|
|
92206
92206
|
*/
|
|
92207
|
-
static for(type,
|
|
92207
|
+
static for(type, behaviors2) {
|
|
92208
92208
|
const props = {};
|
|
92209
|
-
Object.values(
|
|
92209
|
+
Object.values(behaviors2).forEach((behavior) => {
|
|
92210
92210
|
props[behavior.id] = {
|
|
92211
92211
|
get() {
|
|
92212
92212
|
return this.get(behavior);
|
|
@@ -101287,7 +101287,7 @@ var init_IcdManagementServer = __esm({
|
|
|
101287
101287
|
__typeError34 = (msg) => {
|
|
101288
101288
|
throw TypeError(msg);
|
|
101289
101289
|
};
|
|
101290
|
-
__defNormalProp = (
|
|
101290
|
+
__defNormalProp = (obj2, key, value) => key in obj2 ? __defProp2(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
101291
101291
|
__name = (target, value) => __defProp2(target, "name", { value, configurable: true });
|
|
101292
101292
|
__decoratorStart = (base) => [, , , __create(base?.[__knownSymbol34("metadata")] ?? null)];
|
|
101293
101293
|
__decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -101322,12 +101322,12 @@ var init_IcdManagementServer = __esm({
|
|
|
101322
101322
|
}
|
|
101323
101323
|
return k || __decoratorMetadata(array, target), desc && __defProp2(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
101324
101324
|
};
|
|
101325
|
-
__publicField = (
|
|
101326
|
-
__accessCheck = (
|
|
101327
|
-
__privateIn = (member,
|
|
101328
|
-
__privateGet = (
|
|
101329
|
-
__privateSet = (
|
|
101330
|
-
__privateMethod = (
|
|
101325
|
+
__publicField = (obj2, key, value) => __defNormalProp(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
101326
|
+
__accessCheck = (obj2, member, msg) => member.has(obj2) || __typeError34("Cannot " + msg);
|
|
101327
|
+
__privateIn = (member, obj2) => Object(obj2) !== obj2 ? __typeError34('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
101328
|
+
__privateGet = (obj2, member, getter) => (__accessCheck(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
101329
|
+
__privateSet = (obj2, member, value, setter) => (__accessCheck(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
101330
|
+
__privateMethod = (obj2, member, method) => (__accessCheck(obj2, member, "access private method"), method);
|
|
101331
101331
|
__using34 = (stack, value, async) => {
|
|
101332
101332
|
if (value != null) {
|
|
101333
101333
|
if (typeof value !== "object" && typeof value !== "function") __typeError34("Object expected");
|
|
@@ -102024,7 +102024,7 @@ var init_NetworkServer = __esm({
|
|
|
102024
102024
|
__typeError35 = (msg) => {
|
|
102025
102025
|
throw TypeError(msg);
|
|
102026
102026
|
};
|
|
102027
|
-
__defNormalProp2 = (
|
|
102027
|
+
__defNormalProp2 = (obj2, key, value) => key in obj2 ? __defProp3(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
102028
102028
|
__name2 = (target, value) => __defProp3(target, "name", { value, configurable: true });
|
|
102029
102029
|
__decoratorStart2 = (base) => [, , , __create2(base?.[__knownSymbol35("metadata")] ?? null)];
|
|
102030
102030
|
__decoratorStrings2 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -102059,12 +102059,12 @@ var init_NetworkServer = __esm({
|
|
|
102059
102059
|
}
|
|
102060
102060
|
return k || __decoratorMetadata2(array, target), desc && __defProp3(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
102061
102061
|
};
|
|
102062
|
-
__publicField2 = (
|
|
102063
|
-
__accessCheck2 = (
|
|
102064
|
-
__privateIn2 = (member,
|
|
102065
|
-
__privateGet2 = (
|
|
102066
|
-
__privateSet2 = (
|
|
102067
|
-
__privateMethod2 = (
|
|
102062
|
+
__publicField2 = (obj2, key, value) => __defNormalProp2(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
102063
|
+
__accessCheck2 = (obj2, member, msg) => member.has(obj2) || __typeError35("Cannot " + msg);
|
|
102064
|
+
__privateIn2 = (member, obj2) => Object(obj2) !== obj2 ? __typeError35('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
102065
|
+
__privateGet2 = (obj2, member, getter) => (__accessCheck2(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
102066
|
+
__privateSet2 = (obj2, member, value, setter) => (__accessCheck2(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
102067
|
+
__privateMethod2 = (obj2, member, method) => (__accessCheck2(obj2, member, "access private method"), method);
|
|
102068
102068
|
logger117 = Logger.get("NetworkingServer");
|
|
102069
102069
|
NetworkServer = class extends NetworkBehavior {
|
|
102070
102070
|
initialize() {
|
|
@@ -102608,7 +102608,7 @@ var init_CommissioningServer = __esm({
|
|
|
102608
102608
|
throw new ImplementationError("pairingCodes read before commissioning was initialized");
|
|
102609
102609
|
}
|
|
102610
102610
|
const bi = endpoint.stateOf(BasicInformationBehavior);
|
|
102611
|
-
const
|
|
102611
|
+
const net2 = endpoint.stateOf(NetworkServer);
|
|
102612
102612
|
const qrPairingCode = QrPairingCodeCodec.encode([
|
|
102613
102613
|
{
|
|
102614
102614
|
version: 0,
|
|
@@ -102617,7 +102617,7 @@ var init_CommissioningServer = __esm({
|
|
|
102617
102617
|
flowType: comm.flowType,
|
|
102618
102618
|
discriminator: comm.discriminator,
|
|
102619
102619
|
passcode: comm.passcode,
|
|
102620
|
-
discoveryCapabilities: DiscoveryCapabilitiesSchema.encode(
|
|
102620
|
+
discoveryCapabilities: DiscoveryCapabilitiesSchema.encode(net2.discoveryCapabilities)
|
|
102621
102621
|
}
|
|
102622
102622
|
]);
|
|
102623
102623
|
return {
|
|
@@ -106420,10 +106420,10 @@ function MutableEndpoint(options) {
|
|
|
106420
106420
|
}
|
|
106421
106421
|
return this.with(...newBehaviors);
|
|
106422
106422
|
},
|
|
106423
|
-
with(...
|
|
106423
|
+
with(...behaviors2) {
|
|
106424
106424
|
const serverArgs = [];
|
|
106425
106425
|
const clientArgs = [];
|
|
106426
|
-
for (const b of
|
|
106426
|
+
for (const b of behaviors2) {
|
|
106427
106427
|
if (isClientBehavior(b)) {
|
|
106428
106428
|
clientArgs.push(b);
|
|
106429
106429
|
} else {
|
|
@@ -106436,10 +106436,10 @@ function MutableEndpoint(options) {
|
|
|
106436
106436
|
clientClusters: clientArgs.length ? SupportedClientClusters.extend(this.clientClusters, clientArgs) : this.clientClusters
|
|
106437
106437
|
});
|
|
106438
106438
|
},
|
|
106439
|
-
withBehaviors(...
|
|
106439
|
+
withBehaviors(...behaviors2) {
|
|
106440
106440
|
return MutableEndpoint({
|
|
106441
106441
|
...options,
|
|
106442
|
-
behaviors: SupportedBehaviors.extend(this.behaviors,
|
|
106442
|
+
behaviors: SupportedBehaviors.extend(this.behaviors, behaviors2),
|
|
106443
106443
|
clientClusters: this.clientClusters
|
|
106444
106444
|
});
|
|
106445
106445
|
},
|
|
@@ -109204,7 +109204,7 @@ var init_IcdClient = __esm({
|
|
|
109204
109204
|
__typeError40 = (msg) => {
|
|
109205
109205
|
throw TypeError(msg);
|
|
109206
109206
|
};
|
|
109207
|
-
__defNormalProp3 = (
|
|
109207
|
+
__defNormalProp3 = (obj2, key, value) => key in obj2 ? __defProp4(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
109208
109208
|
__name3 = (target, value) => __defProp4(target, "name", { value, configurable: true });
|
|
109209
109209
|
__decoratorStart3 = (base) => [, , , __create3(base?.[__knownSymbol40("metadata")] ?? null)];
|
|
109210
109210
|
__decoratorStrings3 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -109239,12 +109239,12 @@ var init_IcdClient = __esm({
|
|
|
109239
109239
|
}
|
|
109240
109240
|
return k || __decoratorMetadata3(array, target), desc && __defProp4(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
109241
109241
|
};
|
|
109242
|
-
__publicField3 = (
|
|
109243
|
-
__accessCheck3 = (
|
|
109244
|
-
__privateIn3 = (member,
|
|
109245
|
-
__privateGet3 = (
|
|
109246
|
-
__privateSet3 = (
|
|
109247
|
-
__privateMethod3 = (
|
|
109242
|
+
__publicField3 = (obj2, key, value) => __defNormalProp3(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
109243
|
+
__accessCheck3 = (obj2, member, msg) => member.has(obj2) || __typeError40("Cannot " + msg);
|
|
109244
|
+
__privateIn3 = (member, obj2) => Object(obj2) !== obj2 ? __typeError40('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
109245
|
+
__privateGet3 = (obj2, member, getter) => (__accessCheck3(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
109246
|
+
__privateSet3 = (obj2, member, value, setter) => (__accessCheck3(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
109247
|
+
__privateMethod3 = (obj2, member, method) => (__accessCheck3(obj2, member, "access private method"), method);
|
|
109248
109248
|
logger132 = Logger.get("IcdClient");
|
|
109249
109249
|
IcdClient = class _IcdClient extends Behavior {
|
|
109250
109250
|
static early = true;
|
|
@@ -115752,7 +115752,7 @@ var init_CommissioningClient = __esm({
|
|
|
115752
115752
|
__typeError42 = (msg) => {
|
|
115753
115753
|
throw TypeError(msg);
|
|
115754
115754
|
};
|
|
115755
|
-
__defNormalProp4 = (
|
|
115755
|
+
__defNormalProp4 = (obj2, key, value) => key in obj2 ? __defProp5(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
115756
115756
|
__name4 = (target, value) => __defProp5(target, "name", { value, configurable: true });
|
|
115757
115757
|
__decoratorStart4 = (base) => [, , , __create4(base?.[__knownSymbol42("metadata")] ?? null)];
|
|
115758
115758
|
__decoratorStrings4 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -115787,12 +115787,12 @@ var init_CommissioningClient = __esm({
|
|
|
115787
115787
|
}
|
|
115788
115788
|
return k || __decoratorMetadata4(array, target), desc && __defProp5(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
115789
115789
|
};
|
|
115790
|
-
__publicField4 = (
|
|
115791
|
-
__accessCheck4 = (
|
|
115792
|
-
__privateIn4 = (member,
|
|
115793
|
-
__privateGet4 = (
|
|
115794
|
-
__privateSet4 = (
|
|
115795
|
-
__privateMethod4 = (
|
|
115790
|
+
__publicField4 = (obj2, key, value) => __defNormalProp4(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
115791
|
+
__accessCheck4 = (obj2, member, msg) => member.has(obj2) || __typeError42("Cannot " + msg);
|
|
115792
|
+
__privateIn4 = (member, obj2) => Object(obj2) !== obj2 ? __typeError42('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
115793
|
+
__privateGet4 = (obj2, member, getter) => (__accessCheck4(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
115794
|
+
__privateSet4 = (obj2, member, value, setter) => (__accessCheck4(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
115795
|
+
__privateMethod4 = (obj2, member, method) => (__accessCheck4(obj2, member, "access private method"), method);
|
|
115796
115796
|
logger144 = Logger.get("CommissioningClient");
|
|
115797
115797
|
CommissioningClient = class _CommissioningClient extends Behavior {
|
|
115798
115798
|
static early = true;
|
|
@@ -131474,140 +131474,197 @@ var init_controller_compat = __esm({
|
|
|
131474
131474
|
// 0x6006 Google Home
|
|
131475
131475
|
4631: "alexa",
|
|
131476
131476
|
// 0x1217 Amazon Alexa
|
|
131477
|
-
|
|
131478
|
-
// 0x1160 Amazon (some Alexa ecosystems)
|
|
131479
|
-
4447: "aqara"
|
|
131477
|
+
4447: "aqara",
|
|
131480
131478
|
// 0x115F Aqara Home
|
|
131479
|
+
4362: "smartthings"
|
|
131480
|
+
// 0x110A SmartThings
|
|
131481
131481
|
};
|
|
131482
131482
|
deviceTypeIdSupport = {
|
|
131483
|
-
|
|
131484
|
-
apple: "no",
|
|
131485
|
-
google: "yes",
|
|
131486
|
-
alexa: "yes",
|
|
131487
|
-
aqara: "yes",
|
|
131488
|
-
note: "Apple Home has no standalone fan."
|
|
131489
|
-
},
|
|
131490
|
-
45: {
|
|
131491
|
-
apple: "no",
|
|
131492
|
-
google: "yes",
|
|
131493
|
-
alexa: "yes",
|
|
131494
|
-
aqara: "yes",
|
|
131495
|
-
note: "Apple Home does not list air purifiers."
|
|
131496
|
-
},
|
|
131483
|
+
// speaker
|
|
131497
131484
|
34: {
|
|
131498
131485
|
apple: "no",
|
|
131499
131486
|
google: "yes",
|
|
131500
131487
|
alexa: "no",
|
|
131501
131488
|
aqara: "yes",
|
|
131502
|
-
|
|
131489
|
+
smartthings: "yes",
|
|
131490
|
+
note: "Apple and Alexa do not show Matter speakers."
|
|
131503
131491
|
},
|
|
131492
|
+
// basic video player
|
|
131504
131493
|
40: {
|
|
131505
131494
|
apple: "no",
|
|
131506
131495
|
google: "no",
|
|
131507
131496
|
alexa: "no",
|
|
131508
131497
|
aqara: "yes",
|
|
131509
|
-
|
|
131498
|
+
smartthings: "yes",
|
|
131499
|
+
note: "TV/media types only show in Aqara Home and SmartThings."
|
|
131510
131500
|
},
|
|
131501
|
+
// pressure sensor
|
|
131511
131502
|
773: {
|
|
131512
131503
|
apple: "no",
|
|
131513
131504
|
google: "yes",
|
|
131514
131505
|
alexa: "no",
|
|
131515
131506
|
aqara: "yes",
|
|
131516
|
-
|
|
131507
|
+
smartthings: "yes",
|
|
131508
|
+
note: "Google Home, Aqara and SmartThings show pressure sensors."
|
|
131517
131509
|
},
|
|
131510
|
+
// flow sensor
|
|
131518
131511
|
774: {
|
|
131519
131512
|
apple: "no",
|
|
131520
131513
|
google: "yes",
|
|
131521
131514
|
alexa: "no",
|
|
131522
|
-
aqara: "unknown",
|
|
131523
|
-
note: "Only Google Home shows flow sensors."
|
|
131524
|
-
},
|
|
131525
|
-
44: {
|
|
131526
|
-
apple: "no",
|
|
131527
|
-
google: "yes",
|
|
131528
|
-
alexa: "yes",
|
|
131529
131515
|
aqara: "yes",
|
|
131530
|
-
|
|
131516
|
+
smartthings: "yes",
|
|
131517
|
+
note: "Google Home, Aqara and SmartThings show flow sensors."
|
|
131531
131518
|
},
|
|
131519
|
+
// solar power
|
|
131532
131520
|
23: {
|
|
131533
131521
|
apple: "no",
|
|
131534
131522
|
google: "no",
|
|
131535
131523
|
alexa: "unknown",
|
|
131536
|
-
aqara: "
|
|
131537
|
-
|
|
131524
|
+
aqara: "yes",
|
|
131525
|
+
smartthings: "yes",
|
|
131526
|
+
note: "SolarPower is only shown standalone by Aqara and SmartThings."
|
|
131538
131527
|
},
|
|
131528
|
+
// electrical meter
|
|
131539
131529
|
1300: {
|
|
131540
131530
|
apple: "no",
|
|
131541
131531
|
google: "yes",
|
|
131542
131532
|
alexa: "no",
|
|
131543
131533
|
aqara: "unknown",
|
|
131544
|
-
|
|
131534
|
+
smartthings: "unknown",
|
|
131535
|
+
note: "ElectricalMeter shows in Google Home; Apple and Alexa do not show standalone power/energy."
|
|
131536
|
+
},
|
|
131537
|
+
// electrical utility meter
|
|
131538
|
+
1297: {
|
|
131539
|
+
apple: "no",
|
|
131540
|
+
google: "yes",
|
|
131541
|
+
alexa: "no",
|
|
131542
|
+
aqara: "unknown",
|
|
131543
|
+
smartthings: "unknown",
|
|
131544
|
+
note: "ElectricalUtilityMeter shows in Google Home only."
|
|
131545
131545
|
},
|
|
131546
|
+
// battery storage
|
|
131546
131547
|
24: {
|
|
131547
131548
|
apple: "no",
|
|
131548
131549
|
google: "no",
|
|
131549
131550
|
alexa: "no",
|
|
131550
131551
|
aqara: "yes",
|
|
131551
|
-
|
|
131552
|
+
smartthings: "yes",
|
|
131553
|
+
note: "Aqara and SmartThings list battery storage; others show battery inside a device."
|
|
131552
131554
|
},
|
|
131555
|
+
// EVSE
|
|
131553
131556
|
1292: {
|
|
131554
131557
|
apple: "no",
|
|
131555
131558
|
google: "no",
|
|
131556
131559
|
alexa: "no",
|
|
131557
131560
|
aqara: "yes",
|
|
131558
|
-
|
|
131561
|
+
smartthings: "yes",
|
|
131562
|
+
note: "HA, Aqara and SmartThings render EnergyEvse (SmartThings sets the limit via EnableCharging and addresses modes by list position). Bridged EVSE can break Alexa device recognition, keep it off Alexa bridges."
|
|
131559
131563
|
},
|
|
131564
|
+
// water heater
|
|
131565
|
+
1295: {
|
|
131566
|
+
apple: "no",
|
|
131567
|
+
google: "no",
|
|
131568
|
+
alexa: "unknown",
|
|
131569
|
+
aqara: "yes",
|
|
131570
|
+
smartthings: "yes",
|
|
131571
|
+
note: "Matter 1.4 Water Heater, only Aqara and SmartThings list it."
|
|
131572
|
+
},
|
|
131573
|
+
// mode select
|
|
131560
131574
|
39: {
|
|
131561
131575
|
apple: "no",
|
|
131562
131576
|
google: "no",
|
|
131563
131577
|
alexa: "no",
|
|
131564
|
-
aqara: "
|
|
131578
|
+
aqara: "no",
|
|
131579
|
+
smartthings: "unknown",
|
|
131565
131580
|
note: "Mode Select is not supported here (Google #356)."
|
|
131566
131581
|
},
|
|
131567
|
-
|
|
131582
|
+
// water valve
|
|
131583
|
+
66: {
|
|
131584
|
+
apple: "no",
|
|
131585
|
+
google: "no",
|
|
131586
|
+
alexa: "no",
|
|
131587
|
+
aqara: "yes",
|
|
131588
|
+
smartthings: "yes"
|
|
131589
|
+
},
|
|
131590
|
+
// pump
|
|
131568
131591
|
771: {
|
|
131569
131592
|
apple: "no",
|
|
131570
131593
|
google: "yes",
|
|
131571
131594
|
alexa: "no",
|
|
131572
131595
|
aqara: "yes",
|
|
131573
|
-
|
|
131596
|
+
smartthings: "yes",
|
|
131597
|
+
note: "Google Home, Aqara and SmartThings show pumps."
|
|
131574
131598
|
},
|
|
131599
|
+
// rain sensor
|
|
131575
131600
|
68: {
|
|
131576
131601
|
apple: "no",
|
|
131577
131602
|
google: "no",
|
|
131578
131603
|
alexa: "no",
|
|
131579
131604
|
aqara: "yes",
|
|
131605
|
+
smartthings: "yes",
|
|
131580
131606
|
note: "Newer Matter detector, thin support; Alexa may reject it (#365)."
|
|
131581
131607
|
},
|
|
131608
|
+
// water freeze detector
|
|
131582
131609
|
65: {
|
|
131583
131610
|
apple: "no",
|
|
131584
131611
|
google: "no",
|
|
131585
131612
|
alexa: "no",
|
|
131586
131613
|
aqara: "yes",
|
|
131614
|
+
smartthings: "yes",
|
|
131587
131615
|
note: "Newer Matter detector, thin support; Alexa may reject it (#365)."
|
|
131588
131616
|
},
|
|
131617
|
+
// water leak detector
|
|
131589
131618
|
67: {
|
|
131590
131619
|
apple: "yes",
|
|
131591
131620
|
google: "no",
|
|
131592
|
-
alexa: "
|
|
131621
|
+
alexa: "no",
|
|
131593
131622
|
aqara: "yes",
|
|
131594
|
-
|
|
131623
|
+
smartthings: "yes",
|
|
131624
|
+
note: "Alexa has no capability for it and it can take an Alexa bridge offline (#365)."
|
|
131595
131625
|
},
|
|
131596
|
-
|
|
131597
|
-
|
|
131598
|
-
apple: "
|
|
131626
|
+
// laundry washer
|
|
131627
|
+
115: {
|
|
131628
|
+
apple: "unknown",
|
|
131629
|
+
google: "yes",
|
|
131630
|
+
alexa: "no",
|
|
131631
|
+
aqara: "yes",
|
|
131632
|
+
smartthings: "yes",
|
|
131633
|
+
note: "iOS 27 knows the type, but it's not confirmed that Apple Home shows it."
|
|
131634
|
+
},
|
|
131635
|
+
// laundry dryer
|
|
131636
|
+
124: {
|
|
131637
|
+
apple: "unknown",
|
|
131599
131638
|
google: "no",
|
|
131600
|
-
alexa: "
|
|
131601
|
-
aqara: "
|
|
131602
|
-
|
|
131639
|
+
alexa: "no",
|
|
131640
|
+
aqara: "yes",
|
|
131641
|
+
smartthings: "yes",
|
|
131642
|
+
note: "iOS 27 knows the type, but it's not confirmed that Apple Home shows it."
|
|
131603
131643
|
},
|
|
131604
|
-
|
|
131644
|
+
// doorbell
|
|
131645
|
+
328: {
|
|
131646
|
+
apple: "no",
|
|
131647
|
+
google: "yes",
|
|
131648
|
+
alexa: "no",
|
|
131649
|
+
aqara: "no",
|
|
131650
|
+
smartthings: "unknown",
|
|
131651
|
+
note: "Google Home lists the Matter 1.4 Doorbell; others fall back to the plain Switch cluster, if they show it at all."
|
|
131652
|
+
},
|
|
131653
|
+
// generic switch
|
|
131654
|
+
15: {
|
|
131655
|
+
apple: "partial",
|
|
131656
|
+
google: "no",
|
|
131657
|
+
alexa: "yes",
|
|
131658
|
+
aqara: "yes",
|
|
131659
|
+
smartthings: "yes"
|
|
131660
|
+
}
|
|
131605
131661
|
};
|
|
131606
131662
|
controllerLabels = {
|
|
131607
131663
|
apple: "Apple Home",
|
|
131608
131664
|
google: "Google Home",
|
|
131609
131665
|
alexa: "Alexa",
|
|
131610
|
-
aqara: "Aqara Home"
|
|
131666
|
+
aqara: "Aqara Home",
|
|
131667
|
+
smartthings: "SmartThings"
|
|
131611
131668
|
};
|
|
131612
131669
|
}
|
|
131613
131670
|
});
|
|
@@ -132849,10 +132906,7 @@ var init_debounce_context = __esm({
|
|
|
132849
132906
|
this.getBuffer(key).push(payload);
|
|
132850
132907
|
bufferedFn();
|
|
132851
132908
|
};
|
|
132852
|
-
|
|
132853
|
-
get isPending() {
|
|
132854
|
-
return bufferedFn.isPending;
|
|
132855
|
-
},
|
|
132909
|
+
const fn = Object.assign(debouncer, {
|
|
132856
132910
|
clear: () => bufferedFn.clear(),
|
|
132857
132911
|
flush: () => bufferedFn.flush(),
|
|
132858
132912
|
trigger: () => bufferedFn.trigger(),
|
|
@@ -132860,6 +132914,13 @@ var init_debounce_context = __esm({
|
|
|
132860
132914
|
this.unregister(key);
|
|
132861
132915
|
}
|
|
132862
132916
|
});
|
|
132917
|
+
Object.defineProperty(fn, "isPending", {
|
|
132918
|
+
get: () => bufferedFn.isPending
|
|
132919
|
+
});
|
|
132920
|
+
return fn;
|
|
132921
|
+
}
|
|
132922
|
+
isPending(key) {
|
|
132923
|
+
return (this.buffers.get(key)?.length ?? 0) > 0;
|
|
132863
132924
|
}
|
|
132864
132925
|
unregisterAll() {
|
|
132865
132926
|
for (const key of [...this.debouncers.keys()]) {
|
|
@@ -132994,16 +133055,18 @@ var init_send_ha_message = __esm({
|
|
|
132994
133055
|
});
|
|
132995
133056
|
|
|
132996
133057
|
// src/services/home-assistant/home-assistant-actions.ts
|
|
132997
|
-
var TARGET_FAILURE_THRESHOLD, defaultConfig, HomeAssistantActions;
|
|
133058
|
+
var TARGET_FAILURE_THRESHOLD, RECENT_CHANGE_MS, defaultConfig, HomeAssistantActions;
|
|
132998
133059
|
var init_home_assistant_actions = __esm({
|
|
132999
133060
|
"src/services/home-assistant/home-assistant-actions.ts"() {
|
|
133000
133061
|
"use strict";
|
|
133062
|
+
init_esm();
|
|
133001
133063
|
init_service();
|
|
133002
133064
|
init_debounce_context();
|
|
133003
133065
|
init_retry();
|
|
133004
133066
|
init_send_ha_message();
|
|
133005
133067
|
init_diagnostic_event_bus();
|
|
133006
133068
|
TARGET_FAILURE_THRESHOLD = 3;
|
|
133069
|
+
RECENT_CHANGE_MS = 2e3;
|
|
133007
133070
|
defaultConfig = {
|
|
133008
133071
|
retryAttempts: 3,
|
|
133009
133072
|
retryBaseDelayMs: 100,
|
|
@@ -133030,6 +133093,9 @@ var init_home_assistant_actions = __esm({
|
|
|
133030
133093
|
get available() {
|
|
133031
133094
|
return this.client.haRunning;
|
|
133032
133095
|
}
|
|
133096
|
+
// #446: the issuing entity of a call that failed after its retries. The
|
|
133097
|
+
// command already returned success, so its endpoint rolls back to HA state.
|
|
133098
|
+
failed = Observable();
|
|
133033
133099
|
log;
|
|
133034
133100
|
debounceContext = new DebounceContext(
|
|
133035
133101
|
this.processAction.bind(this)
|
|
@@ -133059,7 +133125,13 @@ var init_home_assistant_actions = __esm({
|
|
|
133059
133125
|
blockedUntil: count >= TARGET_FAILURE_THRESHOLD ? Date.now() + this.config.circuitBreakerResetMs : 0
|
|
133060
133126
|
});
|
|
133061
133127
|
}
|
|
133062
|
-
|
|
133128
|
+
// bare Ons that may start an on-then-level pair (#491)
|
|
133129
|
+
leadingOn = /* @__PURE__ */ new Set();
|
|
133130
|
+
lastChangeAt = /* @__PURE__ */ new Map();
|
|
133131
|
+
// calls per issuing entity, only the latest one may roll back (#446)
|
|
133132
|
+
callSeq = /* @__PURE__ */ new Map();
|
|
133133
|
+
processAction(key, calls) {
|
|
133134
|
+
this.leadingOn.delete(key);
|
|
133063
133135
|
const skipTarget = calls[0].target === false;
|
|
133064
133136
|
const entity_id = skipTarget ? void 0 : calls[0].target || calls[0].entityId;
|
|
133065
133137
|
const action = calls[0].action;
|
|
@@ -133067,12 +133139,22 @@ var init_home_assistant_actions = __esm({
|
|
|
133067
133139
|
const [domain, actionName] = action.split(".");
|
|
133068
133140
|
const target = entity_id ? { entity_id } : {};
|
|
133069
133141
|
const origin = calls[0].entityId;
|
|
133142
|
+
const seq = calls.reduce(
|
|
133143
|
+
(max, c) => c.entityId === origin ? Math.max(max, c.seq) : max,
|
|
133144
|
+
0
|
|
133145
|
+
);
|
|
133070
133146
|
this.callAction(domain, actionName, data, target, false).then(() => this.recordTargetResult(origin, false)).catch((error) => {
|
|
133071
133147
|
this.recordTargetResult(origin, true);
|
|
133072
133148
|
const errorMsg = this.formatError(error);
|
|
133073
133149
|
this.log.error(
|
|
133074
133150
|
`Failed to call action '${action}' for entity '${entity_id ?? "(no target)"}': ${errorMsg}`
|
|
133075
133151
|
);
|
|
133152
|
+
if (this.callSeq.get(origin) !== seq) return;
|
|
133153
|
+
try {
|
|
133154
|
+
this.failed.emit(origin);
|
|
133155
|
+
} catch (e) {
|
|
133156
|
+
this.log.debug(`Rollback for '${origin}' failed: ${e}`);
|
|
133157
|
+
}
|
|
133076
133158
|
});
|
|
133077
133159
|
diagnosticEventBus.emit(
|
|
133078
133160
|
"command_received",
|
|
@@ -133091,9 +133173,23 @@ var init_home_assistant_actions = __esm({
|
|
|
133091
133173
|
}
|
|
133092
133174
|
call(action, entityId) {
|
|
133093
133175
|
const target = action.target === false ? entityId : action.target ?? entityId;
|
|
133094
|
-
const
|
|
133095
|
-
const
|
|
133096
|
-
|
|
133176
|
+
const base = `${target}-${action.action}`;
|
|
133177
|
+
const hasData = Object.keys(action.data ?? {}).length > 0;
|
|
133178
|
+
const key = `${base}-${hasData ? "adjust" : "command"}`;
|
|
133179
|
+
if (action.action === "light.turn_on") {
|
|
133180
|
+
const commandKey = `${base}-command`;
|
|
133181
|
+
if (hasData) {
|
|
133182
|
+
this.lastChangeAt.set(base, Date.now());
|
|
133183
|
+
if (this.leadingOn.delete(commandKey)) {
|
|
133184
|
+
this.debounceContext.get(commandKey, 100).unregister();
|
|
133185
|
+
}
|
|
133186
|
+
} else if (Date.now() - (this.lastChangeAt.get(base) ?? Number.NEGATIVE_INFINITY) >= RECENT_CHANGE_MS && !this.debounceContext.isPending(commandKey)) {
|
|
133187
|
+
this.leadingOn.add(commandKey);
|
|
133188
|
+
}
|
|
133189
|
+
}
|
|
133190
|
+
const seq = (this.callSeq.get(entityId) ?? 0) + 1;
|
|
133191
|
+
this.callSeq.set(entityId, seq);
|
|
133192
|
+
this.debounceContext.get(key, 100)({ ...action, entityId, seq });
|
|
133097
133193
|
}
|
|
133098
133194
|
async callAction(domain, action, data, target, returnResponse) {
|
|
133099
133195
|
const actionKey = `${domain}.${action}`;
|
|
@@ -133228,6 +133324,88 @@ var init_transaction_is_offline = __esm({
|
|
|
133228
133324
|
}
|
|
133229
133325
|
});
|
|
133230
133326
|
|
|
133327
|
+
// src/matter/endpoints/update-entity-state.ts
|
|
133328
|
+
function isDetachedEndpointError(error) {
|
|
133329
|
+
if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
|
|
133330
|
+
return true;
|
|
133331
|
+
}
|
|
133332
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133333
|
+
return message.includes("Endpoint storage inaccessible") || message.includes("is not present on this endpoint");
|
|
133334
|
+
}
|
|
133335
|
+
function updateEntityState(endpoint, state) {
|
|
133336
|
+
const next = (chains.get(endpoint) ?? Promise.resolve()).then(
|
|
133337
|
+
() => writeState(endpoint, state)
|
|
133338
|
+
);
|
|
133339
|
+
chains.set(
|
|
133340
|
+
endpoint,
|
|
133341
|
+
next.catch(() => {
|
|
133342
|
+
})
|
|
133343
|
+
);
|
|
133344
|
+
return next;
|
|
133345
|
+
}
|
|
133346
|
+
function updateEntityRegistry(endpoint, registry3, deviceRegistry) {
|
|
133347
|
+
const next = (chains.get(endpoint) ?? Promise.resolve()).then(
|
|
133348
|
+
() => writeRegistry(endpoint, registry3, deviceRegistry)
|
|
133349
|
+
);
|
|
133350
|
+
chains.set(
|
|
133351
|
+
endpoint,
|
|
133352
|
+
next.catch(() => {
|
|
133353
|
+
})
|
|
133354
|
+
);
|
|
133355
|
+
return next;
|
|
133356
|
+
}
|
|
133357
|
+
async function writeRegistry(endpoint, registry3, deviceRegistry) {
|
|
133358
|
+
try {
|
|
133359
|
+
await endpoint.construction.ready;
|
|
133360
|
+
} catch {
|
|
133361
|
+
return;
|
|
133362
|
+
}
|
|
133363
|
+
try {
|
|
133364
|
+
const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
|
|
133365
|
+
if (JSON.stringify(current.registry) === JSON.stringify(registry3) && JSON.stringify(current.deviceRegistry) === JSON.stringify(deviceRegistry)) {
|
|
133366
|
+
return;
|
|
133367
|
+
}
|
|
133368
|
+
await endpoint.setStateOf(HomeAssistantEntityBehavior, {
|
|
133369
|
+
entity: { ...current, registry: registry3, deviceRegistry }
|
|
133370
|
+
});
|
|
133371
|
+
} catch (error) {
|
|
133372
|
+
if (isDetachedEndpointError(error)) return;
|
|
133373
|
+
throw error;
|
|
133374
|
+
}
|
|
133375
|
+
}
|
|
133376
|
+
async function writeState(endpoint, state) {
|
|
133377
|
+
try {
|
|
133378
|
+
await endpoint.construction.ready;
|
|
133379
|
+
} catch {
|
|
133380
|
+
return;
|
|
133381
|
+
}
|
|
133382
|
+
try {
|
|
133383
|
+
const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
|
|
133384
|
+
await endpoint.setStateOf(HomeAssistantEntityBehavior, {
|
|
133385
|
+
entity: {
|
|
133386
|
+
...current,
|
|
133387
|
+
// a re-push needs a new last_updated, setStateOf drops equal values
|
|
133388
|
+
state: state ?? {
|
|
133389
|
+
...current.state,
|
|
133390
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
133391
|
+
}
|
|
133392
|
+
}
|
|
133393
|
+
});
|
|
133394
|
+
} catch (error) {
|
|
133395
|
+
if (isDetachedEndpointError(error)) return;
|
|
133396
|
+
throw error;
|
|
133397
|
+
}
|
|
133398
|
+
}
|
|
133399
|
+
var chains;
|
|
133400
|
+
var init_update_entity_state = __esm({
|
|
133401
|
+
"src/matter/endpoints/update-entity-state.ts"() {
|
|
133402
|
+
"use strict";
|
|
133403
|
+
init_esm();
|
|
133404
|
+
init_home_assistant_entity_behavior();
|
|
133405
|
+
chains = /* @__PURE__ */ new WeakMap();
|
|
133406
|
+
}
|
|
133407
|
+
});
|
|
133408
|
+
|
|
133231
133409
|
// src/matter/behaviors/home-assistant-entity-behavior.ts
|
|
133232
133410
|
var home_assistant_entity_behavior_exports = {};
|
|
133233
133411
|
__export(home_assistant_entity_behavior_exports, {
|
|
@@ -133238,11 +133416,13 @@ var init_home_assistant_entity_behavior = __esm({
|
|
|
133238
133416
|
"src/matter/behaviors/home-assistant-entity-behavior.ts"() {
|
|
133239
133417
|
"use strict";
|
|
133240
133418
|
init_dist();
|
|
133419
|
+
init_esm();
|
|
133241
133420
|
init_esm7();
|
|
133242
133421
|
init_types2();
|
|
133243
133422
|
init_home_assistant_actions();
|
|
133244
133423
|
init_async_observable();
|
|
133245
133424
|
init_transaction_is_offline();
|
|
133425
|
+
init_update_entity_state();
|
|
133246
133426
|
HomeAssistantEntityBehavior = class extends Behavior {
|
|
133247
133427
|
static id = ClusterId2.homeAssistantEntity;
|
|
133248
133428
|
get entityId() {
|
|
@@ -133259,6 +133439,21 @@ var init_home_assistant_entity_behavior = __esm({
|
|
|
133259
133439
|
get isAvailable() {
|
|
133260
133440
|
return this.entity.state.state !== "unavailable" && this.entity.state.state !== "unknown";
|
|
133261
133441
|
}
|
|
133442
|
+
initialize() {
|
|
133443
|
+
const failed = this.env.maybeGet(HomeAssistantActions)?.failed;
|
|
133444
|
+
if (failed) {
|
|
133445
|
+
this.reactTo(failed, this.rollback);
|
|
133446
|
+
}
|
|
133447
|
+
}
|
|
133448
|
+
// #446: the command already returned success, only assertAvailable can
|
|
133449
|
+
// still fail it. Drop the optimistic holds and re-apply the HA state so the
|
|
133450
|
+
// subscription shows the truth.
|
|
133451
|
+
rollback(entityId) {
|
|
133452
|
+
if (entityId !== this.entityId) return;
|
|
133453
|
+
this.events.actionFailed.emit();
|
|
133454
|
+
updateEntityState(this.endpoint).catch(() => {
|
|
133455
|
+
});
|
|
133456
|
+
}
|
|
133262
133457
|
// #446: fail the command instead of telling the controller it worked while
|
|
133263
133458
|
// the call never reaches HA. `=== false` on purpose: test stubs do not
|
|
133264
133459
|
// define `available`, and undefined must read as "no opinion".
|
|
@@ -133304,6 +133499,8 @@ var init_home_assistant_entity_behavior = __esm({
|
|
|
133304
133499
|
HomeAssistantEntityBehavior2.State = State;
|
|
133305
133500
|
class Events2 extends EventEmitter {
|
|
133306
133501
|
entity$Changed = AsyncObservable2();
|
|
133502
|
+
// an HA call this entity issued failed, see rollback()
|
|
133503
|
+
actionFailed = Observable();
|
|
133307
133504
|
}
|
|
133308
133505
|
HomeAssistantEntityBehavior2.Events = Events2;
|
|
133309
133506
|
})(HomeAssistantEntityBehavior || (HomeAssistantEntityBehavior = {}));
|
|
@@ -133974,8 +134171,25 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
|
|
|
133974
134171
|
});
|
|
133975
134172
|
router.put("/settings", async (req, res) => {
|
|
133976
134173
|
try {
|
|
133977
|
-
const
|
|
133978
|
-
|
|
134174
|
+
const { autoBackup, backupRetentionCount } = req.body ?? {};
|
|
134175
|
+
const next = {};
|
|
134176
|
+
if (autoBackup !== void 0) {
|
|
134177
|
+
if (typeof autoBackup !== "boolean") {
|
|
134178
|
+
res.status(400).json({ error: "autoBackup must be a boolean" });
|
|
134179
|
+
return;
|
|
134180
|
+
}
|
|
134181
|
+
next.autoBackup = autoBackup;
|
|
134182
|
+
}
|
|
134183
|
+
if (backupRetentionCount !== void 0) {
|
|
134184
|
+
if (typeof backupRetentionCount !== "number" || !Number.isInteger(backupRetentionCount) || backupRetentionCount < 1 || backupRetentionCount > 100) {
|
|
134185
|
+
res.status(400).json({
|
|
134186
|
+
error: "backupRetentionCount must be an integer between 1 and 100"
|
|
134187
|
+
});
|
|
134188
|
+
return;
|
|
134189
|
+
}
|
|
134190
|
+
next.backupRetentionCount = backupRetentionCount;
|
|
134191
|
+
}
|
|
134192
|
+
await settingsStorage.setBackupSettings(next);
|
|
133979
134193
|
res.json(settingsStorage.backupSettings);
|
|
133980
134194
|
} catch (error) {
|
|
133981
134195
|
const message = error instanceof Error ? error.message : "Failed to update settings";
|
|
@@ -134570,6 +134784,7 @@ function logsApi(_logger) {
|
|
|
134570
134784
|
}
|
|
134571
134785
|
|
|
134572
134786
|
// src/api/network-diagnostic-api.ts
|
|
134787
|
+
import * as net from "node:net";
|
|
134573
134788
|
import * as os2 from "node:os";
|
|
134574
134789
|
import express6 from "express";
|
|
134575
134790
|
|
|
@@ -134633,8 +134848,8 @@ function selectMdnsInterface(raw) {
|
|
|
134633
134848
|
// src/api/network-diagnostic-api.ts
|
|
134634
134849
|
function networkDiagnosticApi(mdnsInterface, mdnsIpv4) {
|
|
134635
134850
|
const router = express6.Router();
|
|
134636
|
-
router.get("/", (_, res) => {
|
|
134637
|
-
const result = runDiagnostics(mdnsInterface, mdnsIpv4);
|
|
134851
|
+
router.get("/", async (_, res) => {
|
|
134852
|
+
const result = await runDiagnostics(mdnsInterface, mdnsIpv4);
|
|
134638
134853
|
res.json(result);
|
|
134639
134854
|
});
|
|
134640
134855
|
return router;
|
|
@@ -134662,7 +134877,20 @@ function getNetworkInterfaces() {
|
|
|
134662
134877
|
}
|
|
134663
134878
|
return result;
|
|
134664
134879
|
}
|
|
134665
|
-
function
|
|
134880
|
+
async function isPort80Taken(host) {
|
|
134881
|
+
return new Promise((resolve12) => {
|
|
134882
|
+
const socket = net.connect({ host, port: 80 });
|
|
134883
|
+
const done = (taken) => {
|
|
134884
|
+
socket.destroy();
|
|
134885
|
+
resolve12(taken);
|
|
134886
|
+
};
|
|
134887
|
+
socket.setTimeout(500);
|
|
134888
|
+
socket.once("connect", () => done(true));
|
|
134889
|
+
socket.once("timeout", () => done(false));
|
|
134890
|
+
socket.once("error", () => done(false));
|
|
134891
|
+
});
|
|
134892
|
+
}
|
|
134893
|
+
async function runDiagnostics(mdnsInterface, mdnsIpv4) {
|
|
134666
134894
|
const interfaces = getNetworkInterfaces();
|
|
134667
134895
|
const checks = [];
|
|
134668
134896
|
const external = interfaces.filter((i) => !i.internal);
|
|
@@ -134777,6 +135005,20 @@ function runDiagnostics(mdnsInterface, mdnsIpv4) {
|
|
|
134777
135005
|
});
|
|
134778
135006
|
}
|
|
134779
135007
|
}
|
|
135008
|
+
const probeHosts = [
|
|
135009
|
+
"127.0.0.1",
|
|
135010
|
+
...(mdnsInterface ? interfaces.find((i) => i.name === mdnsInterface)?.ipv4 ?? [] : external.find((i) => i.ipv4.length > 0)?.ipv4 ?? []).slice(0, 1)
|
|
135011
|
+
].filter((h, i, all) => all.indexOf(h) === i);
|
|
135012
|
+
const taken = await Promise.all(probeHosts.map(isPort80Taken));
|
|
135013
|
+
const busy = probeHosts.filter((_, i) => taken[i]);
|
|
135014
|
+
if (busy.length > 0) {
|
|
135015
|
+
checks.push({
|
|
135016
|
+
name: "port_80_in_use",
|
|
135017
|
+
status: "warn",
|
|
135018
|
+
message: "Port 80 is in use on this host",
|
|
135019
|
+
detail: `Answering on ${busy.join(", ")}. That is fine on its own. Two users could only pair Alexa after freeing port 80 (#449, #478), nobody knows why yet. If Alexa won't pair, stop whatever holds it while pairing, then turn it back on.`
|
|
135020
|
+
});
|
|
135021
|
+
}
|
|
134780
135022
|
return {
|
|
134781
135023
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
134782
135024
|
interfaces,
|
|
@@ -134815,10 +135057,10 @@ function anonymizeEntityId(entityId) {
|
|
|
134815
135057
|
const hash2 = simpleHash(name).toString(16).substring(0, 6);
|
|
134816
135058
|
return `${domain}.${prefix}***${hash2}`;
|
|
134817
135059
|
}
|
|
134818
|
-
function simpleHash(
|
|
135060
|
+
function simpleHash(str2) {
|
|
134819
135061
|
let hash2 = 0;
|
|
134820
|
-
for (let i = 0; i <
|
|
134821
|
-
const char =
|
|
135062
|
+
for (let i = 0; i < str2.length; i++) {
|
|
135063
|
+
const char = str2.charCodeAt(i);
|
|
134822
135064
|
hash2 = (hash2 << 5) - hash2 + char;
|
|
134823
135065
|
hash2 |= 0;
|
|
134824
135066
|
}
|
|
@@ -134875,7 +135117,7 @@ function anonymizeNetwork(result) {
|
|
|
134875
135117
|
}
|
|
134876
135118
|
function diagnosticApi(bridgeService, haClient, haRegistry, version2, startTime, logger257, mdnsInterface, mdnsIpv4) {
|
|
134877
135119
|
const router = express7.Router();
|
|
134878
|
-
router.get("/export", (req, res) => {
|
|
135120
|
+
router.get("/export", async (req, res) => {
|
|
134879
135121
|
const anonymize = req.query.anonymize !== "false";
|
|
134880
135122
|
const logLimit = Math.min(
|
|
134881
135123
|
500,
|
|
@@ -134884,7 +135126,7 @@ function diagnosticApi(bridgeService, haClient, haRegistry, version2, startTime,
|
|
|
134884
135126
|
const memUsage = process.memoryUsage();
|
|
134885
135127
|
const bridges = bridgeService.bridges;
|
|
134886
135128
|
const haConnected = haClient.connection?.connected ?? false;
|
|
134887
|
-
const network = runDiagnostics(mdnsInterface, mdnsIpv4);
|
|
135129
|
+
const network = await runDiagnostics(mdnsInterface, mdnsIpv4);
|
|
134888
135130
|
const bridgeDetails = bridges.map((b) => {
|
|
134889
135131
|
const data = b.data;
|
|
134890
135132
|
const fabrics = data.commissioning?.fabrics ?? [];
|
|
@@ -135961,6 +136203,7 @@ function getMappedEntityIds(mapping) {
|
|
|
135961
136203
|
if (mapping.currentLimitEntity) ids.push(mapping.currentLimitEntity);
|
|
135962
136204
|
if (mapping.currentRoomEntity) ids.push(mapping.currentRoomEntity);
|
|
135963
136205
|
if (mapping.cleanedAreaEntity) ids.push(mapping.cleanedAreaEntity);
|
|
136206
|
+
if (mapping.operationalStateEntity) ids.push(mapping.operationalStateEntity);
|
|
135964
136207
|
if (mapping.composedEntities) {
|
|
135965
136208
|
for (const sub of mapping.composedEntities) {
|
|
135966
136209
|
if (sub.entityId) ids.push(sub.entityId);
|
|
@@ -136888,7 +137131,11 @@ import * as nodePath from "node:path";
|
|
|
136888
137131
|
import express14 from "express";
|
|
136889
137132
|
|
|
136890
137133
|
// src/plugins/builtin/names.ts
|
|
136891
|
-
var BUILTIN_PLUGIN_NAMES = [
|
|
137134
|
+
var BUILTIN_PLUGIN_NAMES = [
|
|
137135
|
+
"camera",
|
|
137136
|
+
"security",
|
|
137137
|
+
"vesync"
|
|
137138
|
+
];
|
|
136892
137139
|
|
|
136893
137140
|
// src/plugins/plugin-installer.ts
|
|
136894
137141
|
init_esm();
|
|
@@ -138926,6 +139173,12 @@ var BackupService = class {
|
|
|
138926
139173
|
this.props = props;
|
|
138927
139174
|
this.backupDir = path11.join(props.storageLocation, "backups");
|
|
138928
139175
|
fs9.mkdirSync(this.backupDir, { recursive: true });
|
|
139176
|
+
for (const file of fs9.readdirSync(this.backupDir)) {
|
|
139177
|
+
if (file.endsWith(".part")) {
|
|
139178
|
+
fs9.unlinkSync(path11.join(this.backupDir, file));
|
|
139179
|
+
}
|
|
139180
|
+
}
|
|
139181
|
+
this.enforceRetention();
|
|
138929
139182
|
}
|
|
138930
139183
|
bridgeStorage;
|
|
138931
139184
|
mappingStorage;
|
|
@@ -138940,6 +139193,7 @@ var BackupService = class {
|
|
|
138940
139193
|
const prefix = auto ? "auto" : "manual";
|
|
138941
139194
|
const filename = `hamh-${prefix}-${version2}-${dateStr}.zip`;
|
|
138942
139195
|
const filepath = path11.join(this.backupDir, filename);
|
|
139196
|
+
const tmpPath = `${filepath}.part`;
|
|
138943
139197
|
const bridges = this.bridgeStorage.bridges;
|
|
138944
139198
|
const entityMappings = {};
|
|
138945
139199
|
for (const bridge of bridges) {
|
|
@@ -138968,9 +139222,10 @@ var BackupService = class {
|
|
|
138968
139222
|
auto
|
|
138969
139223
|
};
|
|
138970
139224
|
await new Promise((resolve12, reject) => {
|
|
138971
|
-
const output = fs9.createWriteStream(
|
|
139225
|
+
const output = fs9.createWriteStream(tmpPath);
|
|
138972
139226
|
const archive = archiver2("zip", { zlib: { level: 9 } });
|
|
138973
139227
|
output.on("close", () => resolve12());
|
|
139228
|
+
output.on("error", (err) => reject(err));
|
|
138974
139229
|
archive.on("error", (err) => reject(err));
|
|
138975
139230
|
archive.pipe(output);
|
|
138976
139231
|
archive.append(JSON.stringify(backupData, null, 2), {
|
|
@@ -139019,7 +139274,11 @@ var BackupService = class {
|
|
|
139019
139274
|
}
|
|
139020
139275
|
}
|
|
139021
139276
|
archive.finalize();
|
|
139277
|
+
}).catch((e) => {
|
|
139278
|
+
fs9.rmSync(tmpPath, { force: true });
|
|
139279
|
+
throw e;
|
|
139022
139280
|
});
|
|
139281
|
+
fs9.renameSync(tmpPath, filepath);
|
|
139023
139282
|
const stat4 = fs9.statSync(filepath);
|
|
139024
139283
|
const metadata = {
|
|
139025
139284
|
filename,
|
|
@@ -139031,7 +139290,7 @@ var BackupService = class {
|
|
|
139031
139290
|
this.log.info(
|
|
139032
139291
|
`Backup created: ${filename} (${Math.round(stat4.size / 1024)} KB)`
|
|
139033
139292
|
);
|
|
139034
|
-
|
|
139293
|
+
this.enforceRetention();
|
|
139035
139294
|
return metadata;
|
|
139036
139295
|
}
|
|
139037
139296
|
async createAutoBackup() {
|
|
@@ -139092,10 +139351,10 @@ var BackupService = class {
|
|
|
139092
139351
|
return false;
|
|
139093
139352
|
}
|
|
139094
139353
|
}
|
|
139095
|
-
|
|
139354
|
+
enforceRetention() {
|
|
139096
139355
|
const settings = this.settingsStorage.backupSettings;
|
|
139097
139356
|
const maxCount = settings.backupRetentionCount;
|
|
139098
|
-
if (maxCount <= 0) return;
|
|
139357
|
+
if (!Number.isInteger(maxCount) || maxCount <= 0) return;
|
|
139099
139358
|
const backups = this.listBackups();
|
|
139100
139359
|
if (backups.length <= maxCount) return;
|
|
139101
139360
|
const toDelete = backups.slice(maxCount);
|
|
@@ -140806,7 +141065,7 @@ var __knownSymbol50 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @_
|
|
|
140806
141065
|
var __typeError50 = (msg) => {
|
|
140807
141066
|
throw TypeError(msg);
|
|
140808
141067
|
};
|
|
140809
|
-
var __defNormalProp5 = (
|
|
141068
|
+
var __defNormalProp5 = (obj2, key, value) => key in obj2 ? __defProp6(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
140810
141069
|
var __name5 = (target, value) => __defProp6(target, "name", { value, configurable: true });
|
|
140811
141070
|
var __decoratorStart5 = (base) => [, , , __create5(base?.[__knownSymbol50("metadata")] ?? null)];
|
|
140812
141071
|
var __decoratorStrings5 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -140841,12 +141100,12 @@ var __decorateElement5 = (array, flags2, name, decorators, target, extra) => {
|
|
|
140841
141100
|
}
|
|
140842
141101
|
return k || __decoratorMetadata5(array, target), desc && __defProp6(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
140843
141102
|
};
|
|
140844
|
-
var __publicField5 = (
|
|
140845
|
-
var __accessCheck5 = (
|
|
140846
|
-
var __privateIn5 = (member,
|
|
140847
|
-
var __privateGet5 = (
|
|
140848
|
-
var __privateSet5 = (
|
|
140849
|
-
var __privateMethod5 = (
|
|
141103
|
+
var __publicField5 = (obj2, key, value) => __defNormalProp5(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
141104
|
+
var __accessCheck5 = (obj2, member, msg) => member.has(obj2) || __typeError50("Cannot " + msg);
|
|
141105
|
+
var __privateIn5 = (member, obj2) => Object(obj2) !== obj2 ? __typeError50('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
141106
|
+
var __privateGet5 = (obj2, member, getter) => (__accessCheck5(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
141107
|
+
var __privateSet5 = (obj2, member, value, setter) => (__accessCheck5(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
141108
|
+
var __privateMethod5 = (obj2, member, method) => (__accessCheck5(obj2, member, "access private method"), method);
|
|
140850
141109
|
var logger185 = Logger.get("ScenesManagementServer");
|
|
140851
141110
|
var UNDEFINED_SCENE_ID = 255;
|
|
140852
141111
|
function constraintErrorWithSceneId(groupId22, sceneId) {
|
|
@@ -142057,17 +142316,17 @@ var OnOffBaseServer = class extends OnOffLogicBase {
|
|
|
142057
142316
|
/** Apply Scene values when requested from ScenesManagement cluster */
|
|
142058
142317
|
#applySceneValues(values4, transitionTime) {
|
|
142059
142318
|
this.#clearDelayedSceneApplyData();
|
|
142060
|
-
const
|
|
142061
|
-
if (typeof
|
|
142319
|
+
const onOff2 = values4.onOff;
|
|
142320
|
+
if (typeof onOff2 !== "boolean" || this.state.onOff === onOff2) {
|
|
142062
142321
|
return;
|
|
142063
142322
|
}
|
|
142064
142323
|
if (transitionTime === 0) {
|
|
142065
|
-
if (
|
|
142324
|
+
if (onOff2) {
|
|
142066
142325
|
return this.on();
|
|
142067
142326
|
}
|
|
142068
142327
|
return this.off();
|
|
142069
142328
|
}
|
|
142070
|
-
this.internal.applyScenePendingOnOff =
|
|
142329
|
+
this.internal.applyScenePendingOnOff = onOff2;
|
|
142071
142330
|
this.internal.applySceneDelayTimer = Time.getTimer(
|
|
142072
142331
|
"delayed scene apply",
|
|
142073
142332
|
Millis(transitionTime),
|
|
@@ -142082,12 +142341,12 @@ var OnOffBaseServer = class extends OnOffLogicBase {
|
|
|
142082
142341
|
this.internal.applyScenePendingOnOff = void 0;
|
|
142083
142342
|
}
|
|
142084
142343
|
#applyDelayedSceneOnOffValue() {
|
|
142085
|
-
const
|
|
142344
|
+
const onOff2 = this.internal.applyScenePendingOnOff;
|
|
142086
142345
|
this.#clearDelayedSceneApplyData();
|
|
142087
|
-
if (
|
|
142346
|
+
if (onOff2 === void 0) {
|
|
142088
142347
|
return;
|
|
142089
142348
|
}
|
|
142090
|
-
if (
|
|
142349
|
+
if (onOff2) {
|
|
142091
142350
|
this.delayedPromises.add(this.on());
|
|
142092
142351
|
} else {
|
|
142093
142352
|
this.delayedPromises.add(this.off());
|
|
@@ -147905,9 +148164,9 @@ var LevelControlBaseServer = class _LevelControlBaseServer extends LevelControlB
|
|
|
147905
148164
|
this.context.transaction.addParticipants({
|
|
147906
148165
|
preCommit: () => {
|
|
147907
148166
|
if (this.currentLevel === this.minLevel) {
|
|
147908
|
-
const
|
|
147909
|
-
if (
|
|
147910
|
-
|
|
148167
|
+
const onOff2 = this.agent.get(OnOffServer);
|
|
148168
|
+
if (onOff2.state.onOff) {
|
|
148169
|
+
onOff2.state.onOff = false;
|
|
147911
148170
|
return true;
|
|
147912
148171
|
}
|
|
147913
148172
|
}
|
|
@@ -147915,9 +148174,9 @@ var LevelControlBaseServer = class _LevelControlBaseServer extends LevelControlB
|
|
|
147915
148174
|
}
|
|
147916
148175
|
});
|
|
147917
148176
|
} else {
|
|
147918
|
-
const
|
|
147919
|
-
if (!
|
|
147920
|
-
|
|
148177
|
+
const onOff2 = this.agent.get(OnOffServer);
|
|
148178
|
+
if (!onOff2.state.onOff) {
|
|
148179
|
+
onOff2.state.onOff = true;
|
|
147921
148180
|
if (this.state.onLevel !== null) {
|
|
147922
148181
|
result = this.handleOnOffChange(true);
|
|
147923
148182
|
this.internal.blockOnOffCouplingOnce = true;
|
|
@@ -147957,12 +148216,12 @@ var LevelControlBaseServer = class _LevelControlBaseServer extends LevelControlB
|
|
|
147957
148216
|
*
|
|
147958
148217
|
* @param onOff The new onOff state
|
|
147959
148218
|
*/
|
|
147960
|
-
handleOnOffChange(
|
|
148219
|
+
handleOnOffChange(onOff2) {
|
|
147961
148220
|
if (this.internal.blockOnOffCouplingOnce) {
|
|
147962
148221
|
this.internal.blockOnOffCouplingOnce = false;
|
|
147963
148222
|
return;
|
|
147964
148223
|
}
|
|
147965
|
-
if (!
|
|
148224
|
+
if (!onOff2 || this.state.onLevel === null) {
|
|
147966
148225
|
return;
|
|
147967
148226
|
}
|
|
147968
148227
|
logger191.debug(`OnOff changed to ON, setting level to onLevel value of ${this.state.onLevel}`);
|
|
@@ -148739,7 +148998,7 @@ var __knownSymbol51 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @_
|
|
|
148739
148998
|
var __typeError51 = (msg) => {
|
|
148740
148999
|
throw TypeError(msg);
|
|
148741
149000
|
};
|
|
148742
|
-
var __defNormalProp6 = (
|
|
149001
|
+
var __defNormalProp6 = (obj2, key, value) => key in obj2 ? __defProp7(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
148743
149002
|
var __name6 = (target, value) => __defProp7(target, "name", { value, configurable: true });
|
|
148744
149003
|
var __decoratorStart6 = (base) => [, , , __create6(base?.[__knownSymbol51("metadata")] ?? null)];
|
|
148745
149004
|
var __decoratorStrings6 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -148774,12 +149033,12 @@ var __decorateElement6 = (array, flags2, name, decorators, target, extra) => {
|
|
|
148774
149033
|
}
|
|
148775
149034
|
return k || __decoratorMetadata6(array, target), desc && __defProp7(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
148776
149035
|
};
|
|
148777
|
-
var __publicField6 = (
|
|
148778
|
-
var __accessCheck6 = (
|
|
148779
|
-
var __privateIn6 = (member,
|
|
148780
|
-
var __privateGet6 = (
|
|
148781
|
-
var __privateSet6 = (
|
|
148782
|
-
var __privateMethod6 = (
|
|
149036
|
+
var __publicField6 = (obj2, key, value) => __defNormalProp6(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
149037
|
+
var __accessCheck6 = (obj2, member, msg) => member.has(obj2) || __typeError51("Cannot " + msg);
|
|
149038
|
+
var __privateIn6 = (member, obj2) => Object(obj2) !== obj2 ? __typeError51('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
149039
|
+
var __privateGet6 = (obj2, member, getter) => (__accessCheck6(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
149040
|
+
var __privateSet6 = (obj2, member, value, setter) => (__accessCheck6(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
149041
|
+
var __privateMethod6 = (obj2, member, method) => (__accessCheck6(obj2, member, "access private method"), method);
|
|
148783
149042
|
var CredentialType = DoorLock3.CredentialType;
|
|
148784
149043
|
var LockDataType = DoorLock3.LockDataType;
|
|
148785
149044
|
var LockAuth;
|
|
@@ -148993,7 +149252,7 @@ var __knownSymbol52 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @_
|
|
|
148993
149252
|
var __typeError52 = (msg) => {
|
|
148994
149253
|
throw TypeError(msg);
|
|
148995
149254
|
};
|
|
148996
|
-
var __defNormalProp7 = (
|
|
149255
|
+
var __defNormalProp7 = (obj2, key, value) => key in obj2 ? __defProp8(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
148997
149256
|
var __name7 = (target, value) => __defProp8(target, "name", { value, configurable: true });
|
|
148998
149257
|
var __decoratorStart7 = (base) => [, , , __create7(base?.[__knownSymbol52("metadata")] ?? null)];
|
|
148999
149258
|
var __decoratorStrings7 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -149028,12 +149287,12 @@ var __decorateElement7 = (array, flags2, name, decorators, target, extra) => {
|
|
|
149028
149287
|
}
|
|
149029
149288
|
return k || __decoratorMetadata7(array, target), desc && __defProp8(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
149030
149289
|
};
|
|
149031
|
-
var __publicField7 = (
|
|
149032
|
-
var __accessCheck7 = (
|
|
149033
|
-
var __privateIn7 = (member,
|
|
149034
|
-
var __privateGet7 = (
|
|
149035
|
-
var __privateSet7 = (
|
|
149036
|
-
var __privateMethod7 = (
|
|
149290
|
+
var __publicField7 = (obj2, key, value) => __defNormalProp7(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
149291
|
+
var __accessCheck7 = (obj2, member, msg) => member.has(obj2) || __typeError52("Cannot " + msg);
|
|
149292
|
+
var __privateIn7 = (member, obj2) => Object(obj2) !== obj2 ? __typeError52('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
149293
|
+
var __privateGet7 = (obj2, member, getter) => (__accessCheck7(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
149294
|
+
var __privateSet7 = (obj2, member, value, setter) => (__accessCheck7(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
149295
|
+
var __privateMethod7 = (obj2, member, method) => (__accessCheck7(obj2, member, "access private method"), method);
|
|
149037
149296
|
var LockSchedule;
|
|
149038
149297
|
((LockSchedule2) => {
|
|
149039
149298
|
var _endMinute_dec, _endHour_dec, _startMinute_dec, _startHour_dec, _daysMask_dec, _userIndex_dec, _weekDayIndex_dec, _init, _localEndTime_dec, _localStartTime_dec, _userIndex_dec2, _yearDayIndex_dec, _init2, _operatingMode_dec, _localEndTime_dec2, _localStartTime_dec2, _holidayIndex_dec, _init3;
|
|
@@ -149101,7 +149360,7 @@ var __knownSymbol53 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @_
|
|
|
149101
149360
|
var __typeError53 = (msg) => {
|
|
149102
149361
|
throw TypeError(msg);
|
|
149103
149362
|
};
|
|
149104
|
-
var __defNormalProp8 = (
|
|
149363
|
+
var __defNormalProp8 = (obj2, key, value) => key in obj2 ? __defProp9(obj2, key, { enumerable: true, configurable: true, writable: true, value }) : obj2[key] = value;
|
|
149105
149364
|
var __name8 = (target, value) => __defProp9(target, "name", { value, configurable: true });
|
|
149106
149365
|
var __decoratorStart8 = (base) => [, , , __create8(base?.[__knownSymbol53("metadata")] ?? null)];
|
|
149107
149366
|
var __decoratorStrings8 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
@@ -149136,12 +149395,12 @@ var __decorateElement8 = (array, flags2, name, decorators, target, extra) => {
|
|
|
149136
149395
|
}
|
|
149137
149396
|
return k || __decoratorMetadata8(array, target), desc && __defProp9(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
149138
149397
|
};
|
|
149139
|
-
var __publicField8 = (
|
|
149140
|
-
var __accessCheck8 = (
|
|
149141
|
-
var __privateIn8 = (member,
|
|
149142
|
-
var __privateGet8 = (
|
|
149143
|
-
var __privateSet8 = (
|
|
149144
|
-
var __privateMethod8 = (
|
|
149398
|
+
var __publicField8 = (obj2, key, value) => __defNormalProp8(obj2, typeof key !== "symbol" ? key + "" : key, value);
|
|
149399
|
+
var __accessCheck8 = (obj2, member, msg) => member.has(obj2) || __typeError53("Cannot " + msg);
|
|
149400
|
+
var __privateIn8 = (member, obj2) => Object(obj2) !== obj2 ? __typeError53('Cannot use the "in" operator on this value') : member.has(obj2);
|
|
149401
|
+
var __privateGet8 = (obj2, member, getter) => (__accessCheck8(obj2, member, "read from private field"), getter ? getter.call(obj2) : member.get(obj2));
|
|
149402
|
+
var __privateSet8 = (obj2, member, value, setter) => (__accessCheck8(obj2, member, "write to private field"), setter ? setter.call(obj2, value) : member.set(obj2, value), value);
|
|
149403
|
+
var __privateMethod8 = (obj2, member, method) => (__accessCheck8(obj2, member, "access private method"), method);
|
|
149145
149404
|
var AlarmCode = DoorLock3.AlarmCode;
|
|
149146
149405
|
var CredentialRule = DoorLock3.CredentialRule;
|
|
149147
149406
|
var CredentialType2 = DoorLock3.CredentialType;
|
|
@@ -150823,6 +151082,7 @@ var LaundryDryerDeviceDefinition = MutableEndpoint({
|
|
|
150823
151082
|
behaviors: SupportedBehaviors(LaundryDryerRequirements.server.mandatory.OperationalState)
|
|
150824
151083
|
});
|
|
150825
151084
|
Object.freeze(LaundryDryerDeviceDefinition);
|
|
151085
|
+
var LaundryDryerDevice = LaundryDryerDeviceDefinition;
|
|
150826
151086
|
|
|
150827
151087
|
// ../../node_modules/.pnpm/@matter+node@0.17.9_patch_hash=36b92ac82ac22a042f8521224156076e8f31f149bd4a87aa14d87af50ce802af/node_modules/@matter/node/dist/esm/devices/laundry-washer.js
|
|
150828
151088
|
init_IdentifyServer();
|
|
@@ -150871,6 +151131,7 @@ var LaundryWasherDeviceDefinition = MutableEndpoint({
|
|
|
150871
151131
|
behaviors: SupportedBehaviors(LaundryWasherRequirements.server.mandatory.OperationalState)
|
|
150872
151132
|
});
|
|
150873
151133
|
Object.freeze(LaundryWasherDeviceDefinition);
|
|
151134
|
+
var LaundryWasherDevice = LaundryWasherDeviceDefinition;
|
|
150874
151135
|
|
|
150875
151136
|
// ../../node_modules/.pnpm/@matter+node@0.17.9_patch_hash=36b92ac82ac22a042f8521224156076e8f31f149bd4a87aa14d87af50ce802af/node_modules/@matter/node/dist/esm/devices/light-sensor.js
|
|
150876
151137
|
init_IdentifyServer();
|
|
@@ -156922,11 +157183,11 @@ var ServerModeServerNode = class extends ServerNode {
|
|
|
156922
157183
|
await this.erase();
|
|
156923
157184
|
}
|
|
156924
157185
|
};
|
|
156925
|
-
function dropUndefined(
|
|
157186
|
+
function dropUndefined(obj2) {
|
|
156926
157187
|
const result = {};
|
|
156927
|
-
for (const key in
|
|
156928
|
-
if (
|
|
156929
|
-
result[key] =
|
|
157188
|
+
for (const key in obj2) {
|
|
157189
|
+
if (obj2[key] !== void 0) {
|
|
157190
|
+
result[key] = obj2[key];
|
|
156930
157191
|
}
|
|
156931
157192
|
}
|
|
156932
157193
|
return result;
|
|
@@ -157362,6 +157623,11 @@ var deviceTypeMap = {
|
|
|
157362
157623
|
PluginBasicInformationServer,
|
|
157363
157624
|
PluginDeviceBehavior
|
|
157364
157625
|
),
|
|
157626
|
+
air_purifier: () => AirPurifierDevice.with(
|
|
157627
|
+
IdentifyServer2,
|
|
157628
|
+
PluginBasicInformationServer,
|
|
157629
|
+
PluginDeviceBehavior
|
|
157630
|
+
),
|
|
157365
157631
|
window_covering: () => WindowCoveringDevice.with(
|
|
157366
157632
|
IdentifyServer2,
|
|
157367
157633
|
PluginBasicInformationServer,
|
|
@@ -157701,6 +157967,9 @@ var PluginManager = class {
|
|
|
157701
157967
|
if (enabled === false) {
|
|
157702
157968
|
metadata.enabled = false;
|
|
157703
157969
|
logger205.info(`Plugin "${plugin.name}" stays disabled (persisted)`);
|
|
157970
|
+
if (metadata.source === "builtin") {
|
|
157971
|
+
metadata.config = await storage2.get(PLUGIN_CONFIG_KEY) ?? metadata.config;
|
|
157972
|
+
}
|
|
157704
157973
|
}
|
|
157705
157974
|
const devices = /* @__PURE__ */ new Map();
|
|
157706
157975
|
const pluginLogger = Logger.get(`Plugin:${plugin.name}`);
|
|
@@ -158391,74 +158660,8 @@ var BridgeServerNode = class extends ServerNode {
|
|
|
158391
158660
|
}
|
|
158392
158661
|
};
|
|
158393
158662
|
|
|
158394
|
-
// src/
|
|
158395
|
-
|
|
158396
|
-
init_home_assistant_entity_behavior();
|
|
158397
|
-
var chains = /* @__PURE__ */ new WeakMap();
|
|
158398
|
-
function isDetachedEndpointError(error) {
|
|
158399
|
-
if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
|
|
158400
|
-
return true;
|
|
158401
|
-
}
|
|
158402
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
158403
|
-
return message.includes("Endpoint storage inaccessible") || message.includes("is not present on this endpoint");
|
|
158404
|
-
}
|
|
158405
|
-
function updateEntityState(endpoint, state) {
|
|
158406
|
-
const next = (chains.get(endpoint) ?? Promise.resolve()).then(
|
|
158407
|
-
() => writeState(endpoint, state)
|
|
158408
|
-
);
|
|
158409
|
-
chains.set(
|
|
158410
|
-
endpoint,
|
|
158411
|
-
next.catch(() => {
|
|
158412
|
-
})
|
|
158413
|
-
);
|
|
158414
|
-
return next;
|
|
158415
|
-
}
|
|
158416
|
-
function updateEntityRegistry(endpoint, registry3, deviceRegistry) {
|
|
158417
|
-
const next = (chains.get(endpoint) ?? Promise.resolve()).then(
|
|
158418
|
-
() => writeRegistry(endpoint, registry3, deviceRegistry)
|
|
158419
|
-
);
|
|
158420
|
-
chains.set(
|
|
158421
|
-
endpoint,
|
|
158422
|
-
next.catch(() => {
|
|
158423
|
-
})
|
|
158424
|
-
);
|
|
158425
|
-
return next;
|
|
158426
|
-
}
|
|
158427
|
-
async function writeRegistry(endpoint, registry3, deviceRegistry) {
|
|
158428
|
-
try {
|
|
158429
|
-
await endpoint.construction.ready;
|
|
158430
|
-
} catch {
|
|
158431
|
-
return;
|
|
158432
|
-
}
|
|
158433
|
-
try {
|
|
158434
|
-
const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
|
|
158435
|
-
if (JSON.stringify(current.registry) === JSON.stringify(registry3) && JSON.stringify(current.deviceRegistry) === JSON.stringify(deviceRegistry)) {
|
|
158436
|
-
return;
|
|
158437
|
-
}
|
|
158438
|
-
await endpoint.setStateOf(HomeAssistantEntityBehavior, {
|
|
158439
|
-
entity: { ...current, registry: registry3, deviceRegistry }
|
|
158440
|
-
});
|
|
158441
|
-
} catch (error) {
|
|
158442
|
-
if (isDetachedEndpointError(error)) return;
|
|
158443
|
-
throw error;
|
|
158444
|
-
}
|
|
158445
|
-
}
|
|
158446
|
-
async function writeState(endpoint, state) {
|
|
158447
|
-
try {
|
|
158448
|
-
await endpoint.construction.ready;
|
|
158449
|
-
} catch {
|
|
158450
|
-
return;
|
|
158451
|
-
}
|
|
158452
|
-
try {
|
|
158453
|
-
const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
|
|
158454
|
-
await endpoint.setStateOf(HomeAssistantEntityBehavior, {
|
|
158455
|
-
entity: { ...current, state }
|
|
158456
|
-
});
|
|
158457
|
-
} catch (error) {
|
|
158458
|
-
if (isDetachedEndpointError(error)) return;
|
|
158459
|
-
throw error;
|
|
158460
|
-
}
|
|
158461
|
-
}
|
|
158663
|
+
// src/services/bridges/bridge.ts
|
|
158664
|
+
init_update_entity_state();
|
|
158462
158665
|
|
|
158463
158666
|
// src/utils/ensure-commissioning-config.ts
|
|
158464
158667
|
init_protocol3();
|
|
@@ -159174,6 +159377,7 @@ var SessionSupervisor = class {
|
|
|
159174
159377
|
const type = message?.payloadHeader?.messageType;
|
|
159175
159378
|
if (type === MessageType.SubscribeRequest) {
|
|
159176
159379
|
this.pushWedgeRing(this.subscribeTimesMs, session, now);
|
|
159380
|
+
this.armSubscribeWatchdog(session);
|
|
159177
159381
|
} else if (type != null && commandMessageTypes.includes(type)) {
|
|
159178
159382
|
this.lastCommandImAt.set(session, now);
|
|
159179
159383
|
}
|
|
@@ -159230,6 +159434,20 @@ var SessionSupervisor = class {
|
|
|
159230
159434
|
);
|
|
159231
159435
|
});
|
|
159232
159436
|
}
|
|
159437
|
+
// A subscription that dies before its first report never joins the session,
|
|
159438
|
+
// so no event fires to reap the session (#487).
|
|
159439
|
+
armSubscribeWatchdog(session) {
|
|
159440
|
+
const sessionId = session.id;
|
|
159441
|
+
if (typeof sessionId !== "number") return;
|
|
159442
|
+
if (this.staleSessionTimers.has(sessionId)) return;
|
|
159443
|
+
this.staleSessionTimers.set(
|
|
159444
|
+
sessionId,
|
|
159445
|
+
setTimeout(() => {
|
|
159446
|
+
this.staleSessionTimers.delete(sessionId);
|
|
159447
|
+
this.closeStaleSession(sessionId, PRIMING_GRACE_MS);
|
|
159448
|
+
}, replacedSessionTimeoutMs(this.dataProvider.featureFlags))
|
|
159449
|
+
);
|
|
159450
|
+
}
|
|
159233
159451
|
closeStaleSession(sessionId, minQuietMs = 0) {
|
|
159234
159452
|
try {
|
|
159235
159453
|
const sessionManager = this.server.env.get(SessionManager);
|
|
@@ -161222,6 +161440,12 @@ var LevelControlServerBase = class extends FeaturedBase5 {
|
|
|
161222
161440
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
161223
161441
|
this.update(homeAssistant.entity);
|
|
161224
161442
|
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
161443
|
+
this.reactTo(homeAssistant.events.actionFailed, this.dropOptimistic);
|
|
161444
|
+
}
|
|
161445
|
+
dropOptimistic() {
|
|
161446
|
+
optimisticLevelState.delete(
|
|
161447
|
+
this.agent.get(HomeAssistantEntityBehavior).entityId
|
|
161448
|
+
);
|
|
161225
161449
|
}
|
|
161226
161450
|
update(entity) {
|
|
161227
161451
|
if (!entity.state || !entity.state.attributes) {
|
|
@@ -161417,25 +161641,31 @@ var OnOffServerBase = class extends OnOffServer {
|
|
|
161417
161641
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
161418
161642
|
this.update(homeAssistant.entity);
|
|
161419
161643
|
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
161644
|
+
this.reactTo(homeAssistant.events.actionFailed, this.dropOptimistic);
|
|
161645
|
+
}
|
|
161646
|
+
dropOptimistic() {
|
|
161647
|
+
optimisticOnOffState.delete(
|
|
161648
|
+
this.agent.get(HomeAssistantEntityBehavior).entityId
|
|
161649
|
+
);
|
|
161420
161650
|
}
|
|
161421
161651
|
update(entity) {
|
|
161422
161652
|
if (!entity.state || !entity.state.attributes) {
|
|
161423
161653
|
return;
|
|
161424
161654
|
}
|
|
161425
161655
|
const { state } = entity;
|
|
161426
|
-
const
|
|
161656
|
+
const onOff2 = this.isOn(state);
|
|
161427
161657
|
const entityId = this.agent.get(HomeAssistantEntityBehavior).entity.entity_id;
|
|
161428
161658
|
const optimistic = optimisticOnOffState.get(entityId);
|
|
161429
161659
|
if (optimistic != null) {
|
|
161430
161660
|
if (Date.now() - optimistic.timestamp > OPTIMISTIC_TIMEOUT_MS2) {
|
|
161431
161661
|
optimisticOnOffState.delete(entityId);
|
|
161432
|
-
} else if (
|
|
161662
|
+
} else if (onOff2 === optimistic.expectedOnOff) {
|
|
161433
161663
|
optimisticOnOffState.delete(entityId);
|
|
161434
161664
|
} else {
|
|
161435
161665
|
return;
|
|
161436
161666
|
}
|
|
161437
161667
|
}
|
|
161438
|
-
applyPatchState(this.state, { onOff });
|
|
161668
|
+
applyPatchState(this.state, { onOff: onOff2 });
|
|
161439
161669
|
}
|
|
161440
161670
|
isOn(entity) {
|
|
161441
161671
|
return this.state.config?.isOn?.(entity, this.agent) ?? (this.agent.get(HomeAssistantEntityBehavior).isAvailable && entity.state !== "off");
|
|
@@ -161521,6 +161751,13 @@ function setOptimisticOnOff(entityId, expectedOnOff) {
|
|
|
161521
161751
|
|
|
161522
161752
|
// src/matter/behaviors/fan-control-server.ts
|
|
161523
161753
|
var logger214 = Logger.get("FanControlServer");
|
|
161754
|
+
function lockResource(behavior) {
|
|
161755
|
+
let resource = behavior;
|
|
161756
|
+
while (resource[Transaction.Resource.reference]) {
|
|
161757
|
+
resource = resource[Transaction.Resource.reference];
|
|
161758
|
+
}
|
|
161759
|
+
return resource;
|
|
161760
|
+
}
|
|
161524
161761
|
var defaultStepSize = 33.33;
|
|
161525
161762
|
var minSpeedMax = 3;
|
|
161526
161763
|
var maxSpeedMax = 100;
|
|
@@ -161608,7 +161845,10 @@ var FanControlServerBase = class extends FeaturedBase6 {
|
|
|
161608
161845
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
161609
161846
|
this.update(homeAssistant.entity);
|
|
161610
161847
|
this.reactTo(homeAssistant.onChange, this.update, {
|
|
161611
|
-
lock: this.agent.has(FanSpeedMemoryBehavior) ? [
|
|
161848
|
+
lock: this.agent.has(FanSpeedMemoryBehavior) ? [
|
|
161849
|
+
lockResource(this),
|
|
161850
|
+
lockResource(this.agent.get(FanSpeedMemoryBehavior))
|
|
161851
|
+
] : true
|
|
161612
161852
|
});
|
|
161613
161853
|
this.reactTo(
|
|
161614
161854
|
this.events.percentSetting$Changed,
|
|
@@ -162016,12 +162256,12 @@ var FanControlServerBase = class extends FeaturedBase6 {
|
|
|
162016
162256
|
// Cross-cluster: restore fan speed on controller-initiated turn-on.
|
|
162017
162257
|
// When a controller sends OnOff.on() and percentSetting is 0, Apple Home
|
|
162018
162258
|
// may default to 100%. Restoring the last non-zero value avoids this (#225).
|
|
162019
|
-
onOffChanged(
|
|
162259
|
+
onOffChanged(onOff2, _oldValue, context) {
|
|
162020
162260
|
if (transactionIsOffline(context)) {
|
|
162021
162261
|
return;
|
|
162022
162262
|
}
|
|
162023
162263
|
const remembered = this.remembered();
|
|
162024
|
-
if (
|
|
162264
|
+
if (onOff2 && remembered.percent > 0) {
|
|
162025
162265
|
this.agent.asLocalActor(() => {
|
|
162026
162266
|
try {
|
|
162027
162267
|
applyPatchState(this.state, {
|
|
@@ -162198,6 +162438,7 @@ var FanOnOffServer = OnOffServer2({
|
|
|
162198
162438
|
});
|
|
162199
162439
|
|
|
162200
162440
|
// src/matter/endpoints/composed/composed-air-purifier-endpoint.ts
|
|
162441
|
+
init_update_entity_state();
|
|
162201
162442
|
var logger215 = Logger.get("ComposedAirPurifierEndpoint");
|
|
162202
162443
|
var temperatureConfig = {
|
|
162203
162444
|
getValue(entity, agent) {
|
|
@@ -163861,6 +164102,7 @@ function ClimateDevice(homeAssistantEntity, includeBasicInformation = true) {
|
|
|
163861
164102
|
}
|
|
163862
164103
|
|
|
163863
164104
|
// src/matter/endpoints/composed/composed-climate-fan-endpoint.ts
|
|
164105
|
+
init_update_entity_state();
|
|
163864
164106
|
var logger218 = Logger.get("ComposedClimateFanEndpoint");
|
|
163865
164107
|
function createEndpointId3(entityId, customName) {
|
|
163866
164108
|
const baseName = customName || entityId;
|
|
@@ -164080,6 +164322,7 @@ function PressureMeasurementServer2(config8) {
|
|
|
164080
164322
|
}
|
|
164081
164323
|
|
|
164082
164324
|
// src/matter/endpoints/composed/composed-sensor-endpoint.ts
|
|
164325
|
+
init_update_entity_state();
|
|
164083
164326
|
var logger220 = Logger.get("ComposedSensorEndpoint");
|
|
164084
164327
|
var temperatureConfig2 = {
|
|
164085
164328
|
getValue(entity, agent) {
|
|
@@ -164572,6 +164815,179 @@ function AlarmControlPanelDevice(homeAssistantEntity) {
|
|
|
164572
164815
|
});
|
|
164573
164816
|
}
|
|
164574
164817
|
|
|
164818
|
+
// ../../node_modules/.pnpm/@matter+main@0.17.9/node_modules/@matter/main/dist/esm/forwards/behaviors/operational-state.js
|
|
164819
|
+
init_nodejs();
|
|
164820
|
+
|
|
164821
|
+
// ../../node_modules/.pnpm/@matter+main@0.17.9/node_modules/@matter/main/dist/esm/forwards/clusters/operational-state.js
|
|
164822
|
+
init_nodejs();
|
|
164823
|
+
|
|
164824
|
+
// src/matter/endpoints/legacy/appliance/index.ts
|
|
164825
|
+
init_home_assistant_entity_behavior();
|
|
164826
|
+
var Op = OperationalState3.OperationalStateEnum;
|
|
164827
|
+
var haStateToOperationalState = {
|
|
164828
|
+
off: Op.Stopped,
|
|
164829
|
+
idle: Op.Stopped,
|
|
164830
|
+
standby: Op.Stopped,
|
|
164831
|
+
on: Op.Running,
|
|
164832
|
+
running: Op.Running,
|
|
164833
|
+
active: Op.Running,
|
|
164834
|
+
drying: Op.Running,
|
|
164835
|
+
washing: Op.Running,
|
|
164836
|
+
paused: Op.Paused,
|
|
164837
|
+
complete: Op.Stopped,
|
|
164838
|
+
finished: Op.Stopped,
|
|
164839
|
+
inactive: Op.Stopped,
|
|
164840
|
+
ready: Op.Stopped,
|
|
164841
|
+
delayedstart: Op.Stopped,
|
|
164842
|
+
run: Op.Running,
|
|
164843
|
+
pause: Op.Paused,
|
|
164844
|
+
// waiting for the user
|
|
164845
|
+
actionrequired: Op.Paused,
|
|
164846
|
+
aborting: Op.Running,
|
|
164847
|
+
error: Op.Error,
|
|
164848
|
+
stop: Op.Stopped,
|
|
164849
|
+
// Miele
|
|
164850
|
+
in_use: Op.Running,
|
|
164851
|
+
programmed: Op.Stopped,
|
|
164852
|
+
waiting_to_start: Op.Stopped,
|
|
164853
|
+
program_ended: Op.Stopped,
|
|
164854
|
+
program_interrupted: Op.Stopped,
|
|
164855
|
+
rinse_hold: Op.Paused,
|
|
164856
|
+
failure: Op.Error,
|
|
164857
|
+
// LG ThinQ
|
|
164858
|
+
detecting: Op.Running,
|
|
164859
|
+
prewash: Op.Running,
|
|
164860
|
+
soaking: Op.Running,
|
|
164861
|
+
add_drain: Op.Running,
|
|
164862
|
+
dispensing: Op.Running,
|
|
164863
|
+
rinsing: Op.Running,
|
|
164864
|
+
softening: Op.Running,
|
|
164865
|
+
steam_softening: Op.Running,
|
|
164866
|
+
spinning: Op.Running,
|
|
164867
|
+
refreshing: Op.Running,
|
|
164868
|
+
cooling: Op.Running,
|
|
164869
|
+
cool_down: Op.Running,
|
|
164870
|
+
night_dry: Op.Running,
|
|
164871
|
+
end: Op.Stopped,
|
|
164872
|
+
running_end: Op.Stopped,
|
|
164873
|
+
done: Op.Stopped,
|
|
164874
|
+
initial: Op.Stopped,
|
|
164875
|
+
power_off: Op.Stopped,
|
|
164876
|
+
reserved: Op.Stopped,
|
|
164877
|
+
power_fail: Op.Error
|
|
164878
|
+
};
|
|
164879
|
+
var cycleStates = /* @__PURE__ */ new Set([Op.Running, Op.Paused]);
|
|
164880
|
+
var ApplianceOperationalStateServer = class extends OperationalStateServer.enable({
|
|
164881
|
+
events: { operationCompletion: true }
|
|
164882
|
+
}) {
|
|
164883
|
+
async initialize() {
|
|
164884
|
+
this.state.operationalStateList = [
|
|
164885
|
+
{ operationalStateId: Op.Stopped },
|
|
164886
|
+
{ operationalStateId: Op.Running },
|
|
164887
|
+
{ operationalStateId: Op.Paused },
|
|
164888
|
+
{ operationalStateId: Op.Error }
|
|
164889
|
+
];
|
|
164890
|
+
this.state.operationalState = Op.Stopped;
|
|
164891
|
+
this.state.operationalError = {
|
|
164892
|
+
errorStateId: OperationalState3.ErrorState.NoError
|
|
164893
|
+
};
|
|
164894
|
+
await super.initialize();
|
|
164895
|
+
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
164896
|
+
this.update(homeAssistant.entity);
|
|
164897
|
+
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
164898
|
+
}
|
|
164899
|
+
update(entity) {
|
|
164900
|
+
if (!entity.state) {
|
|
164901
|
+
return;
|
|
164902
|
+
}
|
|
164903
|
+
const stateEntity = this.agent.get(HomeAssistantEntityBehavior).state.mapping?.operationalStateEntity;
|
|
164904
|
+
const raw = stateEntity ? this.agent.env.get(EntityStateProvider).getState(stateEntity)?.state : entity.state.state;
|
|
164905
|
+
if (raw === "unavailable" || raw === "unknown") {
|
|
164906
|
+
return;
|
|
164907
|
+
}
|
|
164908
|
+
let haState = raw?.toLowerCase() ?? "off";
|
|
164909
|
+
if (stateEntity && haState === "on") {
|
|
164910
|
+
haState = "idle";
|
|
164911
|
+
}
|
|
164912
|
+
const mapped = haStateToOperationalState[haState];
|
|
164913
|
+
if (stateEntity && (raw === void 0 || mapped === void 0)) {
|
|
164914
|
+
return;
|
|
164915
|
+
}
|
|
164916
|
+
const newState = mapped ?? Op.Stopped;
|
|
164917
|
+
const previous = this.state.operationalState;
|
|
164918
|
+
const errorStateId = newState === Op.Error ? OperationalState3.ErrorState.UnableToCompleteOperation : OperationalState3.ErrorState.NoError;
|
|
164919
|
+
applyPatchState(this.state, {
|
|
164920
|
+
operationalState: newState,
|
|
164921
|
+
operationalError: { errorStateId }
|
|
164922
|
+
});
|
|
164923
|
+
if (cycleStates.has(previous) && !cycleStates.has(newState)) {
|
|
164924
|
+
this.events.operationCompletion.emit(
|
|
164925
|
+
{
|
|
164926
|
+
completionErrorCode: errorStateId,
|
|
164927
|
+
totalOperationalTime: null,
|
|
164928
|
+
pausedTime: null
|
|
164929
|
+
},
|
|
164930
|
+
this.context
|
|
164931
|
+
);
|
|
164932
|
+
}
|
|
164933
|
+
}
|
|
164934
|
+
pause() {
|
|
164935
|
+
return {
|
|
164936
|
+
commandResponseState: {
|
|
164937
|
+
errorStateId: OperationalState3.ErrorState.CommandInvalidInState
|
|
164938
|
+
}
|
|
164939
|
+
};
|
|
164940
|
+
}
|
|
164941
|
+
stop() {
|
|
164942
|
+
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
164943
|
+
homeAssistant.callAction({ action: "homeassistant.turn_off" });
|
|
164944
|
+
return {
|
|
164945
|
+
commandResponseState: {
|
|
164946
|
+
errorStateId: OperationalState3.ErrorState.NoError
|
|
164947
|
+
}
|
|
164948
|
+
};
|
|
164949
|
+
}
|
|
164950
|
+
start() {
|
|
164951
|
+
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
164952
|
+
homeAssistant.callAction({ action: "homeassistant.turn_on" });
|
|
164953
|
+
return {
|
|
164954
|
+
commandResponseState: {
|
|
164955
|
+
errorStateId: OperationalState3.ErrorState.NoError
|
|
164956
|
+
}
|
|
164957
|
+
};
|
|
164958
|
+
}
|
|
164959
|
+
resume() {
|
|
164960
|
+
return this.start();
|
|
164961
|
+
}
|
|
164962
|
+
};
|
|
164963
|
+
var ApplianceOnOffServer = OnOffServer2({
|
|
164964
|
+
turnOn: () => ({
|
|
164965
|
+
action: "homeassistant.turn_on"
|
|
164966
|
+
}),
|
|
164967
|
+
turnOff: () => ({
|
|
164968
|
+
action: "homeassistant.turn_off"
|
|
164969
|
+
})
|
|
164970
|
+
}).with("DeadFrontBehavior");
|
|
164971
|
+
var behaviors = [
|
|
164972
|
+
BasicInformationServer2,
|
|
164973
|
+
IdentifyServer2,
|
|
164974
|
+
HomeAssistantEntityBehavior,
|
|
164975
|
+
ApplianceOperationalStateServer,
|
|
164976
|
+
ApplianceOnOffServer
|
|
164977
|
+
];
|
|
164978
|
+
var DishwasherType = DishwasherDevice.with(...behaviors);
|
|
164979
|
+
var LaundryWasherType = LaundryWasherDevice.with(...behaviors);
|
|
164980
|
+
var LaundryDryerType = LaundryDryerDevice.with(...behaviors);
|
|
164981
|
+
function DishwasherEndpoint(homeAssistantEntity) {
|
|
164982
|
+
return DishwasherType.set({ homeAssistantEntity });
|
|
164983
|
+
}
|
|
164984
|
+
function LaundryWasherEndpoint(homeAssistantEntity) {
|
|
164985
|
+
return LaundryWasherType.set({ homeAssistantEntity });
|
|
164986
|
+
}
|
|
164987
|
+
function LaundryDryerEndpoint(homeAssistantEntity) {
|
|
164988
|
+
return LaundryDryerType.set({ homeAssistantEntity });
|
|
164989
|
+
}
|
|
164990
|
+
|
|
164575
164991
|
// src/matter/endpoints/legacy/automation/index.ts
|
|
164576
164992
|
init_home_assistant_entity_behavior();
|
|
164577
164993
|
var AutomationOnOffServer = OnOffServer2({
|
|
@@ -165277,6 +165693,15 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
|
|
|
165277
165693
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
165278
165694
|
this.update(homeAssistant.entity);
|
|
165279
165695
|
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
165696
|
+
this.reactTo(homeAssistant.events.actionFailed, this.dropOptimistic);
|
|
165697
|
+
}
|
|
165698
|
+
// The HA call failed, so the move never started. Without this the re-applied
|
|
165699
|
+
// HA state would keep the optimistic Opening/Closing until the timeout (#446).
|
|
165700
|
+
dropOptimistic() {
|
|
165701
|
+
const optimistic = coverOptimistic.get(this.endpoint);
|
|
165702
|
+
if (!optimistic) return;
|
|
165703
|
+
clearOptimisticAxis(optimistic, "lift");
|
|
165704
|
+
clearOptimisticAxis(optimistic, "tilt");
|
|
165280
165705
|
}
|
|
165281
165706
|
update(entity) {
|
|
165282
165707
|
if (!entity.state || !entity.state.attributes) {
|
|
@@ -166153,105 +166578,6 @@ function CoverDevice(homeAssistantEntity) {
|
|
|
166153
166578
|
});
|
|
166154
166579
|
}
|
|
166155
166580
|
|
|
166156
|
-
// ../../node_modules/.pnpm/@matter+main@0.17.9/node_modules/@matter/main/dist/esm/forwards/behaviors/operational-state.js
|
|
166157
|
-
init_nodejs();
|
|
166158
|
-
|
|
166159
|
-
// ../../node_modules/.pnpm/@matter+main@0.17.9/node_modules/@matter/main/dist/esm/forwards/clusters/operational-state.js
|
|
166160
|
-
init_nodejs();
|
|
166161
|
-
|
|
166162
|
-
// src/matter/endpoints/legacy/dishwasher/index.ts
|
|
166163
|
-
init_home_assistant_entity_behavior();
|
|
166164
|
-
var haStateToDishwasherState = {
|
|
166165
|
-
off: OperationalState3.OperationalStateEnum.Stopped,
|
|
166166
|
-
idle: OperationalState3.OperationalStateEnum.Stopped,
|
|
166167
|
-
standby: OperationalState3.OperationalStateEnum.Stopped,
|
|
166168
|
-
on: OperationalState3.OperationalStateEnum.Running,
|
|
166169
|
-
running: OperationalState3.OperationalStateEnum.Running,
|
|
166170
|
-
active: OperationalState3.OperationalStateEnum.Running,
|
|
166171
|
-
drying: OperationalState3.OperationalStateEnum.Running,
|
|
166172
|
-
washing: OperationalState3.OperationalStateEnum.Running,
|
|
166173
|
-
paused: OperationalState3.OperationalStateEnum.Paused,
|
|
166174
|
-
complete: OperationalState3.OperationalStateEnum.Stopped,
|
|
166175
|
-
finished: OperationalState3.OperationalStateEnum.Stopped
|
|
166176
|
-
};
|
|
166177
|
-
var DishwasherOperationalStateServer = class extends OperationalStateServer {
|
|
166178
|
-
async initialize() {
|
|
166179
|
-
this.state.operationalStateList = [
|
|
166180
|
-
{ operationalStateId: OperationalState3.OperationalStateEnum.Stopped },
|
|
166181
|
-
{ operationalStateId: OperationalState3.OperationalStateEnum.Running },
|
|
166182
|
-
{ operationalStateId: OperationalState3.OperationalStateEnum.Paused },
|
|
166183
|
-
{ operationalStateId: OperationalState3.OperationalStateEnum.Error }
|
|
166184
|
-
];
|
|
166185
|
-
this.state.operationalState = OperationalState3.OperationalStateEnum.Stopped;
|
|
166186
|
-
this.state.operationalError = {
|
|
166187
|
-
errorStateId: OperationalState3.ErrorState.NoError
|
|
166188
|
-
};
|
|
166189
|
-
await super.initialize();
|
|
166190
|
-
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
166191
|
-
this.update(homeAssistant.entity);
|
|
166192
|
-
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
166193
|
-
}
|
|
166194
|
-
update(entity) {
|
|
166195
|
-
if (!entity.state) {
|
|
166196
|
-
return;
|
|
166197
|
-
}
|
|
166198
|
-
const haState = entity.state.state?.toLowerCase() ?? "off";
|
|
166199
|
-
const newState = haStateToDishwasherState[haState] ?? OperationalState3.OperationalStateEnum.Stopped;
|
|
166200
|
-
applyPatchState(this.state, {
|
|
166201
|
-
operationalState: newState,
|
|
166202
|
-
operationalError: {
|
|
166203
|
-
errorStateId: OperationalState3.ErrorState.NoError
|
|
166204
|
-
}
|
|
166205
|
-
});
|
|
166206
|
-
}
|
|
166207
|
-
pause() {
|
|
166208
|
-
return {
|
|
166209
|
-
commandResponseState: {
|
|
166210
|
-
errorStateId: OperationalState3.ErrorState.CommandInvalidInState
|
|
166211
|
-
}
|
|
166212
|
-
};
|
|
166213
|
-
}
|
|
166214
|
-
stop() {
|
|
166215
|
-
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
166216
|
-
homeAssistant.callAction({ action: "homeassistant.turn_off" });
|
|
166217
|
-
return {
|
|
166218
|
-
commandResponseState: {
|
|
166219
|
-
errorStateId: OperationalState3.ErrorState.NoError
|
|
166220
|
-
}
|
|
166221
|
-
};
|
|
166222
|
-
}
|
|
166223
|
-
start() {
|
|
166224
|
-
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
166225
|
-
homeAssistant.callAction({ action: "homeassistant.turn_on" });
|
|
166226
|
-
return {
|
|
166227
|
-
commandResponseState: {
|
|
166228
|
-
errorStateId: OperationalState3.ErrorState.NoError
|
|
166229
|
-
}
|
|
166230
|
-
};
|
|
166231
|
-
}
|
|
166232
|
-
resume() {
|
|
166233
|
-
return this.start();
|
|
166234
|
-
}
|
|
166235
|
-
};
|
|
166236
|
-
var DishwasherOnOffServer = OnOffServer2({
|
|
166237
|
-
turnOn: () => ({
|
|
166238
|
-
action: "homeassistant.turn_on"
|
|
166239
|
-
}),
|
|
166240
|
-
turnOff: () => ({
|
|
166241
|
-
action: "homeassistant.turn_off"
|
|
166242
|
-
})
|
|
166243
|
-
});
|
|
166244
|
-
var DishwasherDeviceType = DishwasherDevice.with(
|
|
166245
|
-
BasicInformationServer2,
|
|
166246
|
-
IdentifyServer2,
|
|
166247
|
-
HomeAssistantEntityBehavior,
|
|
166248
|
-
DishwasherOperationalStateServer,
|
|
166249
|
-
DishwasherOnOffServer
|
|
166250
|
-
);
|
|
166251
|
-
function DishwasherEndpoint(homeAssistantEntity) {
|
|
166252
|
-
return DishwasherDeviceType.set({ homeAssistantEntity });
|
|
166253
|
-
}
|
|
166254
|
-
|
|
166255
166581
|
// src/matter/behaviors/generic-switch-server.ts
|
|
166256
166582
|
init_esm();
|
|
166257
166583
|
init_home_assistant_entity_behavior();
|
|
@@ -166778,8 +167104,10 @@ var ErrorState = RvcOperationalState4.ErrorState;
|
|
|
166778
167104
|
var logger227 = Logger.get("RvcOperationalStateServer");
|
|
166779
167105
|
var activeStates = /* @__PURE__ */ new Set([
|
|
166780
167106
|
OperationalState4.Running,
|
|
166781
|
-
OperationalState4.SeekingCharger
|
|
167107
|
+
OperationalState4.SeekingCharger,
|
|
167108
|
+
OperationalState4.Paused
|
|
166782
167109
|
]);
|
|
167110
|
+
var haStates = /* @__PURE__ */ new WeakMap();
|
|
166783
167111
|
var advertisedOperationalStates = [
|
|
166784
167112
|
OperationalState4.Stopped,
|
|
166785
167113
|
OperationalState4.Running,
|
|
@@ -166795,7 +167123,9 @@ function makeRvcOperationalError(errorStateId) {
|
|
|
166795
167123
|
}
|
|
166796
167124
|
return { errorStateId, errorStateDetails: ErrorState[errorStateId] };
|
|
166797
167125
|
}
|
|
166798
|
-
var RvcOperationalStateServerBase = class extends RvcOperationalStateServer
|
|
167126
|
+
var RvcOperationalStateServerBase = class extends RvcOperationalStateServer.enable({
|
|
167127
|
+
events: { operationCompletion: true }
|
|
167128
|
+
}) {
|
|
166799
167129
|
async initialize() {
|
|
166800
167130
|
this.state.operationalStateList = advertisedOperationalStates.map((id) => ({
|
|
166801
167131
|
operationalStateId: id
|
|
@@ -166818,7 +167148,8 @@ var RvcOperationalStateServerBase = class extends RvcOperationalStateServer {
|
|
|
166818
167148
|
entity.state,
|
|
166819
167149
|
this.agent
|
|
166820
167150
|
);
|
|
166821
|
-
const previousState = this.
|
|
167151
|
+
const previousState = haStates.get(this.endpoint);
|
|
167152
|
+
haStates.set(this.endpoint, newState);
|
|
166822
167153
|
const errorStateId = newState === OperationalState4.Error ? ErrorState.Stuck : ErrorState.NoError;
|
|
166823
167154
|
const operationalError = makeRvcOperationalError(errorStateId);
|
|
166824
167155
|
applyPatchState(
|
|
@@ -166829,7 +167160,8 @@ var RvcOperationalStateServerBase = class extends RvcOperationalStateServer {
|
|
|
166829
167160
|
},
|
|
166830
167161
|
{ force: true }
|
|
166831
167162
|
);
|
|
166832
|
-
|
|
167163
|
+
const offline = entity.state.state === "unavailable" || entity.state.state === "unknown";
|
|
167164
|
+
if (!offline && previousState !== void 0 && activeStates.has(previousState) && !activeStates.has(newState)) {
|
|
166833
167165
|
logger227.info(
|
|
166834
167166
|
`Operation completed: ${OperationalState4[previousState]} -> ${OperationalState4[newState]}`
|
|
166835
167167
|
);
|
|
@@ -166974,6 +167306,11 @@ function isRoomMode(mode) {
|
|
|
166974
167306
|
return mode >= ROOM_MODE_BASE;
|
|
166975
167307
|
}
|
|
166976
167308
|
var cleaningSessions = /* @__PURE__ */ new WeakMap();
|
|
167309
|
+
var idleOperationalStates = /* @__PURE__ */ new Set([
|
|
167310
|
+
RvcOperationalState4.OperationalState.Stopped,
|
|
167311
|
+
RvcOperationalState4.OperationalState.Charging,
|
|
167312
|
+
RvcOperationalState4.OperationalState.Docked
|
|
167313
|
+
]);
|
|
166977
167314
|
function getSession(endpoint) {
|
|
166978
167315
|
let session = cleaningSessions.get(endpoint);
|
|
166979
167316
|
if (!session) {
|
|
@@ -166984,7 +167321,9 @@ function getSession(endpoint) {
|
|
|
166984
167321
|
loggedShortCircuits: /* @__PURE__ */ new Set(),
|
|
166985
167322
|
observedCleaning: false,
|
|
166986
167323
|
pendingDispatches: [],
|
|
166987
|
-
cleanedAreaBaseline: null
|
|
167324
|
+
cleanedAreaBaseline: null,
|
|
167325
|
+
parkedAreas: [],
|
|
167326
|
+
haCleaning: false
|
|
166988
167327
|
};
|
|
166989
167328
|
cleaningSessions.set(endpoint, session);
|
|
166990
167329
|
}
|
|
@@ -167018,9 +167357,14 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167018
167357
|
},
|
|
167019
167358
|
{ force: true }
|
|
167020
167359
|
);
|
|
167360
|
+
if (!["unavailable", "unknown"].includes(entity.state.state)) {
|
|
167361
|
+
this.renameAreas(entity.state);
|
|
167362
|
+
}
|
|
167021
167363
|
if (newMode === 1 /* Cleaning */) {
|
|
167022
167364
|
s.observedCleaning = true;
|
|
167023
167365
|
}
|
|
167366
|
+
const wasHaCleaning = s.haCleaning;
|
|
167367
|
+
s.haCleaning = newMode === 1 /* Cleaning */;
|
|
167024
167368
|
if (previousMode !== newMode) {
|
|
167025
167369
|
if (newMode === 0 /* Idle */) {
|
|
167026
167370
|
if (s.lastCurrentArea !== null) {
|
|
@@ -167057,6 +167401,13 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167057
167401
|
} catch {
|
|
167058
167402
|
}
|
|
167059
167403
|
s.observedCleaning = false;
|
|
167404
|
+
if (s.activeAreas.every((id) => s.completedAreas.has(id))) {
|
|
167405
|
+
s.activeAreas = [];
|
|
167406
|
+
s.completedAreas.clear();
|
|
167407
|
+
s.lastCurrentArea = null;
|
|
167408
|
+
s.cleanedAreaBaseline = null;
|
|
167409
|
+
}
|
|
167410
|
+
this.restoreParkedAreas();
|
|
167060
167411
|
}
|
|
167061
167412
|
s.loggedShortCircuits.clear();
|
|
167062
167413
|
} else if (newMode === 1 /* Cleaning */) {
|
|
@@ -167067,6 +167418,8 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167067
167418
|
if (firstPending !== void 0) {
|
|
167068
167419
|
this.trySetCurrentArea(firstPending);
|
|
167069
167420
|
}
|
|
167421
|
+
} else if (s.activeAreas.length === 0 && !wasHaCleaning) {
|
|
167422
|
+
this.parkSelectedAreas();
|
|
167070
167423
|
}
|
|
167071
167424
|
}
|
|
167072
167425
|
}
|
|
@@ -167088,6 +167441,41 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167088
167441
|
}
|
|
167089
167442
|
}
|
|
167090
167443
|
}
|
|
167444
|
+
/**
|
|
167445
|
+
* A room renamed in HA keeps its id, so relabel the area in place. A
|
|
167446
|
+
* rebuild would cost the controller its selection and progress (#501).
|
|
167447
|
+
*/
|
|
167448
|
+
renameAreas(state) {
|
|
167449
|
+
const getAreaNames = this.state.config.getAreaNames;
|
|
167450
|
+
if (!getAreaNames) return;
|
|
167451
|
+
try {
|
|
167452
|
+
const names = new Map(
|
|
167453
|
+
getAreaNames(state, this.agent).map((a) => [a.areaId, a.name])
|
|
167454
|
+
);
|
|
167455
|
+
const serviceArea = this.agent.get(ServiceAreaBehavior);
|
|
167456
|
+
let renamed = false;
|
|
167457
|
+
const areas = serviceArea.state.supportedAreas.map((area) => {
|
|
167458
|
+
const name = names.get(area.areaId);
|
|
167459
|
+
const info = area.areaInfo.locationInfo;
|
|
167460
|
+
if (!name || !info || info.locationName === name) return area;
|
|
167461
|
+
renamed = true;
|
|
167462
|
+
return {
|
|
167463
|
+
...area,
|
|
167464
|
+
areaInfo: {
|
|
167465
|
+
...area.areaInfo,
|
|
167466
|
+
locationInfo: { ...info, locationName: name }
|
|
167467
|
+
}
|
|
167468
|
+
};
|
|
167469
|
+
});
|
|
167470
|
+
if (renamed) {
|
|
167471
|
+
serviceArea.state.supportedAreas = areas;
|
|
167472
|
+
logger229.info(
|
|
167473
|
+
`Renamed service areas: ${areas.map((a) => `${a.areaId}:${a.areaInfo.locationInfo?.locationName}`).join(", ")}`
|
|
167474
|
+
);
|
|
167475
|
+
}
|
|
167476
|
+
} catch {
|
|
167477
|
+
}
|
|
167478
|
+
}
|
|
167091
167479
|
/**
|
|
167092
167480
|
* Emit a diagnostic INFO log exactly once per cleaning session for a
|
|
167093
167481
|
* given short-circuit reason. Prevents log flooding while still
|
|
@@ -167125,41 +167513,55 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167125
167513
|
return;
|
|
167126
167514
|
}
|
|
167127
167515
|
const serviceArea = this.agent.get(ServiceAreaBehavior);
|
|
167128
|
-
const externalSession = s.activeAreas.length === 0;
|
|
167129
167516
|
const supportedAreaIds = serviceArea.state.supportedAreas.map(
|
|
167130
167517
|
(a) => a.areaId
|
|
167131
167518
|
);
|
|
167132
|
-
const isAllowedArea = (id) => externalSession ? supportedAreaIds.includes(id) : s.activeAreas.includes(id);
|
|
167133
167519
|
const sensorAttrs = roomState.attributes;
|
|
167134
167520
|
const segmentId = sensorAttrs.segment_id ?? sensorAttrs.room_id;
|
|
167135
167521
|
const roomName = roomState.state;
|
|
167136
|
-
|
|
167137
|
-
|
|
167138
|
-
|
|
167139
|
-
|
|
167140
|
-
|
|
167141
|
-
|
|
167142
|
-
|
|
167143
|
-
|
|
167144
|
-
|
|
167522
|
+
const findArea = (isAllowedArea) => {
|
|
167523
|
+
if (segmentId != null && isAllowedArea(segmentId)) {
|
|
167524
|
+
return segmentId;
|
|
167525
|
+
}
|
|
167526
|
+
if (segmentId != null) {
|
|
167527
|
+
for (const area of serviceArea.state.supportedAreas) {
|
|
167528
|
+
if (isAllowedArea(area.areaId) && area.areaId % 1e4 === segmentId) {
|
|
167529
|
+
return area.areaId;
|
|
167530
|
+
}
|
|
167145
167531
|
}
|
|
167146
167532
|
}
|
|
167147
|
-
|
|
167148
|
-
|
|
167149
|
-
|
|
167150
|
-
|
|
167151
|
-
|
|
167152
|
-
|
|
167153
|
-
|
|
167533
|
+
if (roomName) {
|
|
167534
|
+
const area = serviceArea.state.supportedAreas.find(
|
|
167535
|
+
(a) => a.areaInfo.locationInfo?.locationName?.toLowerCase() === roomName.toLowerCase()
|
|
167536
|
+
);
|
|
167537
|
+
if (area && isAllowedArea(area.areaId)) {
|
|
167538
|
+
return area.areaId;
|
|
167539
|
+
}
|
|
167154
167540
|
}
|
|
167155
|
-
|
|
167541
|
+
return null;
|
|
167542
|
+
};
|
|
167543
|
+
const matchedAreaId = findArea((id) => s.activeAreas.includes(id)) ?? findArea((id) => supportedAreaIds.includes(id));
|
|
167156
167544
|
if (matchedAreaId === null) {
|
|
167157
167545
|
logger229.info(
|
|
167158
167546
|
`currentRoom sensor: no match for "${roomName}" (segmentId=${segmentId}), activeAreas=[${s.activeAreas.join(", ")}], supportedAreas=[${serviceArea.state.supportedAreas.map((a) => `${a.areaId}:${a.areaInfo.locationInfo?.locationName}`).join(", ")}]`
|
|
167159
167547
|
);
|
|
167160
167548
|
return;
|
|
167161
167549
|
}
|
|
167162
|
-
if (
|
|
167550
|
+
if (s.activeAreas.length > 0 && !s.activeAreas.includes(matchedAreaId)) {
|
|
167551
|
+
if (serviceArea.state.currentArea !== matchedAreaId) {
|
|
167552
|
+
serviceArea.state.currentArea = matchedAreaId;
|
|
167553
|
+
logger229.info(
|
|
167554
|
+
`currentRoom sensor: passing through area ${matchedAreaId} ("${roomName}")`
|
|
167555
|
+
);
|
|
167556
|
+
}
|
|
167557
|
+
return;
|
|
167558
|
+
}
|
|
167559
|
+
if (matchedAreaId === s.lastCurrentArea) {
|
|
167560
|
+
if (serviceArea.state.currentArea !== matchedAreaId) {
|
|
167561
|
+
this.trySetCurrentArea(matchedAreaId);
|
|
167562
|
+
}
|
|
167563
|
+
return;
|
|
167564
|
+
}
|
|
167163
167565
|
if (s.lastCurrentArea !== null) {
|
|
167164
167566
|
s.completedAreas.add(s.lastCurrentArea);
|
|
167165
167567
|
}
|
|
@@ -167343,13 +167745,64 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167343
167745
|
const state = serviceArea.state;
|
|
167344
167746
|
state.progress = s.activeAreas.map((id) => ({
|
|
167345
167747
|
areaId: id,
|
|
167346
|
-
status: s.completedAreas.has(id) || id === last ? ServiceArea3.OperationalStatus.Completed : ServiceArea3.OperationalStatus.Skipped
|
|
167748
|
+
status: s.completedAreas.has(id) || id === last || id === s.lastCurrentArea ? ServiceArea3.OperationalStatus.Completed : ServiceArea3.OperationalStatus.Skipped
|
|
167347
167749
|
}));
|
|
167348
167750
|
}
|
|
167349
167751
|
serviceArea.state.currentArea = null;
|
|
167350
167752
|
} catch {
|
|
167351
167753
|
}
|
|
167352
167754
|
}
|
|
167755
|
+
/**
|
|
167756
|
+
* A clean started from HA, the vendor app, Alexa on/off or a room switch
|
|
167757
|
+
* is not the last controller job. Keeping that job's rooms made
|
|
167758
|
+
* Apple Home show "cleaning 3 rooms" for a one room clean (#492).
|
|
167759
|
+
*/
|
|
167760
|
+
parkSelectedAreas() {
|
|
167761
|
+
const s = getSession(this.endpoint);
|
|
167762
|
+
try {
|
|
167763
|
+
const serviceArea = this.agent.get(ServiceAreaBehavior);
|
|
167764
|
+
const state = serviceArea.state;
|
|
167765
|
+
if (state.selectedAreas.length > 0) {
|
|
167766
|
+
s.parkedAreas = [...state.selectedAreas];
|
|
167767
|
+
state.selectedAreas = [];
|
|
167768
|
+
}
|
|
167769
|
+
if (state.progress?.length) {
|
|
167770
|
+
state.progress = [];
|
|
167771
|
+
}
|
|
167772
|
+
} catch {
|
|
167773
|
+
}
|
|
167774
|
+
}
|
|
167775
|
+
/**
|
|
167776
|
+
* Put the parked selection back once the outside clean is over. Apple
|
|
167777
|
+
* Home does not re-send a selection it thinks is still set (#317).
|
|
167778
|
+
* A newer controller selection wins.
|
|
167779
|
+
*/
|
|
167780
|
+
restoreParkedAreas() {
|
|
167781
|
+
const s = getSession(this.endpoint);
|
|
167782
|
+
if (s.parkedAreas.length === 0) return;
|
|
167783
|
+
try {
|
|
167784
|
+
const serviceArea = this.agent.get(ServiceAreaBehavior);
|
|
167785
|
+
if (serviceArea.state.selectedAreas.length === 0) {
|
|
167786
|
+
serviceArea.state.selectedAreas = s.parkedAreas;
|
|
167787
|
+
}
|
|
167788
|
+
} catch {
|
|
167789
|
+
}
|
|
167790
|
+
s.parkedAreas = [];
|
|
167791
|
+
}
|
|
167792
|
+
/**
|
|
167793
|
+
* HA can take 15 s to report cleaning after a start. Apple Home drops its
|
|
167794
|
+
* own "Vacuuming" after about 10 s and shows Cleaning + Docked as "Ready,
|
|
167795
|
+
* Resume Cleaning Later" until then (#496). The next HA update corrects it.
|
|
167796
|
+
*/
|
|
167797
|
+
showRunning() {
|
|
167798
|
+
try {
|
|
167799
|
+
const opState = this.agent.get(RvcOperationalStateBehavior);
|
|
167800
|
+
if (idleOperationalStates.has(opState.state.operationalState)) {
|
|
167801
|
+
opState.state.operationalState = RvcOperationalState4.OperationalState.Running;
|
|
167802
|
+
}
|
|
167803
|
+
} catch {
|
|
167804
|
+
}
|
|
167805
|
+
}
|
|
167353
167806
|
/**
|
|
167354
167807
|
* Find the ServiceArea area ID that corresponds to a run mode value
|
|
167355
167808
|
* by matching the mode label to the area location name.
|
|
@@ -167377,6 +167830,10 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167377
167830
|
statusText: `Unsupported mode: ${newMode}`
|
|
167378
167831
|
};
|
|
167379
167832
|
}
|
|
167833
|
+
if (newMode !== 0 /* Idle */) {
|
|
167834
|
+
this.restoreParkedAreas();
|
|
167835
|
+
s.haCleaning = true;
|
|
167836
|
+
}
|
|
167380
167837
|
if (isRoomMode(newMode)) {
|
|
167381
167838
|
try {
|
|
167382
167839
|
const serviceArea = this.agent.get(ServiceAreaBehavior);
|
|
@@ -167392,6 +167849,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167392
167849
|
s.cleanedAreaBaseline = this.readCleanedAreaSqm();
|
|
167393
167850
|
this.trySetCurrentArea(s.activeAreas[0]);
|
|
167394
167851
|
homeAssistant.callAction(this.state.config.start(void 0, this.agent));
|
|
167852
|
+
this.showRunning();
|
|
167395
167853
|
this.state.currentMode = newMode;
|
|
167396
167854
|
return {
|
|
167397
167855
|
status: ModeBase3.ModeChangeStatus.Success,
|
|
@@ -167411,6 +167869,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167411
167869
|
homeAssistant.callAction(
|
|
167412
167870
|
this.state.config.cleanRoom(newMode, this.agent)
|
|
167413
167871
|
);
|
|
167872
|
+
this.showRunning();
|
|
167414
167873
|
this.state.currentMode = newMode;
|
|
167415
167874
|
return {
|
|
167416
167875
|
status: ModeBase3.ModeChangeStatus.Success,
|
|
@@ -167437,10 +167896,12 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
|
|
|
167437
167896
|
} catch {
|
|
167438
167897
|
}
|
|
167439
167898
|
homeAssistant.callAction(this.state.config.start(void 0, this.agent));
|
|
167899
|
+
this.showRunning();
|
|
167440
167900
|
break;
|
|
167441
167901
|
}
|
|
167442
167902
|
case 0 /* Idle */:
|
|
167443
167903
|
this.finalizeProgressOnStop();
|
|
167904
|
+
this.restoreParkedAreas();
|
|
167444
167905
|
s.completedAreas.clear();
|
|
167445
167906
|
s.lastCurrentArea = null;
|
|
167446
167907
|
s.activeAreas = [];
|
|
@@ -167637,6 +168098,12 @@ var ColorControlServerBase = class extends FeaturedBase8 {
|
|
|
167637
168098
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
167638
168099
|
this.update(homeAssistant.entity);
|
|
167639
168100
|
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
168101
|
+
this.reactTo(homeAssistant.events.actionFailed, this.dropOptimistic);
|
|
168102
|
+
}
|
|
168103
|
+
dropOptimistic() {
|
|
168104
|
+
optimisticColorState.delete(
|
|
168105
|
+
this.agent.get(HomeAssistantEntityBehavior).entityId
|
|
168106
|
+
);
|
|
167640
168107
|
}
|
|
167641
168108
|
update(entity) {
|
|
167642
168109
|
if (!entity.state || !entity.state.attributes) {
|
|
@@ -169356,6 +169823,12 @@ var SpeakerLevelControlServerBase = class extends FeaturedBase9 {
|
|
|
169356
169823
|
const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
|
|
169357
169824
|
this.update(homeAssistant.entity);
|
|
169358
169825
|
this.reactTo(homeAssistant.onChange, this.update, { lock: true });
|
|
169826
|
+
this.reactTo(homeAssistant.events.actionFailed, this.dropOptimistic);
|
|
169827
|
+
}
|
|
169828
|
+
dropOptimistic() {
|
|
169829
|
+
optimisticLevelState2.delete(
|
|
169830
|
+
this.agent.get(HomeAssistantEntityBehavior).entityId
|
|
169831
|
+
);
|
|
169359
169832
|
}
|
|
169360
169833
|
update(entity) {
|
|
169361
169834
|
if (!entity.state || !entity.state.attributes) {
|
|
@@ -172402,6 +172875,7 @@ var ServiceAreaServerBase = class extends ServiceAreaWithProgress {
|
|
|
172402
172875
|
};
|
|
172403
172876
|
}
|
|
172404
172877
|
this.state.selectedAreas = uniqueAreas;
|
|
172878
|
+
getSession(this.endpoint).parkedAreas = [];
|
|
172405
172879
|
this.state.progress = uniqueAreas.map((areaId) => ({
|
|
172406
172880
|
areaId,
|
|
172407
172881
|
status: ServiceArea3.OperationalStatus.Pending
|
|
@@ -172461,6 +172935,7 @@ var ServiceAreaServerWithMapsBase = class extends ServiceAreaWithMapsAndProgress
|
|
|
172461
172935
|
};
|
|
172462
172936
|
}
|
|
172463
172937
|
this.state.selectedAreas = uniqueAreas;
|
|
172938
|
+
getSession(this.endpoint).parkedAreas = [];
|
|
172464
172939
|
this.state.progress = uniqueAreas.map((areaId) => ({
|
|
172465
172940
|
areaId,
|
|
172466
172941
|
status: ServiceArea3.OperationalStatus.Pending
|
|
@@ -172676,6 +173151,17 @@ function createCleanAreaServiceAreaServer(cleanAreaRooms) {
|
|
|
172676
173151
|
currentArea: null
|
|
172677
173152
|
});
|
|
172678
173153
|
}
|
|
173154
|
+
function withResolvedRooms(state, effective) {
|
|
173155
|
+
const rooms = effective?.state.attributes?.rooms;
|
|
173156
|
+
const live = state.attributes;
|
|
173157
|
+
if (!rooms || !live || live.rooms || live.segments || live.room_mapping) {
|
|
173158
|
+
return state;
|
|
173159
|
+
}
|
|
173160
|
+
return {
|
|
173161
|
+
...state,
|
|
173162
|
+
attributes: { ...state.attributes, rooms }
|
|
173163
|
+
};
|
|
173164
|
+
}
|
|
172679
173165
|
function getVacuumServiceAreas(attributes9, mapping) {
|
|
172680
173166
|
const cleanAreaRooms = mapping?.cleanAreaRooms;
|
|
172681
173167
|
if (cleanAreaRooms && cleanAreaRooms.length > 0) {
|
|
@@ -172885,7 +173371,9 @@ function dispatchRoomClean(attributes9, mapping, entityId, selectedAreas, seam)
|
|
|
172885
173371
|
loggedShortCircuits: /* @__PURE__ */ new Set(),
|
|
172886
173372
|
observedCleaning: false,
|
|
172887
173373
|
pendingDispatches: [],
|
|
172888
|
-
cleanedAreaBaseline: null
|
|
173374
|
+
cleanedAreaBaseline: null,
|
|
173375
|
+
parkedAreas: [],
|
|
173376
|
+
haCleaning: false
|
|
172889
173377
|
};
|
|
172890
173378
|
const action = handleCustomServiceAreas(
|
|
172891
173379
|
selectedAreas,
|
|
@@ -173015,6 +173503,9 @@ var vacuumRvcRunModeConfig = {
|
|
|
173015
173503
|
getSupportedModes: (entity, agent) => {
|
|
173016
173504
|
const attributes9 = entity.attributes;
|
|
173017
173505
|
const mapping = agent.get(HomeAssistantEntityBehavior).state.mapping;
|
|
173506
|
+
if (mapping?.cleanAreaRooms?.length) {
|
|
173507
|
+
return buildCleanAreaModes(mapping.cleanAreaRooms);
|
|
173508
|
+
}
|
|
173018
173509
|
const customAreas = mapping?.customServiceAreas;
|
|
173019
173510
|
return buildSupportedModes2(
|
|
173020
173511
|
attributes9,
|
|
@@ -173023,6 +173514,10 @@ var vacuumRvcRunModeConfig = {
|
|
|
173023
173514
|
mapping?.disableCustomAreaRoomModes
|
|
173024
173515
|
);
|
|
173025
173516
|
},
|
|
173517
|
+
getAreaNames: (entity, agent) => getVacuumServiceAreas(
|
|
173518
|
+
entity.attributes,
|
|
173519
|
+
agent.get(HomeAssistantEntityBehavior).state.mapping
|
|
173520
|
+
),
|
|
173026
173521
|
// biome-ignore lint/suspicious/noConfusingVoidType: Required by ValueSetter<void> interface
|
|
173027
173522
|
start: (_, agent) => {
|
|
173028
173523
|
try {
|
|
@@ -173205,7 +173700,7 @@ function createVacuumRvcRunModeServer(attributes9, includeUnnamedRooms = false,
|
|
|
173205
173700
|
currentMode: 0 /* Idle */
|
|
173206
173701
|
});
|
|
173207
173702
|
}
|
|
173208
|
-
function
|
|
173703
|
+
function buildCleanAreaModes(cleanAreaRooms) {
|
|
173209
173704
|
const modes = [
|
|
173210
173705
|
{
|
|
173211
173706
|
label: "Idle",
|
|
@@ -173230,6 +173725,10 @@ function createCleanAreaRvcRunModeServer(cleanAreaRooms) {
|
|
|
173230
173725
|
modeTags: [{ value: RvcRunMode3.ModeTag.Cleaning }]
|
|
173231
173726
|
});
|
|
173232
173727
|
}
|
|
173728
|
+
return modes;
|
|
173729
|
+
}
|
|
173730
|
+
function createCleanAreaRvcRunModeServer(cleanAreaRooms) {
|
|
173731
|
+
const modes = buildCleanAreaModes(cleanAreaRooms);
|
|
173233
173732
|
logger241.info(
|
|
173234
173733
|
`Creating CLEAN_AREA RvcRunModeServer with ${cleanAreaRooms.length} HA areas, ${modes.length} total modes`
|
|
173235
173734
|
);
|
|
@@ -175206,6 +175705,8 @@ var matterDeviceTypeFactories = {
|
|
|
175206
175705
|
return SwitchDevice(ha);
|
|
175207
175706
|
},
|
|
175208
175707
|
dishwasher: DishwasherEndpoint,
|
|
175708
|
+
laundry_washer: LaundryWasherEndpoint,
|
|
175709
|
+
laundry_dryer: LaundryDryerEndpoint,
|
|
175209
175710
|
dimmable_plugin_unit: (ha) => DimmablePlugInUnitType.set({ homeAssistantEntity: ha }),
|
|
175210
175711
|
// Expose as On/Off Light (0x0100, an OnOff server) so controllers show a
|
|
175211
175712
|
// switch instead of a plug. Plain SwitchDevice is 0x010A (a plug), so the
|
|
@@ -175269,11 +175770,12 @@ var matterDeviceTypeFactories = {
|
|
|
175269
175770
|
};
|
|
175270
175771
|
|
|
175271
175772
|
// src/matter/endpoints/composed/user-composed-endpoint.ts
|
|
175773
|
+
init_update_entity_state();
|
|
175272
175774
|
var logger249 = Logger.get("UserComposedEndpoint");
|
|
175273
175775
|
function stripBasicInformation(type) {
|
|
175274
|
-
const
|
|
175275
|
-
delete
|
|
175276
|
-
return { ...type, behaviors };
|
|
175776
|
+
const behaviors2 = { ...type.behaviors };
|
|
175777
|
+
delete behaviors2.bridgedDeviceBasicInformation;
|
|
175778
|
+
return { ...type, behaviors: behaviors2 };
|
|
175277
175779
|
}
|
|
175278
175780
|
function createEndpointId5(entityId, customName) {
|
|
175279
175781
|
const baseName = customName || entityId;
|
|
@@ -175502,11 +176004,11 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
|
|
|
175502
176004
|
init_esm7();
|
|
175503
176005
|
var BRIDGED_INFO_ID = "bridgedDeviceBasicInformation";
|
|
175504
176006
|
function asStandaloneEndpointType(type) {
|
|
175505
|
-
const
|
|
175506
|
-
if (!(BRIDGED_INFO_ID in
|
|
176007
|
+
const behaviors2 = type.behaviors;
|
|
176008
|
+
if (!(BRIDGED_INFO_ID in behaviors2)) {
|
|
175507
176009
|
return type;
|
|
175508
176010
|
}
|
|
175509
|
-
const kept = Object.entries(
|
|
176011
|
+
const kept = Object.entries(behaviors2).filter(([id]) => id !== BRIDGED_INFO_ID).map(([, behavior]) => behavior);
|
|
175510
176012
|
return MutableEndpoint({
|
|
175511
176013
|
name: type.name,
|
|
175512
176014
|
deviceType: type.deviceType,
|
|
@@ -175518,7 +176020,13 @@ function asStandaloneEndpointType(type) {
|
|
|
175518
176020
|
}
|
|
175519
176021
|
|
|
175520
176022
|
// src/matter/endpoints/legacy/legacy-endpoint.ts
|
|
176023
|
+
init_update_entity_state();
|
|
175521
176024
|
var logger250 = Logger.get("LegacyEndpoint");
|
|
176025
|
+
var applianceTypes = /* @__PURE__ */ new Set([
|
|
176026
|
+
"dishwasher",
|
|
176027
|
+
"laundry_washer",
|
|
176028
|
+
"laundry_dryer"
|
|
176029
|
+
]);
|
|
175522
176030
|
var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
|
|
175523
176031
|
constructor(type, entityId, customName, mappedEntityIds, throttleMs, endpointId, vacuumEffective) {
|
|
175524
176032
|
super(type, entityId, customName, mappedEntityIds, endpointId);
|
|
@@ -175644,11 +176152,27 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
|
|
|
175644
176152
|
logger250.debug(`Auto-assigned fault ${faultEntityId} to ${entityId}`);
|
|
175645
176153
|
}
|
|
175646
176154
|
}
|
|
176155
|
+
if (mapping?.matterDeviceType && applianceTypes.has(mapping.matterDeviceType) && !mapping.operationalStateEntity) {
|
|
176156
|
+
const stateEntityId = registry3.findOperationalStateEntityForDevice(
|
|
176157
|
+
entity.device_id
|
|
176158
|
+
);
|
|
176159
|
+
if (stateEntityId && stateEntityId !== entityId) {
|
|
176160
|
+
effectiveMapping = {
|
|
176161
|
+
...effectiveMapping,
|
|
176162
|
+
entityId: effectiveMapping?.entityId ?? entityId,
|
|
176163
|
+
operationalStateEntity: stateEntityId
|
|
176164
|
+
};
|
|
176165
|
+
logger250.debug(
|
|
176166
|
+
`Auto-assigned operational state ${stateEntityId} to ${entityId}`
|
|
176167
|
+
);
|
|
176168
|
+
}
|
|
176169
|
+
}
|
|
175647
176170
|
if (!mapping?.powerEntity) {
|
|
175648
176171
|
const domain = entityId.split(".")[0];
|
|
175649
176172
|
if (domain === "switch") {
|
|
175650
176173
|
const powerEntityId = registry3.findPowerEntityForDevice(
|
|
175651
|
-
entity.device_id
|
|
176174
|
+
entity.device_id,
|
|
176175
|
+
entityId
|
|
175652
176176
|
);
|
|
175653
176177
|
if (powerEntityId && powerEntityId !== entityId) {
|
|
175654
176178
|
effectiveMapping = {
|
|
@@ -175665,7 +176189,8 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
|
|
|
175665
176189
|
const domain = entityId.split(".")[0];
|
|
175666
176190
|
if (domain === "switch") {
|
|
175667
176191
|
const energyEntityId = registry3.findEnergyEntityForDevice(
|
|
175668
|
-
entity.device_id
|
|
176192
|
+
entity.device_id,
|
|
176193
|
+
entityId
|
|
175669
176194
|
);
|
|
175670
176195
|
if (energyEntityId && energyEntityId !== entityId) {
|
|
175671
176196
|
effectiveMapping = {
|
|
@@ -175958,10 +176483,13 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
|
|
|
175958
176483
|
this.flushUpdate(state);
|
|
175959
176484
|
}
|
|
175960
176485
|
async flushPendingUpdate(state) {
|
|
175961
|
-
let effectiveState = state;
|
|
176486
|
+
let effectiveState = withResolvedRooms(state, this.vacuumEffective);
|
|
175962
176487
|
if (this.pendingMappedChange) {
|
|
175963
176488
|
this.pendingMappedChange = false;
|
|
175964
|
-
effectiveState = {
|
|
176489
|
+
effectiveState = {
|
|
176490
|
+
...effectiveState,
|
|
176491
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
176492
|
+
};
|
|
175965
176493
|
}
|
|
175966
176494
|
await updateEntityState(this, effectiveState);
|
|
175967
176495
|
}
|
|
@@ -175969,6 +176497,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
|
|
|
175969
176497
|
|
|
175970
176498
|
// src/matter/endpoints/legacy/vacuum/vacuum-area-switch.ts
|
|
175971
176499
|
init_home_assistant_entity_behavior();
|
|
176500
|
+
init_update_entity_state();
|
|
175972
176501
|
var VacuumAreaSwitchEndpoint = class _VacuumAreaSwitchEndpoint extends EntityEndpoint {
|
|
175973
176502
|
constructor(type, entityId, endpointId, areaId, vacuumEndpointId, parentEffective) {
|
|
175974
176503
|
super(type, entityId, void 0, [], endpointId);
|
|
@@ -176037,6 +176566,9 @@ var VacuumAreaSwitchEndpoint = class _VacuumAreaSwitchEndpoint extends EntityEnd
|
|
|
176037
176566
|
}
|
|
176038
176567
|
};
|
|
176039
176568
|
|
|
176569
|
+
// src/services/bridges/bridge-endpoint-manager.ts
|
|
176570
|
+
init_update_entity_state();
|
|
176571
|
+
|
|
176040
176572
|
// src/plugins/builtin/camera/camera-plugin.ts
|
|
176041
176573
|
init_esm();
|
|
176042
176574
|
|
|
@@ -178216,10 +178748,1170 @@ var SecurityPlugin = class {
|
|
|
178216
178748
|
}
|
|
178217
178749
|
};
|
|
178218
178750
|
|
|
178751
|
+
// src/plugins/builtin/vesync/vesync-plugin.ts
|
|
178752
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
178753
|
+
|
|
178754
|
+
// src/plugins/builtin/vesync/vesync-client.ts
|
|
178755
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
178756
|
+
var APP_VERSION = "5.6.60";
|
|
178757
|
+
var CLIENT_VERSION = `VeSync ${APP_VERSION}`;
|
|
178758
|
+
var PHONE_BRAND = "pyvesync";
|
|
178759
|
+
var PHONE_OS = "Android";
|
|
178760
|
+
var APP_ID = "eldodkfj";
|
|
178761
|
+
var CLIENT_TYPE = "vesyncApp";
|
|
178762
|
+
var LANGUAGE = "en";
|
|
178763
|
+
var HEADERS = {
|
|
178764
|
+
"Content-Type": "application/json; charset=UTF-8",
|
|
178765
|
+
"User-Agent": "okhttp/3.12.1"
|
|
178766
|
+
};
|
|
178767
|
+
var HOSTS = {
|
|
178768
|
+
US: "https://smartapi.vesync.com",
|
|
178769
|
+
EU: "https://smartapi.vesync.eu"
|
|
178770
|
+
};
|
|
178771
|
+
var US_COUNTRIES = ["US", "CA", "MX", "JP"];
|
|
178772
|
+
var TIME_ZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
178773
|
+
var TIMEOUT_MS = 15e3;
|
|
178774
|
+
var PAGE_SIZE = 100;
|
|
178775
|
+
var AUTH_PATH = "/globalPlatform/api/accountAuth/v1/authByPWDOrOTM";
|
|
178776
|
+
var LOGIN_PATH = "/user/api/accountManage/v1/loginByAuthorizeCode4Vesync";
|
|
178777
|
+
var DEVICES_PATH = "/cloud/v1/deviceManaged/devices";
|
|
178778
|
+
var V1_PATH = "/cloud/v1/deviceManaged/";
|
|
178779
|
+
var V2_PATH = "/cloud/v2/deviceManaged/bypassV2";
|
|
178780
|
+
var CROSS_REGION_CODES = [-11260022, -11261022];
|
|
178781
|
+
var MFA_CODE = -11257129;
|
|
178782
|
+
var OFFLINE_CODES = [11, 4041004];
|
|
178783
|
+
var OFFLINE_GROUPS = [-113e5, -11302e3];
|
|
178784
|
+
var QUOTA_GROUPS = [-16906e3, -11003e3];
|
|
178785
|
+
var CREDENTIAL_GROUPS = [-11201e3, -11202e3, -112e5, -11e6];
|
|
178786
|
+
var TOKEN_STATUSES = [401, 419];
|
|
178787
|
+
var DESCRIPTIONS = {
|
|
178788
|
+
credentials: "wrong email or password",
|
|
178789
|
+
mfa: "two-factor sign-in is not supported",
|
|
178790
|
+
quota: "daily request quota used up",
|
|
178791
|
+
token: "session token rejected",
|
|
178792
|
+
offline: "device offline",
|
|
178793
|
+
network: "cloud unreachable",
|
|
178794
|
+
api: "request failed"
|
|
178795
|
+
};
|
|
178796
|
+
var VeSyncError = class extends Error {
|
|
178797
|
+
constructor(kind, message, code) {
|
|
178798
|
+
super(message);
|
|
178799
|
+
this.kind = kind;
|
|
178800
|
+
this.code = code;
|
|
178801
|
+
this.name = "VeSyncError";
|
|
178802
|
+
}
|
|
178803
|
+
kind;
|
|
178804
|
+
code;
|
|
178805
|
+
};
|
|
178806
|
+
function obj(value) {
|
|
178807
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
178808
|
+
}
|
|
178809
|
+
function str(value) {
|
|
178810
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
178811
|
+
}
|
|
178812
|
+
function errorFor(raw, op, login) {
|
|
178813
|
+
const code = Number(raw);
|
|
178814
|
+
if (!Number.isFinite(code)) {
|
|
178815
|
+
return new VeSyncError("api", `VeSync ${op}: ${DESCRIPTIONS.api}`);
|
|
178816
|
+
}
|
|
178817
|
+
const group = Math.trunc(code / 1e3) * 1e3;
|
|
178818
|
+
let kind = "api";
|
|
178819
|
+
if (code === MFA_CODE) kind = "mfa";
|
|
178820
|
+
else if (OFFLINE_CODES.includes(code) || OFFLINE_GROUPS.includes(group))
|
|
178821
|
+
kind = "offline";
|
|
178822
|
+
else if (group === -11001e3) kind = "token";
|
|
178823
|
+
else if (QUOTA_GROUPS.includes(group)) kind = "quota";
|
|
178824
|
+
else if (login && CREDENTIAL_GROUPS.includes(group)) kind = "credentials";
|
|
178825
|
+
return new VeSyncError(
|
|
178826
|
+
kind,
|
|
178827
|
+
`VeSync ${op}: ${DESCRIPTIONS[kind]} (code ${code})`,
|
|
178828
|
+
code
|
|
178829
|
+
);
|
|
178830
|
+
}
|
|
178831
|
+
function networkError(op, reason) {
|
|
178832
|
+
return new VeSyncError(
|
|
178833
|
+
"network",
|
|
178834
|
+
`VeSync ${op}: ${DESCRIPTIONS.network} (${reason})`
|
|
178835
|
+
);
|
|
178836
|
+
}
|
|
178837
|
+
function toDevice(row) {
|
|
178838
|
+
const prop = obj(row.deviceProp);
|
|
178839
|
+
const cid = str(row.cid) ?? str(row.uuid) ?? str(prop.wifiMac) ?? str(row.macID);
|
|
178840
|
+
const deviceType = str(row.deviceType);
|
|
178841
|
+
if (!cid || !deviceType) return void 0;
|
|
178842
|
+
return {
|
|
178843
|
+
cid,
|
|
178844
|
+
uuid: str(row.uuid),
|
|
178845
|
+
name: str(row.deviceName) ?? deviceType,
|
|
178846
|
+
deviceType,
|
|
178847
|
+
configModule: str(row.configModule) ?? "",
|
|
178848
|
+
subDeviceNo: typeof row.subDeviceNo === "number" ? row.subDeviceNo : void 0,
|
|
178849
|
+
online: (row.connectionStatus ?? prop.connectionStatus) === "online"
|
|
178850
|
+
};
|
|
178851
|
+
}
|
|
178852
|
+
function generateTerminalId() {
|
|
178853
|
+
return `2${randomUUID().replaceAll("-", "")}`;
|
|
178854
|
+
}
|
|
178855
|
+
var VeSyncClient = class {
|
|
178856
|
+
#email;
|
|
178857
|
+
#passwordHash;
|
|
178858
|
+
#terminalId;
|
|
178859
|
+
#fetch;
|
|
178860
|
+
#onSession;
|
|
178861
|
+
#session;
|
|
178862
|
+
#region;
|
|
178863
|
+
#countryCode;
|
|
178864
|
+
#pendingLogin;
|
|
178865
|
+
#calls = 0;
|
|
178866
|
+
constructor(options) {
|
|
178867
|
+
this.#email = options.email;
|
|
178868
|
+
this.#passwordHash = createHash2("md5").update(options.password, "utf8").digest("hex");
|
|
178869
|
+
this.#terminalId = options.terminalId;
|
|
178870
|
+
this.#fetch = options.fetch;
|
|
178871
|
+
this.#onSession = options.onSession;
|
|
178872
|
+
this.#session = options.session;
|
|
178873
|
+
this.#region = options.session?.region ?? "US";
|
|
178874
|
+
this.#countryCode = options.session?.countryCode ?? "US";
|
|
178875
|
+
}
|
|
178876
|
+
// concurrent callers share one sign-in
|
|
178877
|
+
login() {
|
|
178878
|
+
if (this.#session) return Promise.resolve(this.#session);
|
|
178879
|
+
this.#pendingLogin ??= this.#signIn().finally(() => {
|
|
178880
|
+
this.#pendingLogin = void 0;
|
|
178881
|
+
});
|
|
178882
|
+
return this.#pendingLogin;
|
|
178883
|
+
}
|
|
178884
|
+
async devices() {
|
|
178885
|
+
const devices = [];
|
|
178886
|
+
for (let pageNo = 1; ; pageNo++) {
|
|
178887
|
+
const result = await this.#request("devices", DEVICES_PATH, (s) => ({
|
|
178888
|
+
token: s.token,
|
|
178889
|
+
accountID: s.accountId,
|
|
178890
|
+
timeZone: TIME_ZONE,
|
|
178891
|
+
method: "devices",
|
|
178892
|
+
pageNo,
|
|
178893
|
+
pageSize: PAGE_SIZE,
|
|
178894
|
+
appVersion: APP_VERSION,
|
|
178895
|
+
phoneBrand: PHONE_BRAND,
|
|
178896
|
+
phoneOS: PHONE_OS,
|
|
178897
|
+
acceptLanguage: LANGUAGE,
|
|
178898
|
+
traceId: this.#traceId()
|
|
178899
|
+
}));
|
|
178900
|
+
const list3 = Array.isArray(result.list) ? result.list : [];
|
|
178901
|
+
for (const row of list3) {
|
|
178902
|
+
const device = toDevice(obj(row));
|
|
178903
|
+
if (device) devices.push(device);
|
|
178904
|
+
}
|
|
178905
|
+
const total = Number(result.total) || 0;
|
|
178906
|
+
if (list3.length === 0 || pageNo * PAGE_SIZE >= total) return devices;
|
|
178907
|
+
}
|
|
178908
|
+
}
|
|
178909
|
+
async bypassV2(device, method, data = {}, options = {}) {
|
|
178910
|
+
const op = `bypassV2 ${method}`;
|
|
178911
|
+
const sub = options.subDevice ? { subDeviceNo: 0, subDeviceType: "" } : {};
|
|
178912
|
+
const outer = await this.#request(op, V2_PATH, (s) => ({
|
|
178913
|
+
...this.#deviceBody(s, device, "bypassV2"),
|
|
178914
|
+
...sub,
|
|
178915
|
+
payload: { method, source: "APP", data, ...sub }
|
|
178916
|
+
}));
|
|
178917
|
+
if (outer.code !== 0) throw errorFor(outer.code, op, false);
|
|
178918
|
+
return obj(outer.result);
|
|
178919
|
+
}
|
|
178920
|
+
bypassV1(device, endpoint, extra = {}) {
|
|
178921
|
+
return this.#request(endpoint, V1_PATH + endpoint, (s) => ({
|
|
178922
|
+
...this.#deviceBody(s, device, endpoint),
|
|
178923
|
+
uuid: device.uuid,
|
|
178924
|
+
...extra
|
|
178925
|
+
}));
|
|
178926
|
+
}
|
|
178927
|
+
async #signIn() {
|
|
178928
|
+
let region = this.#region;
|
|
178929
|
+
let countryCode = this.#countryCode;
|
|
178930
|
+
for (let attempt = 0; ; attempt++) {
|
|
178931
|
+
const auth = await this.#post(
|
|
178932
|
+
region,
|
|
178933
|
+
AUTH_PATH,
|
|
178934
|
+
"login",
|
|
178935
|
+
this.#authBody(countryCode)
|
|
178936
|
+
);
|
|
178937
|
+
const authorizeCode = str(obj(auth.result).authorizeCode);
|
|
178938
|
+
if (auth.code !== 0 || !authorizeCode) {
|
|
178939
|
+
if (attempt === 0 && CROSS_REGION_CODES.includes(Number(auth.code))) {
|
|
178940
|
+
region = region === "US" ? "EU" : "US";
|
|
178941
|
+
continue;
|
|
178942
|
+
}
|
|
178943
|
+
throw errorFor(auth.code, "login", true);
|
|
178944
|
+
}
|
|
178945
|
+
let res = await this.#post(
|
|
178946
|
+
region,
|
|
178947
|
+
LOGIN_PATH,
|
|
178948
|
+
"login",
|
|
178949
|
+
this.#loginBody(authorizeCode, countryCode)
|
|
178950
|
+
);
|
|
178951
|
+
if (attempt === 0 && CROSS_REGION_CODES.includes(Number(res.code))) {
|
|
178952
|
+
const cross = obj(res.result);
|
|
178953
|
+
countryCode = str(cross.countryCode) ?? countryCode;
|
|
178954
|
+
const bizToken = str(cross.bizToken);
|
|
178955
|
+
if (!bizToken) {
|
|
178956
|
+
region = region === "US" ? "EU" : "US";
|
|
178957
|
+
continue;
|
|
178958
|
+
}
|
|
178959
|
+
region = cross.currentRegion === "US" || cross.currentRegion === "EU" ? cross.currentRegion : US_COUNTRIES.includes(countryCode.toUpperCase()) ? "US" : "EU";
|
|
178960
|
+
res = await this.#post(
|
|
178961
|
+
region,
|
|
178962
|
+
LOGIN_PATH,
|
|
178963
|
+
"login",
|
|
178964
|
+
this.#loginBody(authorizeCode, countryCode, bizToken)
|
|
178965
|
+
);
|
|
178966
|
+
}
|
|
178967
|
+
const result = obj(res.result);
|
|
178968
|
+
const token = str(result.token);
|
|
178969
|
+
if (res.code !== 0 || !token || result.accountID == null) {
|
|
178970
|
+
throw errorFor(res.code, "login", true);
|
|
178971
|
+
}
|
|
178972
|
+
const session = {
|
|
178973
|
+
token,
|
|
178974
|
+
accountId: String(result.accountID),
|
|
178975
|
+
countryCode: str(result.countryCode) ?? countryCode,
|
|
178976
|
+
region
|
|
178977
|
+
};
|
|
178978
|
+
this.#session = session;
|
|
178979
|
+
this.#region = region;
|
|
178980
|
+
this.#countryCode = session.countryCode;
|
|
178981
|
+
this.#onSession?.(session);
|
|
178982
|
+
return session;
|
|
178983
|
+
}
|
|
178984
|
+
}
|
|
178985
|
+
// a rejected token is dropped here and login() runs once more
|
|
178986
|
+
async #request(op, path14, body) {
|
|
178987
|
+
const run = async (session2) => {
|
|
178988
|
+
const res = await this.#post(session2.region, path14, op, body(session2));
|
|
178989
|
+
if (res.code !== 0) throw errorFor(res.code, op, false);
|
|
178990
|
+
return obj(res.result);
|
|
178991
|
+
};
|
|
178992
|
+
const session = await this.login();
|
|
178993
|
+
try {
|
|
178994
|
+
return await run(session);
|
|
178995
|
+
} catch (err) {
|
|
178996
|
+
if (!(err instanceof VeSyncError && err.kind === "token")) throw err;
|
|
178997
|
+
}
|
|
178998
|
+
if (this.#session === session) {
|
|
178999
|
+
this.#session = void 0;
|
|
179000
|
+
this.#onSession?.(void 0);
|
|
179001
|
+
}
|
|
179002
|
+
return run(await this.login());
|
|
179003
|
+
}
|
|
179004
|
+
async #post(region, path14, op, body) {
|
|
179005
|
+
let res;
|
|
179006
|
+
try {
|
|
179007
|
+
res = await (this.#fetch ?? fetch)(HOSTS[region] + path14, {
|
|
179008
|
+
method: "POST",
|
|
179009
|
+
headers: HEADERS,
|
|
179010
|
+
body: JSON.stringify(body),
|
|
179011
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
179012
|
+
});
|
|
179013
|
+
} catch (err) {
|
|
179014
|
+
throw networkError(op, err instanceof Error ? err.message : String(err));
|
|
179015
|
+
}
|
|
179016
|
+
if (TOKEN_STATUSES.includes(res.status)) {
|
|
179017
|
+
throw new VeSyncError(
|
|
179018
|
+
"token",
|
|
179019
|
+
`VeSync ${op}: ${DESCRIPTIONS.token} (HTTP ${res.status})`
|
|
179020
|
+
);
|
|
179021
|
+
}
|
|
179022
|
+
if (res.status >= 400) throw networkError(op, `HTTP ${res.status}`);
|
|
179023
|
+
const json = await res.json().catch(() => void 0);
|
|
179024
|
+
if (typeof json !== "object" || json === null) {
|
|
179025
|
+
throw networkError(op, "invalid response");
|
|
179026
|
+
}
|
|
179027
|
+
return json;
|
|
179028
|
+
}
|
|
179029
|
+
#deviceBody(session, device, method) {
|
|
179030
|
+
return {
|
|
179031
|
+
acceptLanguage: LANGUAGE,
|
|
179032
|
+
accountID: session.accountId,
|
|
179033
|
+
appVersion: APP_VERSION,
|
|
179034
|
+
cid: device.cid,
|
|
179035
|
+
configModule: device.configModule,
|
|
179036
|
+
debugMode: false,
|
|
179037
|
+
method,
|
|
179038
|
+
phoneBrand: PHONE_BRAND,
|
|
179039
|
+
phoneOS: PHONE_OS,
|
|
179040
|
+
traceId: this.#traceId(),
|
|
179041
|
+
timeZone: TIME_ZONE,
|
|
179042
|
+
token: session.token,
|
|
179043
|
+
userCountryCode: session.countryCode,
|
|
179044
|
+
deviceId: device.cid,
|
|
179045
|
+
configModel: device.configModule
|
|
179046
|
+
};
|
|
179047
|
+
}
|
|
179048
|
+
#authBody(countryCode) {
|
|
179049
|
+
return {
|
|
179050
|
+
email: this.#email,
|
|
179051
|
+
method: "authByPWDOrOTM",
|
|
179052
|
+
password: this.#passwordHash,
|
|
179053
|
+
acceptLanguage: LANGUAGE,
|
|
179054
|
+
accountID: "",
|
|
179055
|
+
authProtocolType: "generic",
|
|
179056
|
+
clientInfo: PHONE_BRAND,
|
|
179057
|
+
clientType: CLIENT_TYPE,
|
|
179058
|
+
clientVersion: CLIENT_VERSION,
|
|
179059
|
+
debugMode: false,
|
|
179060
|
+
osInfo: PHONE_OS,
|
|
179061
|
+
terminalId: this.#terminalId,
|
|
179062
|
+
timeZone: TIME_ZONE,
|
|
179063
|
+
token: "",
|
|
179064
|
+
userCountryCode: countryCode,
|
|
179065
|
+
appID: APP_ID,
|
|
179066
|
+
sourceAppID: APP_ID,
|
|
179067
|
+
traceId: this.#traceId()
|
|
179068
|
+
};
|
|
179069
|
+
}
|
|
179070
|
+
#loginBody(authorizeCode, countryCode, bizToken) {
|
|
179071
|
+
return {
|
|
179072
|
+
method: "loginByAuthorizeCode4Vesync",
|
|
179073
|
+
authorizeCode,
|
|
179074
|
+
acceptLanguage: LANGUAGE,
|
|
179075
|
+
accountID: "",
|
|
179076
|
+
clientInfo: PHONE_BRAND,
|
|
179077
|
+
clientType: CLIENT_TYPE,
|
|
179078
|
+
clientVersion: CLIENT_VERSION,
|
|
179079
|
+
debugMode: false,
|
|
179080
|
+
emailSubscriptions: false,
|
|
179081
|
+
osInfo: PHONE_OS,
|
|
179082
|
+
terminalId: this.#terminalId,
|
|
179083
|
+
timeZone: TIME_ZONE,
|
|
179084
|
+
token: "",
|
|
179085
|
+
...bizToken ? { bizToken, regionChange: "lastRegion" } : {},
|
|
179086
|
+
userCountryCode: countryCode,
|
|
179087
|
+
traceId: this.#traceId()
|
|
179088
|
+
};
|
|
179089
|
+
}
|
|
179090
|
+
#traceId() {
|
|
179091
|
+
this.#calls++;
|
|
179092
|
+
const seconds = Math.floor(Date.now() / 1e3);
|
|
179093
|
+
return `APP${this.#terminalId.slice(-5, -1)}${seconds}-${String(this.#calls).padStart(5, "0")}`;
|
|
179094
|
+
}
|
|
179095
|
+
};
|
|
179096
|
+
|
|
179097
|
+
// src/plugins/builtin/vesync/vesync-devices.ts
|
|
179098
|
+
var plugUnit = () => ({
|
|
179099
|
+
key: "",
|
|
179100
|
+
label: "",
|
|
179101
|
+
deviceType: "on_off_plugin_unit"
|
|
179102
|
+
});
|
|
179103
|
+
var onOff = (on) => [
|
|
179104
|
+
{ clusterId: "onOff", attributes: { onOff: on } }
|
|
179105
|
+
];
|
|
179106
|
+
var switchData = (camel, on) => camel ? { powerSwitch: on ? 1 : 0, switchIdx: 0 } : { enabled: on, id: 0 };
|
|
179107
|
+
function onOffWrite(set, canStart = true) {
|
|
179108
|
+
return async (client, device, unitKey, clusterId3, attribute, value) => {
|
|
179109
|
+
if (clusterId3 !== "onOff" || attribute !== "onOff") return true;
|
|
179110
|
+
if (typeof value !== "boolean") return true;
|
|
179111
|
+
if (value && !canStart) return false;
|
|
179112
|
+
await set(client, device, value, unitKey);
|
|
179113
|
+
return true;
|
|
179114
|
+
};
|
|
179115
|
+
}
|
|
179116
|
+
function plug(id, label, read, set) {
|
|
179117
|
+
return {
|
|
179118
|
+
id,
|
|
179119
|
+
label,
|
|
179120
|
+
units: () => [plugUnit()],
|
|
179121
|
+
initial: () => ({ "": onOff(false) }),
|
|
179122
|
+
read: async (client, device) => ({ "": onOff(await read(client, device)) }),
|
|
179123
|
+
write: onOffWrite(set)
|
|
179124
|
+
};
|
|
179125
|
+
}
|
|
179126
|
+
var setSwitchEnabled = (client, device, on) => client.bypassV2(device, "setSwitch", switchData(false, on));
|
|
179127
|
+
var whogPlug = plug(
|
|
179128
|
+
"outlet-v2",
|
|
179129
|
+
"Smart plug",
|
|
179130
|
+
async (client, device) => Boolean((await client.bypassV2(device, "getOutletStatus")).enabled),
|
|
179131
|
+
setSwitchEnabled
|
|
179132
|
+
);
|
|
179133
|
+
var esw10Plug = plug(
|
|
179134
|
+
"outlet-v2",
|
|
179135
|
+
"Smart plug",
|
|
179136
|
+
async (client, device) => Boolean((await client.bypassV2(device, "getSwitch", { id: 0 })).enabled),
|
|
179137
|
+
setSwitchEnabled
|
|
179138
|
+
);
|
|
179139
|
+
var BSDOG_PROPERTIES = [
|
|
179140
|
+
"powerSwitch_1",
|
|
179141
|
+
"realTimeVoltage",
|
|
179142
|
+
"realTimePower",
|
|
179143
|
+
"electricalEnergy",
|
|
179144
|
+
"protectionStatus",
|
|
179145
|
+
"voltageUpperThreshold",
|
|
179146
|
+
"currentUpperThreshold",
|
|
179147
|
+
"scheduleNum"
|
|
179148
|
+
];
|
|
179149
|
+
var propertyPlug = plug(
|
|
179150
|
+
"outlet-prop",
|
|
179151
|
+
"Smart plug",
|
|
179152
|
+
async (client, device) => {
|
|
179153
|
+
const r = await client.bypassV2(device, "getProperty", {
|
|
179154
|
+
properties: BSDOG_PROPERTIES
|
|
179155
|
+
});
|
|
179156
|
+
return Boolean(r.powerSwitch_1);
|
|
179157
|
+
},
|
|
179158
|
+
(client, device, on) => client.bypassV2(
|
|
179159
|
+
device,
|
|
179160
|
+
"setProperty",
|
|
179161
|
+
{ powerSwitch_1: on ? 1 : 0 },
|
|
179162
|
+
{ subDevice: true }
|
|
179163
|
+
)
|
|
179164
|
+
);
|
|
179165
|
+
var readV1 = async (client, device) => {
|
|
179166
|
+
const r = await client.bypassV1(device, "deviceDetail");
|
|
179167
|
+
if (r.connectionStatus !== void 0 && r.connectionStatus !== "online") {
|
|
179168
|
+
throw new VeSyncError("offline", "VeSync deviceDetail: device offline");
|
|
179169
|
+
}
|
|
179170
|
+
return r.deviceStatus === "on";
|
|
179171
|
+
};
|
|
179172
|
+
var v1Outlet = plug(
|
|
179173
|
+
"outlet-v1",
|
|
179174
|
+
"Smart plug",
|
|
179175
|
+
readV1,
|
|
179176
|
+
(client, device, on) => client.bypassV1(device, "deviceStatus", { status: on ? "on" : "off" })
|
|
179177
|
+
);
|
|
179178
|
+
var wallSwitch = plug(
|
|
179179
|
+
"outlet-v1",
|
|
179180
|
+
"Wall switch",
|
|
179181
|
+
readV1,
|
|
179182
|
+
(client, device, on) => client.bypassV1(device, "deviceStatus", {
|
|
179183
|
+
status: on ? "on" : "off",
|
|
179184
|
+
switchNo: 0
|
|
179185
|
+
})
|
|
179186
|
+
);
|
|
179187
|
+
var cooking = (status3) => typeof status3 === "string" && !["standby", "cookend"].includes(status3.toLowerCase());
|
|
179188
|
+
var dualStatus = /* @__PURE__ */ new Map();
|
|
179189
|
+
var fryerDual = {
|
|
179190
|
+
id: "fryer-dual",
|
|
179191
|
+
label: "Air fryer (two baskets)",
|
|
179192
|
+
units: () => [
|
|
179193
|
+
{ key: "left", label: "Left", deviceType: "on_off_plugin_unit" },
|
|
179194
|
+
{ key: "right", label: "Right", deviceType: "on_off_plugin_unit" }
|
|
179195
|
+
],
|
|
179196
|
+
initial: () => ({ left: onOff(false), right: onOff(false) }),
|
|
179197
|
+
async read(client, device) {
|
|
179198
|
+
const r = await client.bypassV2(
|
|
179199
|
+
device,
|
|
179200
|
+
"getAirfryerMultiStatus",
|
|
179201
|
+
{},
|
|
179202
|
+
{ subDevice: true }
|
|
179203
|
+
);
|
|
179204
|
+
dualStatus.set(device.cid, r);
|
|
179205
|
+
const list3 = Array.isArray(r.statusList) ? r.statusList : [];
|
|
179206
|
+
const on = (chamber) => list3.some(
|
|
179207
|
+
(item) => (item.chamber === chamber || item.chamber === 3) && cooking(item.cookStatus)
|
|
179208
|
+
);
|
|
179209
|
+
return { left: onOff(on(1)), right: onOff(on(2)) };
|
|
179210
|
+
},
|
|
179211
|
+
write: onOffWrite((client, device, _on, unitKey) => {
|
|
179212
|
+
const last = dualStatus.get(device.cid);
|
|
179213
|
+
let chamber = unitKey === "right" ? 2 : 1;
|
|
179214
|
+
if (last?.workChamber === 3) chamber = 3;
|
|
179215
|
+
if (last?.syncType === 2) chamber = 4;
|
|
179216
|
+
return client.bypassV2(device, "endCook", { chamber }, { subDevice: true });
|
|
179217
|
+
}, false)
|
|
179218
|
+
};
|
|
179219
|
+
var fryerSingle = {
|
|
179220
|
+
id: "fryer-single",
|
|
179221
|
+
label: "Air fryer",
|
|
179222
|
+
units: () => [plugUnit()],
|
|
179223
|
+
initial: () => ({ "": onOff(false) }),
|
|
179224
|
+
async read(client, device) {
|
|
179225
|
+
const r = await client.bypassV2(
|
|
179226
|
+
device,
|
|
179227
|
+
"getAirfryerStatus",
|
|
179228
|
+
{},
|
|
179229
|
+
{ subDevice: true }
|
|
179230
|
+
);
|
|
179231
|
+
return { "": onOff(cooking(r.cookStatus)) };
|
|
179232
|
+
},
|
|
179233
|
+
write: onOffWrite(
|
|
179234
|
+
(client, device) => client.bypassV2(device, "endCook", {}, { subDevice: true }),
|
|
179235
|
+
false
|
|
179236
|
+
)
|
|
179237
|
+
};
|
|
179238
|
+
function fanState(on, level, levels) {
|
|
179239
|
+
const percent3 = on ? Math.floor(level * 100 / levels) : 0;
|
|
179240
|
+
const fanMode = on ? Math.max(1, Math.ceil(level * 3 / levels)) : 0;
|
|
179241
|
+
return [
|
|
179242
|
+
{
|
|
179243
|
+
clusterId: "fanControl",
|
|
179244
|
+
attributes: { fanMode, percentSetting: percent3, percentCurrent: percent3 }
|
|
179245
|
+
}
|
|
179246
|
+
];
|
|
179247
|
+
}
|
|
179248
|
+
function fanFamily(id, deviceType, status3, camel, levels) {
|
|
179249
|
+
const powered = /* @__PURE__ */ new Map();
|
|
179250
|
+
return {
|
|
179251
|
+
id,
|
|
179252
|
+
label: deviceType === "fan" ? "Fan" : "Air purifier",
|
|
179253
|
+
units: () => [{ key: "", label: "", deviceType }],
|
|
179254
|
+
initial: () => ({ "": fanState(false, 0, levels) }),
|
|
179255
|
+
async read(client, device) {
|
|
179256
|
+
const r = await client.bypassV2(device, status3);
|
|
179257
|
+
const on = Boolean(camel ? r.powerSwitch : r.enabled);
|
|
179258
|
+
powered.set(device.cid, on);
|
|
179259
|
+
const speed = (value) => {
|
|
179260
|
+
const n = Number(value) || 0;
|
|
179261
|
+
return n === 255 ? 0 : Math.min(n, levels);
|
|
179262
|
+
};
|
|
179263
|
+
const level = speed(camel ? r.fanSpeedLevel : r.level) || speed(r.manualSpeedLevel) || 1;
|
|
179264
|
+
return { "": fanState(on, level, levels) };
|
|
179265
|
+
},
|
|
179266
|
+
async write(client, device, _unitKey, clusterId3, attribute, value) {
|
|
179267
|
+
if (clusterId3 !== "fanControl" || typeof value !== "number") return true;
|
|
179268
|
+
const turn = async (on) => {
|
|
179269
|
+
await client.bypassV2(device, "setSwitch", switchData(camel, on));
|
|
179270
|
+
powered.set(device.cid, on);
|
|
179271
|
+
};
|
|
179272
|
+
const ensureOn = async () => {
|
|
179273
|
+
if (powered.get(device.cid) !== true) await turn(true);
|
|
179274
|
+
};
|
|
179275
|
+
const setLevel = async (level) => {
|
|
179276
|
+
await ensureOn();
|
|
179277
|
+
await client.bypassV2(
|
|
179278
|
+
device,
|
|
179279
|
+
"setLevel",
|
|
179280
|
+
camel ? { levelIdx: 0, manualSpeedLevel: level, levelType: "wind" } : { id: 0, level, type: "wind" }
|
|
179281
|
+
);
|
|
179282
|
+
};
|
|
179283
|
+
if (attribute === "percentSetting") {
|
|
179284
|
+
if (value <= 0) await turn(false);
|
|
179285
|
+
else await setLevel(Math.ceil(value * levels / 100));
|
|
179286
|
+
} else if (attribute === "fanMode") {
|
|
179287
|
+
if (value === 0) await turn(false);
|
|
179288
|
+
else if (value === 1 || value === 2 || value === 3)
|
|
179289
|
+
await setLevel(Math.floor(value * levels / 3));
|
|
179290
|
+
else if (value === 4) await ensureOn();
|
|
179291
|
+
}
|
|
179292
|
+
return true;
|
|
179293
|
+
}
|
|
179294
|
+
};
|
|
179295
|
+
}
|
|
179296
|
+
function humidifier(camel) {
|
|
179297
|
+
const state = (on, humidity) => ({
|
|
179298
|
+
"": onOff(on),
|
|
179299
|
+
humidity: [
|
|
179300
|
+
{
|
|
179301
|
+
clusterId: "relativeHumidityMeasurement",
|
|
179302
|
+
attributes: {
|
|
179303
|
+
// relativeHumidityMeasurement is hundredths of a percent
|
|
179304
|
+
measuredValue: typeof humidity === "number" && humidity >= 0 && humidity <= 100 ? Math.round(humidity * 100) : null
|
|
179305
|
+
}
|
|
179306
|
+
}
|
|
179307
|
+
]
|
|
179308
|
+
});
|
|
179309
|
+
return {
|
|
179310
|
+
id: "humidifier",
|
|
179311
|
+
label: "Humidifier",
|
|
179312
|
+
units: () => [
|
|
179313
|
+
plugUnit(),
|
|
179314
|
+
{ key: "humidity", label: "Humidity", deviceType: "humidity_sensor" }
|
|
179315
|
+
],
|
|
179316
|
+
initial: () => state(false, null),
|
|
179317
|
+
async read(client, device) {
|
|
179318
|
+
const r = await client.bypassV2(device, "getHumidifierStatus");
|
|
179319
|
+
return state(Boolean(camel ? r.powerSwitch : r.enabled), r.humidity);
|
|
179320
|
+
},
|
|
179321
|
+
write: onOffWrite(
|
|
179322
|
+
(client, device, on) => client.bypassV2(device, "setSwitch", switchData(camel, on))
|
|
179323
|
+
)
|
|
179324
|
+
};
|
|
179325
|
+
}
|
|
179326
|
+
var PURIFIER = "getPurifierStatus";
|
|
179327
|
+
var core3 = fanFamily("purifier-core", "air_purifier", PURIFIER, false, 3);
|
|
179328
|
+
var core4 = fanFamily("purifier-core", "air_purifier", PURIFIER, false, 4);
|
|
179329
|
+
var snakeHumidifier = humidifier(false);
|
|
179330
|
+
var camelHumidifier = humidifier(true);
|
|
179331
|
+
var MODELS = [
|
|
179332
|
+
[["ESW10-USA", "ESW10-EU"], esw10Plug],
|
|
179333
|
+
[["ESW01-EU", "ESW01-USA", "ESW03-USA", "ESW03-EU", "ESW15-USA"], v1Outlet],
|
|
179334
|
+
[["WHOGPLUG"], whogPlug],
|
|
179335
|
+
[
|
|
179336
|
+
[
|
|
179337
|
+
"BSDOG01",
|
|
179338
|
+
"BSDOG02",
|
|
179339
|
+
"WYSMTOD16A",
|
|
179340
|
+
"WM-PLUG",
|
|
179341
|
+
"JXUK13APLUG",
|
|
179342
|
+
"WYZYOGMINIPLUG",
|
|
179343
|
+
"HWPLUG16A",
|
|
179344
|
+
"FY-PLUG",
|
|
179345
|
+
"HWPLUG16",
|
|
179346
|
+
"WYLDR16A1081"
|
|
179347
|
+
],
|
|
179348
|
+
propertyPlug
|
|
179349
|
+
],
|
|
179350
|
+
[["ESWL01", "ESWL03"], wallSwitch],
|
|
179351
|
+
[
|
|
179352
|
+
["LTF-F422S-KEU", "LTF-F422S-WUSR", "LTF-F422S-WJP", "LTF-F422S-WUS"],
|
|
179353
|
+
fanFamily("fan", "fan", "getTowerFanStatus", true, 12)
|
|
179354
|
+
],
|
|
179355
|
+
[
|
|
179356
|
+
["LPF-R432S-AEU", "LPF-R432S-AUS"],
|
|
179357
|
+
fanFamily("fan", "fan", "getFanStatus", true, 12)
|
|
179358
|
+
],
|
|
179359
|
+
[
|
|
179360
|
+
["Core200S", "LAP-C201S-AUSR", "LAP-C202S-WUSR"],
|
|
179361
|
+
fanFamily("purifier-core", "air_purifier", PURIFIER, false, 3)
|
|
179362
|
+
],
|
|
179363
|
+
[
|
|
179364
|
+
[
|
|
179365
|
+
"Core300S",
|
|
179366
|
+
"LAP-C301S-WJP",
|
|
179367
|
+
"LAP-C302S-WUSB",
|
|
179368
|
+
"LAP-C301S-WAAA",
|
|
179369
|
+
"LAP-C302S-WGC"
|
|
179370
|
+
],
|
|
179371
|
+
core3
|
|
179372
|
+
],
|
|
179373
|
+
[["Core400S", "LAP-C401S-WJP", "LAP-C401S-WUSR", "LAP-C401S-WAAA"], core4],
|
|
179374
|
+
[["Core600S", "LAP-C601S-WUS", "LAP-C601S-WUSR", "LAP-C601S-WEU"], core4],
|
|
179375
|
+
[["LV-RH131S-WM", "LV-RH131S"], core3],
|
|
179376
|
+
[
|
|
179377
|
+
[
|
|
179378
|
+
"LAP-V102S-AASR",
|
|
179379
|
+
"LAP-V102S-WUS",
|
|
179380
|
+
"LAP-V102S-WEU",
|
|
179381
|
+
"LAP-V102S-AUSR",
|
|
179382
|
+
"LAP-V102S-WJP",
|
|
179383
|
+
"LAP-V102S-AJPR",
|
|
179384
|
+
"LAP-V102S-AEUR",
|
|
179385
|
+
"LAP-V201S-AASR",
|
|
179386
|
+
"LAP-V201S-WJP",
|
|
179387
|
+
"LAP-V201S-WEU",
|
|
179388
|
+
"LAP-V201S-WUS",
|
|
179389
|
+
"LAP-V201-AUSR",
|
|
179390
|
+
"LAP-V201S-AUSR",
|
|
179391
|
+
"LAP-V201S-AEUR"
|
|
179392
|
+
],
|
|
179393
|
+
fanFamily("purifier-v2", "air_purifier", PURIFIER, true, 4)
|
|
179394
|
+
],
|
|
179395
|
+
[
|
|
179396
|
+
[
|
|
179397
|
+
"LAP-EL551S-AUS",
|
|
179398
|
+
"LAP-EL551S-AEUR",
|
|
179399
|
+
"LAP-EL551S-WEU",
|
|
179400
|
+
"LAP-EL551S-WUS",
|
|
179401
|
+
"LAP-B851S-WEU",
|
|
179402
|
+
"LAP-B851S-WNA",
|
|
179403
|
+
"LAP-B851S-AEUR",
|
|
179404
|
+
"LAP-B851S-AUS",
|
|
179405
|
+
"LAP-B851S-WUS",
|
|
179406
|
+
"LAP-BAY-MAX01S"
|
|
179407
|
+
],
|
|
179408
|
+
fanFamily("purifier-v2", "air_purifier", PURIFIER, true, 3)
|
|
179409
|
+
],
|
|
179410
|
+
[
|
|
179411
|
+
[
|
|
179412
|
+
"Classic300S",
|
|
179413
|
+
"LUH-A601S-WUSB",
|
|
179414
|
+
"LUH-A601S-AUSW",
|
|
179415
|
+
"Classic200S",
|
|
179416
|
+
"Dual200S",
|
|
179417
|
+
"LUH-D301S-WUSR",
|
|
179418
|
+
"LUH-D301S-WJP",
|
|
179419
|
+
"LUH-D301S-WEU",
|
|
179420
|
+
"LUH-D301S-KEUR",
|
|
179421
|
+
"LUH-A602S-WUSR",
|
|
179422
|
+
"LUH-A602S-WUS",
|
|
179423
|
+
"LUH-A602S-WEUR",
|
|
179424
|
+
"LUH-A602S-WEU",
|
|
179425
|
+
"LUH-A602S-WJP",
|
|
179426
|
+
"LUH-A602S-WUSC"
|
|
179427
|
+
],
|
|
179428
|
+
snakeHumidifier
|
|
179429
|
+
],
|
|
179430
|
+
[["LUH-A603S-WUS"], camelHumidifier],
|
|
179431
|
+
[
|
|
179432
|
+
[
|
|
179433
|
+
"LUH-O451S-WEU",
|
|
179434
|
+
"LUH-O451S-WUS",
|
|
179435
|
+
"LUH-O451S-WUSR",
|
|
179436
|
+
"LUH-O601S-WUS",
|
|
179437
|
+
"LUH-O601S-KUS"
|
|
179438
|
+
],
|
|
179439
|
+
snakeHumidifier
|
|
179440
|
+
],
|
|
179441
|
+
[
|
|
179442
|
+
[
|
|
179443
|
+
"LUH-M101S-WUS",
|
|
179444
|
+
"LUH-M101S-WUSR",
|
|
179445
|
+
"LUH-M101S-WEUR",
|
|
179446
|
+
"LEH-S601S-WUS",
|
|
179447
|
+
"LEH-S601S-WUSR",
|
|
179448
|
+
"LEH-S601S-WEUR",
|
|
179449
|
+
"LEH-S602S-WUS",
|
|
179450
|
+
"LEH-B381S-WUS",
|
|
179451
|
+
"LEH-B381S-WEU"
|
|
179452
|
+
],
|
|
179453
|
+
camelHumidifier
|
|
179454
|
+
]
|
|
179455
|
+
];
|
|
179456
|
+
var FRYERS = [
|
|
179457
|
+
["CAF-TF10", fryerDual],
|
|
179458
|
+
["CAF-P583S", fryerSingle],
|
|
179459
|
+
["CAF-DC601S", fryerSingle]
|
|
179460
|
+
];
|
|
179461
|
+
function familyFor(deviceType) {
|
|
179462
|
+
const upper = deviceType.toUpperCase();
|
|
179463
|
+
const fryer = FRYERS.find(([prefix]) => upper.startsWith(prefix));
|
|
179464
|
+
if (fryer) return fryer[1];
|
|
179465
|
+
const exact = MODELS.find(([types]) => types.includes(deviceType));
|
|
179466
|
+
if (exact) return exact[1];
|
|
179467
|
+
const parts = deviceType.split("-");
|
|
179468
|
+
if (parts.length < 3) return void 0;
|
|
179469
|
+
const base = parts.slice(0, -1).join("-").toLowerCase();
|
|
179470
|
+
return MODELS.find(
|
|
179471
|
+
([types]) => types.some((t) => t.toLowerCase().includes(base))
|
|
179472
|
+
)?.[1];
|
|
179473
|
+
}
|
|
179474
|
+
|
|
179475
|
+
// src/plugins/builtin/vesync/vesync-plugin.ts
|
|
179476
|
+
var CONFIG_KEY3 = "config";
|
|
179477
|
+
var SESSION_KEY = "session";
|
|
179478
|
+
var TERMINAL_KEY = "terminalId";
|
|
179479
|
+
var DEVICES_KEY = "devices";
|
|
179480
|
+
var ACCOUNT_KEY = "account";
|
|
179481
|
+
var REACHABLE = "bridgedDeviceBasicInformation";
|
|
179482
|
+
var LIST_REFRESH_MS = 15 * 6e4;
|
|
179483
|
+
var RETRY_BASE_MS2 = 1e4;
|
|
179484
|
+
var RETRY_MAX_MS2 = 3e5;
|
|
179485
|
+
var QUOTA_RETRY_MS = 36e5;
|
|
179486
|
+
var EARLY_POLL_MS = 2e3;
|
|
179487
|
+
var ACCOUNT_ERRORS = ["credentials", "mfa", "quota", "token"];
|
|
179488
|
+
var accountLoad = /* @__PURE__ */ new Map();
|
|
179489
|
+
var deviceKey = (device) => (device.subDeviceNo === void 0 ? device.cid : `${device.cid}_${device.subDeviceNo}`).replace(/[^A-Za-z0-9_-]/g, "_");
|
|
179490
|
+
var same = (a, b) => a === b || JSON.stringify(a) === JSON.stringify(b);
|
|
179491
|
+
var errorText = (e) => e instanceof Error ? e.message : String(e);
|
|
179492
|
+
var VeSyncPlugin = class {
|
|
179493
|
+
constructor(deps = {}) {
|
|
179494
|
+
this.deps = deps;
|
|
179495
|
+
}
|
|
179496
|
+
deps;
|
|
179497
|
+
name = "vesync";
|
|
179498
|
+
version = "0.1.0";
|
|
179499
|
+
context;
|
|
179500
|
+
config = {};
|
|
179501
|
+
terminalId = "";
|
|
179502
|
+
client;
|
|
179503
|
+
devices = [];
|
|
179504
|
+
mounted = /* @__PURE__ */ new Map();
|
|
179505
|
+
failures = /* @__PURE__ */ new Map();
|
|
179506
|
+
status = "not configured";
|
|
179507
|
+
// Bumped on start, config save and shutdown; work from an older
|
|
179508
|
+
// generation is dropped after every await.
|
|
179509
|
+
generation = 0;
|
|
179510
|
+
timer;
|
|
179511
|
+
earlyPolls = /* @__PURE__ */ new Map();
|
|
179512
|
+
busy = false;
|
|
179513
|
+
again = false;
|
|
179514
|
+
stopped = false;
|
|
179515
|
+
backoffMs = RETRY_BASE_MS2;
|
|
179516
|
+
listAt;
|
|
179517
|
+
queue = Promise.resolve();
|
|
179518
|
+
async onStart(context) {
|
|
179519
|
+
this.context = context;
|
|
179520
|
+
this.generation++;
|
|
179521
|
+
this.mounted = /* @__PURE__ */ new Map();
|
|
179522
|
+
this.config = await context.storage.get(CONFIG_KEY3) ?? {};
|
|
179523
|
+
await this.forgetOtherAccount(context);
|
|
179524
|
+
const session = await context.storage.get(SESSION_KEY);
|
|
179525
|
+
let terminalId = await context.storage.get(TERMINAL_KEY);
|
|
179526
|
+
if (!terminalId) {
|
|
179527
|
+
terminalId = generateTerminalId();
|
|
179528
|
+
await context.storage.set(TERMINAL_KEY, terminalId);
|
|
179529
|
+
}
|
|
179530
|
+
this.terminalId = terminalId;
|
|
179531
|
+
this.devices = await context.storage.get(DEVICES_KEY) ?? [];
|
|
179532
|
+
this.stopped = false;
|
|
179533
|
+
this.backoffMs = RETRY_BASE_MS2;
|
|
179534
|
+
this.listAt = void 0;
|
|
179535
|
+
if (!this.configured()) {
|
|
179536
|
+
this.status = "not configured";
|
|
179537
|
+
context.log.info("no VeSync account configured, no devices exposed");
|
|
179538
|
+
return;
|
|
179539
|
+
}
|
|
179540
|
+
this.status = "signing in";
|
|
179541
|
+
this.client = this.newClient(session);
|
|
179542
|
+
await this.reconcile();
|
|
179543
|
+
this.schedule(0);
|
|
179544
|
+
}
|
|
179545
|
+
async onConfigChanged(config8) {
|
|
179546
|
+
this.config = config8;
|
|
179547
|
+
const context = this.context;
|
|
179548
|
+
if (!context) return;
|
|
179549
|
+
await context.storage.set(CONFIG_KEY3, config8);
|
|
179550
|
+
this.generation++;
|
|
179551
|
+
this.clearTimers();
|
|
179552
|
+
this.leaveAccountLoad();
|
|
179553
|
+
this.stopped = false;
|
|
179554
|
+
this.backoffMs = RETRY_BASE_MS2;
|
|
179555
|
+
this.listAt = void 0;
|
|
179556
|
+
if (await this.forgetOtherAccount(context)) this.client = void 0;
|
|
179557
|
+
if (this.configured()) {
|
|
179558
|
+
this.client ??= this.newClient();
|
|
179559
|
+
this.status = "signing in";
|
|
179560
|
+
} else {
|
|
179561
|
+
this.client = void 0;
|
|
179562
|
+
this.status = "not configured";
|
|
179563
|
+
}
|
|
179564
|
+
await this.reconcile();
|
|
179565
|
+
if (this.client) this.schedule(0);
|
|
179566
|
+
}
|
|
179567
|
+
async onShutdown() {
|
|
179568
|
+
this.generation++;
|
|
179569
|
+
this.clearTimers();
|
|
179570
|
+
this.leaveAccountLoad();
|
|
179571
|
+
this.again = false;
|
|
179572
|
+
this.client = void 0;
|
|
179573
|
+
await this.reconcile();
|
|
179574
|
+
}
|
|
179575
|
+
getCurrentConfig() {
|
|
179576
|
+
return { ...this.config };
|
|
179577
|
+
}
|
|
179578
|
+
getConfigSchema() {
|
|
179579
|
+
const properties = {
|
|
179580
|
+
email: {
|
|
179581
|
+
type: "string",
|
|
179582
|
+
title: "Email",
|
|
179583
|
+
description: "The account you use in the VeSync app."
|
|
179584
|
+
},
|
|
179585
|
+
password: { type: "string", title: "Password", secret: true },
|
|
179586
|
+
pollInterval: {
|
|
179587
|
+
type: "number",
|
|
179588
|
+
title: "Poll interval (seconds)",
|
|
179589
|
+
description: "30 to 3600. Raised on its own when many devices would use up the daily VeSync request quota, which every bridge and app on the account shares.",
|
|
179590
|
+
default: 60
|
|
179591
|
+
}
|
|
179592
|
+
};
|
|
179593
|
+
const unsupported = [];
|
|
179594
|
+
for (const device of this.devices) {
|
|
179595
|
+
const family = familyFor(device.deviceType);
|
|
179596
|
+
if (!family) {
|
|
179597
|
+
unsupported.push(`${device.name} (${device.deviceType})`);
|
|
179598
|
+
continue;
|
|
179599
|
+
}
|
|
179600
|
+
properties[`expose_${deviceKey(device)}`] = {
|
|
179601
|
+
type: "boolean",
|
|
179602
|
+
title: device.name,
|
|
179603
|
+
description: `${family.label}, ${device.deviceType}`,
|
|
179604
|
+
default: false
|
|
179605
|
+
};
|
|
179606
|
+
}
|
|
179607
|
+
let description = `Status: ${this.status}.`;
|
|
179608
|
+
if (unsupported.length > 0) {
|
|
179609
|
+
description += ` Not supported yet: ${unsupported.join(", ")}.`;
|
|
179610
|
+
}
|
|
179611
|
+
return { title: "VeSync", description, properties };
|
|
179612
|
+
}
|
|
179613
|
+
configured() {
|
|
179614
|
+
const { email, password } = this.config;
|
|
179615
|
+
return typeof email === "string" && email.trim() !== "" && typeof password === "string" && password !== "";
|
|
179616
|
+
}
|
|
179617
|
+
// leftover session and list from another account would be reused
|
|
179618
|
+
async forgetOtherAccount(context) {
|
|
179619
|
+
const { email, password } = this.config;
|
|
179620
|
+
const account = createHash3("sha256").update(`${String(email ?? "").trim()}\0${String(password ?? "")}`).digest("hex");
|
|
179621
|
+
if (await context.storage.get(ACCOUNT_KEY) === account) {
|
|
179622
|
+
return false;
|
|
179623
|
+
}
|
|
179624
|
+
this.devices = [];
|
|
179625
|
+
await context.storage.delete(SESSION_KEY);
|
|
179626
|
+
await context.storage.set(DEVICES_KEY, []);
|
|
179627
|
+
await context.storage.set(ACCOUNT_KEY, account);
|
|
179628
|
+
return true;
|
|
179629
|
+
}
|
|
179630
|
+
leaveAccountLoad() {
|
|
179631
|
+
for (const [account, load2] of accountLoad) {
|
|
179632
|
+
load2.delete(this);
|
|
179633
|
+
if (load2.size === 0) accountLoad.delete(account);
|
|
179634
|
+
}
|
|
179635
|
+
}
|
|
179636
|
+
newClient(session) {
|
|
179637
|
+
const client = new VeSyncClient({
|
|
179638
|
+
email: String(this.config.email).trim(),
|
|
179639
|
+
password: String(this.config.password),
|
|
179640
|
+
terminalId: this.terminalId,
|
|
179641
|
+
session,
|
|
179642
|
+
fetch: this.deps.fetch,
|
|
179643
|
+
onSession: (next) => {
|
|
179644
|
+
if (this.client !== client) return;
|
|
179645
|
+
const storage2 = this.context?.storage;
|
|
179646
|
+
(next ? storage2?.set(SESSION_KEY, next) : storage2?.delete(SESSION_KEY))?.catch(() => this.warn("could not store the VeSync session"));
|
|
179647
|
+
}
|
|
179648
|
+
});
|
|
179649
|
+
return client;
|
|
179650
|
+
}
|
|
179651
|
+
pollSeconds() {
|
|
179652
|
+
const seconds = Number(this.config.pollInterval);
|
|
179653
|
+
return Number.isFinite(seconds) ? Math.min(3600, Math.max(30, seconds)) : 60;
|
|
179654
|
+
}
|
|
179655
|
+
warn(message) {
|
|
179656
|
+
this.context?.log.warn(message);
|
|
179657
|
+
}
|
|
179658
|
+
// Registrations run one at a time, a shutdown queues behind a running one.
|
|
179659
|
+
reconcile() {
|
|
179660
|
+
const run = this.queue.then(() => this.applyExposed());
|
|
179661
|
+
this.queue = run.catch(() => {
|
|
179662
|
+
});
|
|
179663
|
+
return run;
|
|
179664
|
+
}
|
|
179665
|
+
async applyExposed() {
|
|
179666
|
+
const context = this.context;
|
|
179667
|
+
if (!context) return;
|
|
179668
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
179669
|
+
for (const device of this.client ? this.devices : []) {
|
|
179670
|
+
const family = familyFor(device.deviceType);
|
|
179671
|
+
const key = deviceKey(device);
|
|
179672
|
+
if (!family || this.config[`expose_${key}`] !== true) continue;
|
|
179673
|
+
for (const unit of family.units(device)) {
|
|
179674
|
+
const id = unit.key ? `vesync_${key}_${unit.key}` : `vesync_${key}`;
|
|
179675
|
+
wanted.set(id, { id, key, device, family, unit });
|
|
179676
|
+
}
|
|
179677
|
+
}
|
|
179678
|
+
for (const id of [...this.mounted.keys()]) {
|
|
179679
|
+
if (wanted.has(id)) continue;
|
|
179680
|
+
this.mounted.delete(id);
|
|
179681
|
+
await context.unregisterDevice(id).catch(() => {
|
|
179682
|
+
});
|
|
179683
|
+
}
|
|
179684
|
+
for (const [id, want] of wanted) {
|
|
179685
|
+
const have = this.mounted.get(id);
|
|
179686
|
+
if (have) {
|
|
179687
|
+
have.device = want.device;
|
|
179688
|
+
continue;
|
|
179689
|
+
}
|
|
179690
|
+
const clusters = want.family.initial(want.device)[want.unit.key] ?? [];
|
|
179691
|
+
const initial = clusters.map((c) => [
|
|
179692
|
+
c.clusterId,
|
|
179693
|
+
{ ...c.attributes }
|
|
179694
|
+
]);
|
|
179695
|
+
const mounted = {
|
|
179696
|
+
...want,
|
|
179697
|
+
pushed: new Map([[REACHABLE, { reachable: true }], ...initial]),
|
|
179698
|
+
real: new Map(initial),
|
|
179699
|
+
writes: 0
|
|
179700
|
+
};
|
|
179701
|
+
this.mounted.set(id, mounted);
|
|
179702
|
+
await context.registerDevice({
|
|
179703
|
+
id,
|
|
179704
|
+
name: want.unit.label ? `${want.device.name} ${want.unit.label}` : want.device.name,
|
|
179705
|
+
deviceType: want.unit.deviceType,
|
|
179706
|
+
// fanModeSequence is mandatory without a default, the endpoint does
|
|
179707
|
+
// not mount without it
|
|
179708
|
+
clusters: clusters.map(
|
|
179709
|
+
(c) => c.clusterId === "fanControl" ? { ...c, attributes: { fanModeSequence: 0, ...c.attributes } } : c
|
|
179710
|
+
),
|
|
179711
|
+
onAttributeWrite: (clusterId3, attribute, value) => this.onWrite(id, clusterId3, attribute, value)
|
|
179712
|
+
});
|
|
179713
|
+
}
|
|
179714
|
+
for (const mounted of this.mounted.values()) {
|
|
179715
|
+
this.push(mounted, REACHABLE, { reachable: mounted.device.online });
|
|
179716
|
+
}
|
|
179717
|
+
}
|
|
179718
|
+
push(mounted, clusterId3, attributes9) {
|
|
179719
|
+
const last = mounted.pushed.get(clusterId3);
|
|
179720
|
+
if (last && Object.entries(attributes9).every(([k, v]) => same(last[k], v)))
|
|
179721
|
+
return;
|
|
179722
|
+
mounted.pushed.set(clusterId3, { ...last, ...attributes9 });
|
|
179723
|
+
this.context?.updateDeviceState(mounted.id, clusterId3, attributes9);
|
|
179724
|
+
}
|
|
179725
|
+
// Runs after matter.js committed the change, so a refused or failed write
|
|
179726
|
+
// can only be undone by pushing the real state back.
|
|
179727
|
+
async onWrite(id, clusterId3, attribute, value) {
|
|
179728
|
+
const mounted = this.mounted.get(id);
|
|
179729
|
+
const last = mounted?.pushed.get(clusterId3);
|
|
179730
|
+
if (!mounted || !last || !(attribute in last)) return;
|
|
179731
|
+
if (same(last[attribute], value)) return;
|
|
179732
|
+
mounted.pushed.set(clusterId3, { ...last, [attribute]: value });
|
|
179733
|
+
mounted.writes++;
|
|
179734
|
+
const client = this.client;
|
|
179735
|
+
let ok2 = false;
|
|
179736
|
+
if (client && !this.stopped) {
|
|
179737
|
+
try {
|
|
179738
|
+
ok2 = await mounted.family.write(
|
|
179739
|
+
client,
|
|
179740
|
+
mounted.device,
|
|
179741
|
+
mounted.unit.key,
|
|
179742
|
+
clusterId3,
|
|
179743
|
+
attribute,
|
|
179744
|
+
value
|
|
179745
|
+
);
|
|
179746
|
+
} catch (e) {
|
|
179747
|
+
this.warn(`${mounted.device.name}: write failed: ${errorText(e)}`);
|
|
179748
|
+
}
|
|
179749
|
+
}
|
|
179750
|
+
if (this.mounted.get(id) !== mounted) return;
|
|
179751
|
+
if (this.client && !this.stopped) this.pollSoon(mounted.key);
|
|
179752
|
+
const real = mounted.real.get(clusterId3);
|
|
179753
|
+
if (ok2) {
|
|
179754
|
+
mounted.real.set(clusterId3, { ...real, [attribute]: value });
|
|
179755
|
+
return;
|
|
179756
|
+
}
|
|
179757
|
+
const now = mounted.pushed.get(clusterId3);
|
|
179758
|
+
if (!now || !same(now[attribute], value)) return;
|
|
179759
|
+
if (real) this.push(mounted, clusterId3, real);
|
|
179760
|
+
}
|
|
179761
|
+
pollSoon(key) {
|
|
179762
|
+
clearTimeout(this.earlyPolls.get(key));
|
|
179763
|
+
const generation = this.generation;
|
|
179764
|
+
const timer = setTimeout(() => {
|
|
179765
|
+
this.earlyPolls.delete(key);
|
|
179766
|
+
this.poll(key, generation).catch(
|
|
179767
|
+
(e) => this.warn(`status read failed: ${errorText(e)}`)
|
|
179768
|
+
);
|
|
179769
|
+
}, EARLY_POLL_MS);
|
|
179770
|
+
timer.unref?.();
|
|
179771
|
+
this.earlyPolls.set(key, timer);
|
|
179772
|
+
}
|
|
179773
|
+
schedule(delayMs) {
|
|
179774
|
+
clearTimeout(this.timer);
|
|
179775
|
+
this.timer = setTimeout(() => {
|
|
179776
|
+
this.timer = void 0;
|
|
179777
|
+
void this.cycle();
|
|
179778
|
+
}, delayMs);
|
|
179779
|
+
this.timer.unref?.();
|
|
179780
|
+
}
|
|
179781
|
+
clearTimers() {
|
|
179782
|
+
clearTimeout(this.timer);
|
|
179783
|
+
this.timer = void 0;
|
|
179784
|
+
for (const timer of this.earlyPolls.values()) clearTimeout(timer);
|
|
179785
|
+
this.earlyPolls.clear();
|
|
179786
|
+
}
|
|
179787
|
+
// Single flight: a kick during a running cycle runs once it is done.
|
|
179788
|
+
async cycle() {
|
|
179789
|
+
if (this.busy) {
|
|
179790
|
+
this.again = true;
|
|
179791
|
+
return;
|
|
179792
|
+
}
|
|
179793
|
+
this.busy = true;
|
|
179794
|
+
this.again = false;
|
|
179795
|
+
const generation = this.generation;
|
|
179796
|
+
let next;
|
|
179797
|
+
try {
|
|
179798
|
+
next = await this.step(generation);
|
|
179799
|
+
} catch (e) {
|
|
179800
|
+
if (generation === this.generation) next = this.failed(e);
|
|
179801
|
+
} finally {
|
|
179802
|
+
this.busy = false;
|
|
179803
|
+
}
|
|
179804
|
+
if (this.again) this.schedule(0);
|
|
179805
|
+
else if (next !== void 0 && generation === this.generation)
|
|
179806
|
+
this.schedule(next);
|
|
179807
|
+
}
|
|
179808
|
+
async step(generation) {
|
|
179809
|
+
const client = this.client;
|
|
179810
|
+
const context = this.context;
|
|
179811
|
+
if (!client || !context) return void 0;
|
|
179812
|
+
await client.login();
|
|
179813
|
+
if (generation !== this.generation) return void 0;
|
|
179814
|
+
const now = Date.now();
|
|
179815
|
+
if (this.listAt === void 0 || now - this.listAt >= LIST_REFRESH_MS) {
|
|
179816
|
+
const devices = await client.devices();
|
|
179817
|
+
if (generation !== this.generation) return void 0;
|
|
179818
|
+
this.devices = devices;
|
|
179819
|
+
this.listAt = now;
|
|
179820
|
+
await context.storage.set(DEVICES_KEY, devices);
|
|
179821
|
+
await this.reconcile();
|
|
179822
|
+
if (generation !== this.generation) return void 0;
|
|
179823
|
+
}
|
|
179824
|
+
const n = this.devices.length;
|
|
179825
|
+
this.status = `signed in, ${n} device${n === 1 ? "" : "s"} found`;
|
|
179826
|
+
const keys3 = [...new Set([...this.mounted.values()].map((m) => m.key))];
|
|
179827
|
+
let lost;
|
|
179828
|
+
let reached = keys3.length === 0;
|
|
179829
|
+
for (const key of keys3) {
|
|
179830
|
+
const error = await this.poll(key, generation);
|
|
179831
|
+
if (generation !== this.generation) return void 0;
|
|
179832
|
+
if (error) lost ??= error;
|
|
179833
|
+
else reached = true;
|
|
179834
|
+
}
|
|
179835
|
+
if (!reached) throw lost;
|
|
179836
|
+
this.backoffMs = RETRY_BASE_MS2;
|
|
179837
|
+
const account = String(this.config.email).trim().toLowerCase();
|
|
179838
|
+
const load2 = accountLoad.get(account) ?? /* @__PURE__ */ new Map();
|
|
179839
|
+
load2.set(this, keys3);
|
|
179840
|
+
accountLoad.set(account, load2);
|
|
179841
|
+
const polled = [...load2.values()];
|
|
179842
|
+
const total = polled.reduce((sum, k) => sum + k.length, 0);
|
|
179843
|
+
const owned = new Set(polled.flat()).size;
|
|
179844
|
+
const floor2 = Math.ceil(86400 * total / (0.8 * (3200 + 1500 * owned)));
|
|
179845
|
+
return Math.max(this.pollSeconds(), floor2) * 1e3;
|
|
179846
|
+
}
|
|
179847
|
+
failed(e) {
|
|
179848
|
+
this.warn(errorText(e));
|
|
179849
|
+
const kind = e instanceof VeSyncError ? e.kind : void 0;
|
|
179850
|
+
if (kind === "credentials" || kind === "mfa") {
|
|
179851
|
+
this.stopped = true;
|
|
179852
|
+
for (const m of this.mounted.values()) {
|
|
179853
|
+
this.push(m, REACHABLE, { reachable: false });
|
|
179854
|
+
}
|
|
179855
|
+
this.status = kind === "mfa" ? "this account uses two-factor sign-in, which is not supported" : "sign-in failed: wrong email or password";
|
|
179856
|
+
return void 0;
|
|
179857
|
+
}
|
|
179858
|
+
if (kind === "quota") {
|
|
179859
|
+
this.status = "daily request quota used up, retrying later";
|
|
179860
|
+
return QUOTA_RETRY_MS;
|
|
179861
|
+
}
|
|
179862
|
+
this.status = "cloud unreachable, retrying";
|
|
179863
|
+
const delay = this.backoffMs;
|
|
179864
|
+
this.backoffMs = Math.min(delay * 2, RETRY_MAX_MS2);
|
|
179865
|
+
return delay;
|
|
179866
|
+
}
|
|
179867
|
+
// network comes back so the cycle can fail when every device is unreachable
|
|
179868
|
+
async poll(key, generation) {
|
|
179869
|
+
const units = [...this.mounted.values()].filter((m) => m.key === key);
|
|
179870
|
+
const client = this.client;
|
|
179871
|
+
if (generation !== this.generation || this.stopped) return;
|
|
179872
|
+
if (units.length === 0 || !client) return;
|
|
179873
|
+
const { device, family } = units[0];
|
|
179874
|
+
const writes = units.map((m) => m.writes);
|
|
179875
|
+
let state;
|
|
179876
|
+
try {
|
|
179877
|
+
state = await family.read(client, device);
|
|
179878
|
+
} catch (e) {
|
|
179879
|
+
if (generation !== this.generation) return;
|
|
179880
|
+
const kind = e instanceof VeSyncError ? e.kind : void 0;
|
|
179881
|
+
if (kind && ACCOUNT_ERRORS.includes(kind)) throw e;
|
|
179882
|
+
if (kind === "offline") {
|
|
179883
|
+
for (const m of units) this.push(m, REACHABLE, { reachable: false });
|
|
179884
|
+
return;
|
|
179885
|
+
}
|
|
179886
|
+
const message = errorText(e);
|
|
179887
|
+
if (this.failures.get(key) !== message) {
|
|
179888
|
+
this.warn(`${device.name}: status read failed: ${message}`);
|
|
179889
|
+
}
|
|
179890
|
+
this.failures.set(key, message);
|
|
179891
|
+
return kind === "network" ? e : void 0;
|
|
179892
|
+
}
|
|
179893
|
+
if (generation !== this.generation) return;
|
|
179894
|
+
this.failures.delete(key);
|
|
179895
|
+
for (const [i, m] of units.entries()) {
|
|
179896
|
+
if (this.mounted.get(m.id) !== m) continue;
|
|
179897
|
+
const fresh = m.writes === writes[i];
|
|
179898
|
+
for (const c of fresh ? state[m.unit.key] ?? [] : []) {
|
|
179899
|
+
m.real.set(c.clusterId, {
|
|
179900
|
+
...m.real.get(c.clusterId),
|
|
179901
|
+
...c.attributes
|
|
179902
|
+
});
|
|
179903
|
+
this.push(m, c.clusterId, c.attributes);
|
|
179904
|
+
}
|
|
179905
|
+
this.push(m, REACHABLE, { reachable: true });
|
|
179906
|
+
}
|
|
179907
|
+
}
|
|
179908
|
+
};
|
|
179909
|
+
|
|
178219
179910
|
// src/plugins/builtin/index.ts
|
|
178220
179911
|
var BUILTIN_PLUGINS = [
|
|
178221
179912
|
CameraPlugin,
|
|
178222
|
-
SecurityPlugin
|
|
179913
|
+
SecurityPlugin,
|
|
179914
|
+
VeSyncPlugin
|
|
178223
179915
|
];
|
|
178224
179916
|
|
|
178225
179917
|
// src/services/bridges/bridge-endpoint-manager.ts
|
|
@@ -178350,6 +180042,30 @@ init_esm();
|
|
|
178350
180042
|
init_send_ha_message();
|
|
178351
180043
|
import { callService as callService2 } from "home-assistant-js-websocket";
|
|
178352
180044
|
import { keys as keys2, pickBy, values as values3 } from "lodash-es";
|
|
180045
|
+
|
|
180046
|
+
// src/utils/trailing-index.ts
|
|
180047
|
+
function trailingIndex(value) {
|
|
180048
|
+
const match = /(\d+)$/.exec(value);
|
|
180049
|
+
return match ? Number.parseInt(match[1], 10) : void 0;
|
|
180050
|
+
}
|
|
180051
|
+
function isDigit(char) {
|
|
180052
|
+
return char != null && char >= "0" && char <= "9";
|
|
180053
|
+
}
|
|
180054
|
+
function withoutDomain(entityId) {
|
|
180055
|
+
return entityId.slice(entityId.indexOf(".") + 1);
|
|
180056
|
+
}
|
|
180057
|
+
function pairingIndex(entityId, otherId) {
|
|
180058
|
+
const a = withoutDomain(entityId);
|
|
180059
|
+
const b = withoutDomain(otherId);
|
|
180060
|
+
let i = 0;
|
|
180061
|
+
while (i < a.length && i < b.length && a[i] === b[i]) i++;
|
|
180062
|
+
if (isDigit(a[i]) && isDigit(b[i])) {
|
|
180063
|
+
while (i > 0 && isDigit(a[i - 1])) i--;
|
|
180064
|
+
}
|
|
180065
|
+
return trailingIndex(a.slice(i));
|
|
180066
|
+
}
|
|
180067
|
+
|
|
180068
|
+
// src/services/bridges/bridge-registry.ts
|
|
178353
180069
|
function fingerprintBattery(fingerprint) {
|
|
178354
180070
|
try {
|
|
178355
180071
|
const parsed = JSON.parse(fingerprint);
|
|
@@ -178432,9 +180148,7 @@ var BridgeRegistry = class _BridgeRegistry {
|
|
|
178432
180148
|
*/
|
|
178433
180149
|
batteryFingerprintFor(entityId, mapping) {
|
|
178434
180150
|
if (mapping?.batteryEntity || mapping?.disableBatteryMapping) return "";
|
|
178435
|
-
if (entityId.startsWith("
|
|
178436
|
-
return "";
|
|
178437
|
-
}
|
|
180151
|
+
if (entityId.startsWith("binary_sensor.")) return "";
|
|
178438
180152
|
if (!this.isAutoBatteryMappingEnabled() && !entityId.startsWith("vacuum.")) {
|
|
178439
180153
|
return "";
|
|
178440
180154
|
}
|
|
@@ -178506,6 +180220,20 @@ var BridgeRegistry = class _BridgeRegistry {
|
|
|
178506
180220
|
isBatteryEntityUsed(entityId) {
|
|
178507
180221
|
return this._usedBatteryEntities.has(entityId);
|
|
178508
180222
|
}
|
|
180223
|
+
// An enum with a running and a paused state: run (Home Connect, SmartThings),
|
|
180224
|
+
// in_use (Miele), running (LG) (#486).
|
|
180225
|
+
findOperationalStateEntityForDevice(deviceId) {
|
|
180226
|
+
for (const entity of values3(this.registry.entities)) {
|
|
180227
|
+
if (entity.device_id !== deviceId) continue;
|
|
180228
|
+
if (!entity.entity_id.startsWith("sensor.")) continue;
|
|
180229
|
+
const attrs = this.registry.states[entity.entity_id]?.attributes;
|
|
180230
|
+
const options = Array.isArray(attrs?.options) ? attrs.options : [];
|
|
180231
|
+
if (attrs?.device_class === "enum" && ["run", "running", "in_use"].some((o) => options.includes(o)) && ["pause", "paused"].some((o) => options.includes(o))) {
|
|
180232
|
+
return entity.entity_id;
|
|
180233
|
+
}
|
|
180234
|
+
}
|
|
180235
|
+
return void 0;
|
|
180236
|
+
}
|
|
178509
180237
|
/**
|
|
178510
180238
|
* Find a problem/safety binary sensor on the same HA device, so a smoke/CO
|
|
178511
180239
|
* alarm can drive hardwareFaultAlert from it. Prefers device_class=problem
|
|
@@ -178953,38 +180681,51 @@ var BridgeRegistry = class _BridgeRegistry {
|
|
|
178953
180681
|
return this._usedPressureEntities.has(entityId);
|
|
178954
180682
|
}
|
|
178955
180683
|
/**
|
|
178956
|
-
*
|
|
180684
|
+
* Sensor with this device_class on the same HA device.
|
|
180685
|
+
* Pair by index (`switch_2` -> `power_2`) when a strip has one
|
|
180686
|
+
* sensor per outlet. Otherwise the first match (#488).
|
|
178957
180687
|
*/
|
|
178958
|
-
|
|
178959
|
-
const
|
|
178960
|
-
for (const entity of entities) {
|
|
180688
|
+
findSensorEntityForDevice(deviceId, deviceClass, forEntityId) {
|
|
180689
|
+
const candidates = [];
|
|
180690
|
+
for (const entity of values3(this.registry.entities)) {
|
|
178961
180691
|
if (entity.device_id !== deviceId) continue;
|
|
178962
180692
|
if (!entity.entity_id.startsWith("sensor.")) continue;
|
|
178963
180693
|
const state = this.registry.states[entity.entity_id];
|
|
178964
180694
|
if (!state) continue;
|
|
178965
180695
|
const attrs = state.attributes;
|
|
178966
|
-
if (attrs.device_class ===
|
|
178967
|
-
|
|
180696
|
+
if (attrs.device_class === deviceClass) {
|
|
180697
|
+
candidates.push(entity.entity_id);
|
|
178968
180698
|
}
|
|
178969
180699
|
}
|
|
178970
|
-
return
|
|
180700
|
+
if (candidates.length <= 1) return candidates[0];
|
|
180701
|
+
if (forEntityId != null) {
|
|
180702
|
+
const matched = candidates.filter((id) => {
|
|
180703
|
+
const wanted = pairingIndex(forEntityId, id);
|
|
180704
|
+
return wanted != null && pairingIndex(id, forEntityId) === wanted;
|
|
180705
|
+
});
|
|
180706
|
+
if (matched.length === 1) return matched[0];
|
|
180707
|
+
}
|
|
180708
|
+
return candidates[0];
|
|
180709
|
+
}
|
|
180710
|
+
/**
|
|
180711
|
+
* Find a power sensor entity (device_class: power) on the same HA device.
|
|
180712
|
+
*/
|
|
180713
|
+
findPowerEntityForDevice(deviceId, forEntityId) {
|
|
180714
|
+
return this.findSensorEntityForDevice(
|
|
180715
|
+
deviceId,
|
|
180716
|
+
SensorDeviceClass.power,
|
|
180717
|
+
forEntityId
|
|
180718
|
+
);
|
|
178971
180719
|
}
|
|
178972
180720
|
/**
|
|
178973
180721
|
* Find an energy sensor entity (device_class: energy) on the same HA device.
|
|
178974
180722
|
*/
|
|
178975
|
-
findEnergyEntityForDevice(deviceId) {
|
|
178976
|
-
|
|
178977
|
-
|
|
178978
|
-
|
|
178979
|
-
|
|
178980
|
-
|
|
178981
|
-
if (!state) continue;
|
|
178982
|
-
const attrs = state.attributes;
|
|
178983
|
-
if (attrs.device_class === SensorDeviceClass.energy) {
|
|
178984
|
-
return entity.entity_id;
|
|
178985
|
-
}
|
|
178986
|
-
}
|
|
178987
|
-
return void 0;
|
|
180723
|
+
findEnergyEntityForDevice(deviceId, forEntityId) {
|
|
180724
|
+
return this.findSensorEntityForDevice(
|
|
180725
|
+
deviceId,
|
|
180726
|
+
SensorDeviceClass.energy,
|
|
180727
|
+
forEntityId
|
|
180728
|
+
);
|
|
178988
180729
|
}
|
|
178989
180730
|
markPowerEntityUsed(entityId) {
|
|
178990
180731
|
this._usedPowerEntities.add(entityId);
|
|
@@ -179093,13 +180834,19 @@ var BridgeRegistry = class _BridgeRegistry {
|
|
|
179093
180834
|
if (!entity.device_id) continue;
|
|
179094
180835
|
const domain = entity.entity_id.split(".")[0];
|
|
179095
180836
|
if (domain !== "switch" && domain !== "light") continue;
|
|
179096
|
-
const powerEntityId = this.findPowerEntityForDevice(
|
|
180837
|
+
const powerEntityId = this.findPowerEntityForDevice(
|
|
180838
|
+
entity.device_id,
|
|
180839
|
+
entity.entity_id
|
|
180840
|
+
);
|
|
179097
180841
|
if (powerEntityId && powerEntityId !== entity.entity_id) {
|
|
179098
180842
|
if (!this._usedPowerEntities.has(powerEntityId)) {
|
|
179099
180843
|
this._usedPowerEntities.add(powerEntityId);
|
|
179100
180844
|
}
|
|
179101
180845
|
}
|
|
179102
|
-
const energyEntityId = this.findEnergyEntityForDevice(
|
|
180846
|
+
const energyEntityId = this.findEnergyEntityForDevice(
|
|
180847
|
+
entity.device_id,
|
|
180848
|
+
entity.entity_id
|
|
180849
|
+
);
|
|
179103
180850
|
if (energyEntityId && energyEntityId !== entity.entity_id) {
|
|
179104
180851
|
if (!this._usedEnergyEntities.has(energyEntityId)) {
|
|
179105
180852
|
this._usedEnergyEntities.add(energyEntityId);
|
|
@@ -179195,12 +180942,13 @@ var EntityMappingSync = class {
|
|
|
179195
180942
|
// battery, bounds the per-state-batch check to a map hit
|
|
179196
180943
|
candidates = /* @__PURE__ */ new Map();
|
|
179197
180944
|
// Only endpoints the auto-mapping applies to belong in the candidates: a
|
|
179198
|
-
// manual or disabled mapping, or
|
|
179199
|
-
//
|
|
180945
|
+
// manual or disabled mapping, or the battery sensor's own endpoint, must
|
|
180946
|
+
// not claim the slot (last writer would win) and stall the recovery.
|
|
179200
180947
|
eligible(entityId) {
|
|
179201
180948
|
const mapping = this.getMapping(entityId);
|
|
179202
180949
|
if (mapping?.batteryEntity || mapping?.disableBatteryMapping) return false;
|
|
179203
|
-
if (entityId.startsWith("
|
|
180950
|
+
if (entityId.startsWith("binary_sensor.")) return false;
|
|
180951
|
+
if (entityId.startsWith("sensor.") && this.registry.initialState(entityId)?.attributes?.device_class === "battery") {
|
|
179204
180952
|
return false;
|
|
179205
180953
|
}
|
|
179206
180954
|
return entityId.startsWith("vacuum.") || !!this.registry.isAutoBatteryMappingEnabled?.();
|
|
@@ -179242,7 +180990,12 @@ var EntityMappingSync = class {
|
|
|
179242
180990
|
if (fingerprintBattery(fingerprint) != null) continue;
|
|
179243
180991
|
if (!this.eligible(entityId)) continue;
|
|
179244
180992
|
const deviceId = this.registry.entity(entityId)?.device_id;
|
|
179245
|
-
if (deviceId)
|
|
180993
|
+
if (!deviceId) continue;
|
|
180994
|
+
const owner = this.candidates.get(deviceId);
|
|
180995
|
+
if (owner && !owner.startsWith("sensor.") && entityId.startsWith("sensor.")) {
|
|
180996
|
+
continue;
|
|
180997
|
+
}
|
|
180998
|
+
this.candidates.set(deviceId, entityId);
|
|
179246
180999
|
}
|
|
179247
181000
|
}
|
|
179248
181001
|
// battery-less auto-map endpoints watch their device's sensors, an
|
|
@@ -179757,6 +181510,11 @@ var BridgeEndpointManager = class extends Service {
|
|
|
179757
181510
|
}
|
|
179758
181511
|
return result;
|
|
179759
181512
|
}
|
|
181513
|
+
// battery, humidity and pressure skips from LegacyEndpoint.create
|
|
181514
|
+
isAutoAssigned(entityId) {
|
|
181515
|
+
const r = this.registry;
|
|
181516
|
+
return r.isAutoBatteryMappingEnabled() && r.isBatteryEntityUsed(entityId) || r.isAutoHumidityMappingEnabled() && r.isHumidityEntityUsed(entityId) || r.isAutoPressureMappingEnabled() && r.isPressureEntityUsed(entityId);
|
|
181517
|
+
}
|
|
179760
181518
|
getEntityMapping(entityId) {
|
|
179761
181519
|
return this.mappingStorage.getMapping(this.bridgeId, entityId);
|
|
179762
181520
|
}
|
|
@@ -179911,6 +181669,17 @@ var BridgeEndpointManager = class extends Service {
|
|
|
179911
181669
|
const existingEndpoints = [];
|
|
179912
181670
|
const registryPushes = [];
|
|
179913
181671
|
const now = Date.now();
|
|
181672
|
+
const carriedElsewhere = new Set(
|
|
181673
|
+
endpoints.flatMap(
|
|
181674
|
+
(e) => [
|
|
181675
|
+
...e.mappedEntityIds ?? [],
|
|
181676
|
+
this.registry.batteryFingerprintFor(
|
|
181677
|
+
e.entityId,
|
|
181678
|
+
this.getEntityMapping(e.entityId)
|
|
181679
|
+
)
|
|
181680
|
+
].filter((id) => id && id !== e.entityId)
|
|
181681
|
+
)
|
|
181682
|
+
);
|
|
179914
181683
|
for (const endpoint of endpoints) {
|
|
179915
181684
|
const present = this.entityIds.includes(endpoint.entityId);
|
|
179916
181685
|
const claimant = endpointIdToEntity.get(endpoint.id);
|
|
@@ -179987,6 +181756,19 @@ var BridgeEndpointManager = class extends Service {
|
|
|
179987
181756
|
);
|
|
179988
181757
|
}
|
|
179989
181758
|
this.mappingFingerprints.delete(endpoint.entityId);
|
|
181759
|
+
} else if (carriedElsewhere.has(endpoint.entityId) && this.isAutoAssigned(endpoint.entityId)) {
|
|
181760
|
+
this.log.info(
|
|
181761
|
+
`Removing standalone endpoint ${endpoint.entityId}, auto-mapped onto another device`
|
|
181762
|
+
);
|
|
181763
|
+
try {
|
|
181764
|
+
await endpoint.close();
|
|
181765
|
+
} catch (e) {
|
|
181766
|
+
this.log.warn(
|
|
181767
|
+
`Failed to remove auto-mapped endpoint ${endpoint.entityId}:`,
|
|
181768
|
+
e
|
|
181769
|
+
);
|
|
181770
|
+
}
|
|
181771
|
+
this.mappingFingerprints.delete(endpoint.entityId);
|
|
179990
181772
|
} else {
|
|
179991
181773
|
const currentMapping = this.getEntityMapping(endpoint.entityId);
|
|
179992
181774
|
const storedFp = this.mappingFingerprints.get(endpoint.entityId) ?? "";
|
|
@@ -180355,6 +182137,7 @@ var BridgeEndpointManager = class extends Service {
|
|
|
180355
182137
|
// src/services/bridges/server-mode-bridge.ts
|
|
180356
182138
|
init_dist();
|
|
180357
182139
|
init_protocol3();
|
|
182140
|
+
init_update_entity_state();
|
|
180358
182141
|
init_diagnostic_event_bus();
|
|
180359
182142
|
var AUTO_FORCE_SYNC_INTERVAL_MS2 = 9e4;
|
|
180360
182143
|
function makeWarmStartState(state, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
@@ -180756,8 +182539,15 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
|
|
|
180756
182539
|
}
|
|
180757
182540
|
|
|
180758
182541
|
// src/matter/endpoints/server-mode-vacuum-endpoint.ts
|
|
182542
|
+
init_update_entity_state();
|
|
180759
182543
|
var logger255 = Logger.get("ServerModeVacuumEndpoint");
|
|
180760
182544
|
var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
|
|
182545
|
+
constructor(type, entityId, customName, mappedEntityIds, endpointId, vacuumEffective) {
|
|
182546
|
+
super(type, entityId, customName, mappedEntityIds, endpointId);
|
|
182547
|
+
this.vacuumEffective = vacuumEffective;
|
|
182548
|
+
this.flushUpdate = debounce7(this.flushPendingUpdate.bind(this), 50);
|
|
182549
|
+
}
|
|
182550
|
+
vacuumEffective;
|
|
180761
182551
|
static async create(registry3, entityId, mapping, endpointId) {
|
|
180762
182552
|
const deviceRegistry = registry3.deviceOf(entityId);
|
|
180763
182553
|
let state = registry3.initialState(entityId);
|
|
@@ -180955,16 +182745,13 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
|
|
|
180955
182745
|
entityId,
|
|
180956
182746
|
customName,
|
|
180957
182747
|
mappedIds,
|
|
180958
|
-
endpointId
|
|
182748
|
+
endpointId,
|
|
182749
|
+
{ mapping: effectiveMapping, state }
|
|
180959
182750
|
);
|
|
180960
182751
|
}
|
|
180961
182752
|
lastState;
|
|
180962
182753
|
pendingMappedChange = false;
|
|
180963
182754
|
flushUpdate;
|
|
180964
|
-
constructor(type, entityId, customName, mappedEntityIds, endpointId) {
|
|
180965
|
-
super(type, entityId, customName, mappedEntityIds, endpointId);
|
|
180966
|
-
this.flushUpdate = debounce7(this.flushPendingUpdate.bind(this), 50);
|
|
180967
|
-
}
|
|
180968
182755
|
clearTimers() {
|
|
180969
182756
|
this.flushUpdate.clear();
|
|
180970
182757
|
}
|
|
@@ -180996,10 +182783,13 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
|
|
|
180996
182783
|
this.flushUpdate(state);
|
|
180997
182784
|
}
|
|
180998
182785
|
async flushPendingUpdate(state) {
|
|
180999
|
-
let effectiveState = state;
|
|
182786
|
+
let effectiveState = withResolvedRooms(state, this.vacuumEffective);
|
|
181000
182787
|
if (this.pendingMappedChange) {
|
|
181001
182788
|
this.pendingMappedChange = false;
|
|
181002
|
-
effectiveState = {
|
|
182789
|
+
effectiveState = {
|
|
182790
|
+
...effectiveState,
|
|
182791
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
182792
|
+
};
|
|
181003
182793
|
}
|
|
181004
182794
|
await updateEntityState(this, effectiveState);
|
|
181005
182795
|
}
|
|
@@ -181943,12 +183733,12 @@ function formatPatchError(action, error) {
|
|
|
181943
183733
|
function extractErrorMessage(error) {
|
|
181944
183734
|
if (error instanceof Error) return error.message;
|
|
181945
183735
|
if (typeof error === "object" && error !== null) {
|
|
181946
|
-
const
|
|
181947
|
-
if (typeof
|
|
181948
|
-
const inner =
|
|
183736
|
+
const obj2 = error;
|
|
183737
|
+
if (typeof obj2.error === "object" && obj2.error !== null) {
|
|
183738
|
+
const inner = obj2.error;
|
|
181949
183739
|
if (typeof inner.message === "string") return inner.message;
|
|
181950
183740
|
}
|
|
181951
|
-
if (typeof
|
|
183741
|
+
if (typeof obj2.message === "string") return obj2.message;
|
|
181952
183742
|
}
|
|
181953
183743
|
return String(error);
|
|
181954
183744
|
}
|
|
@@ -184298,10 +186088,10 @@ export {
|
|
|
184298
186088
|
@matter/node/dist/esm/endpoints/index.js:
|
|
184299
186089
|
@matter/main/dist/esm/endpoints.js:
|
|
184300
186090
|
@matter/main/dist/esm/forwards/clusters/level-control.js:
|
|
184301
|
-
@matter/main/dist/esm/forwards/behaviors/boolean-state.js:
|
|
184302
|
-
@matter/main/dist/esm/forwards/behaviors/smoke-co-alarm.js:
|
|
184303
186091
|
@matter/main/dist/esm/forwards/behaviors/operational-state.js:
|
|
184304
186092
|
@matter/main/dist/esm/forwards/clusters/operational-state.js:
|
|
186093
|
+
@matter/main/dist/esm/forwards/behaviors/boolean-state.js:
|
|
186094
|
+
@matter/main/dist/esm/forwards/behaviors/smoke-co-alarm.js:
|
|
184305
186095
|
@matter/main/dist/esm/forwards/behaviors/rvc-operational-state.js:
|
|
184306
186096
|
@matter/main/dist/esm/forwards/clusters/rvc-operational-state.js:
|
|
184307
186097
|
@matter/main/dist/esm/forwards/clusters/mode-base.js:
|