@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.
@@ -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(str, maskChar = "*", unmaskedLength) {
3362
- return str.substring(0, unmaskedLength ?? 0) + str.substring(unmaskedLength ?? 0).replace(/./g, maskChar);
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, connect) => {
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 connect();
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, errorText) {
63792
+ function assert(session, errorText2) {
63793
63793
  if (!session?.isSecure) {
63794
- throw new MatterFlowError(errorText ?? "Unsecured session in secure context");
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, errorText) {
64119
+ function assert(session, errorText2) {
64120
64120
  if (!is(session)) {
64121
- throw new MatterFlowError(errorText ?? "Unsecured session in secure context");
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 str = typeof identifier === "bigint" ? GlobalFabricId.strOf(identifier) : `#${identifier}`;
66893
+ const str2 = typeof identifier === "bigint" ? GlobalFabricId.strOf(identifier) : `#${identifier}`;
66894
66894
  throw new FabricNotFoundError(
66895
- `Fabric index ${str} ${fabric === void 0 ? "does not exist" : "is deleted"}`
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, errorText) {
67387
+ function assert(session, errorText2) {
67388
67388
  if (!is(session)) {
67389
- throw new MatterFlowError(errorText ?? "Unsecured session in secure context");
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 str = `${value}`;
72484
- if (str.length > 60) {
72485
- str = `${str.substring(60)}\u2026`;
72483
+ let str2 = `${value}`;
72484
+ if (str2.length > 60) {
72485
+ str2 = `${str2.substring(60)}\u2026`;
72486
72486
  }
72487
- super(path14, `Value "${str}" is not ${type}`, code);
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 deviceKey = device.deviceIdentifier;
85821
- if (this.#invalidCredentialDevices.has(deviceKey)) {
85820
+ const deviceKey2 = device.deviceIdentifier;
85821
+ if (this.#invalidCredentialDevices.has(deviceKey2)) {
85822
85822
  continue;
85823
85823
  }
85824
- const existing = devicesByKey.get(deviceKey);
85824
+ const existing = devicesByKey.get(deviceKey2);
85825
85825
  if (existing === void 0) {
85826
- devicesByKey.set(deviceKey, { ...device, addresses: [...device.addresses] });
85826
+ devicesByKey.set(deviceKey2, { ...device, addresses: [...device.addresses] });
85827
85827
  } else {
85828
85828
  existing.addresses = [...existing.addresses, ...device.addresses];
85829
- devicesByKey.set(deviceKey, { ...existing, ...device, addresses: existing.addresses });
85829
+ devicesByKey.set(deviceKey2, { ...existing, ...device, addresses: existing.addresses });
85830
85830
  }
85831
85831
  }
85832
- for (const [deviceKey, device] of devicesByKey.entries()) {
85833
- let state = this.#devices.get(deviceKey);
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(deviceKey, state);
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(deviceKey) {
85851
- this.#invalidCredentialDevices.add(deviceKey);
85852
- this.#devices.delete(deviceKey);
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 [deviceKey, state] of this.#devices) {
85861
+ for (const [deviceKey2, state] of this.#devices) {
85862
85862
  for (const [addressUrl, address] of state.addresses) {
85863
- const attemptKey = `${deviceKey}:${addressUrl}`;
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 = (deviceKey) => {
85887
- let ac = deviceAborts.get(deviceKey);
85886
+ const getDeviceAbort = (deviceKey2) => {
85887
+ let ac = deviceAborts.get(deviceKey2);
85888
85888
  if (ac === void 0) {
85889
85889
  ac = new AbortController();
85890
- deviceAborts.set(deviceKey, ac);
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 (connect !== void 0) {
88126
- merged.connect = merge(base[key].connect ?? {}, 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 = connect(address, addressAbort).finally(() => {
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 connect(address, addressAbort) {
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, behaviors) {
92207
+ static for(type, behaviors2) {
92208
92208
  const props = {};
92209
- Object.values(behaviors).forEach((behavior) => {
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 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
101326
- __accessCheck = (obj, member, msg) => member.has(obj) || __typeError34("Cannot " + msg);
101327
- __privateIn = (member, obj) => Object(obj) !== obj ? __typeError34('Cannot use the "in" operator on this value') : member.has(obj);
101328
- __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
101329
- __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
101330
- __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
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 = (obj, key, value) => key in obj ? __defProp3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
102063
- __accessCheck2 = (obj, member, msg) => member.has(obj) || __typeError35("Cannot " + msg);
102064
- __privateIn2 = (member, obj) => Object(obj) !== obj ? __typeError35('Cannot use the "in" operator on this value') : member.has(obj);
102065
- __privateGet2 = (obj, member, getter) => (__accessCheck2(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
102066
- __privateSet2 = (obj, member, value, setter) => (__accessCheck2(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
102067
- __privateMethod2 = (obj, member, method) => (__accessCheck2(obj, member, "access private method"), method);
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 net = endpoint.stateOf(NetworkServer);
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(net.discoveryCapabilities)
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(...behaviors) {
106423
+ with(...behaviors2) {
106424
106424
  const serverArgs = [];
106425
106425
  const clientArgs = [];
106426
- for (const b of behaviors) {
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(...behaviors) {
106439
+ withBehaviors(...behaviors2) {
106440
106440
  return MutableEndpoint({
106441
106441
  ...options,
106442
- behaviors: SupportedBehaviors.extend(this.behaviors, 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 = (obj, key, value) => key in obj ? __defProp4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp3(obj, typeof key !== "symbol" ? key + "" : key, value);
109243
- __accessCheck3 = (obj, member, msg) => member.has(obj) || __typeError40("Cannot " + msg);
109244
- __privateIn3 = (member, obj) => Object(obj) !== obj ? __typeError40('Cannot use the "in" operator on this value') : member.has(obj);
109245
- __privateGet3 = (obj, member, getter) => (__accessCheck3(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
109246
- __privateSet3 = (obj, member, value, setter) => (__accessCheck3(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
109247
- __privateMethod3 = (obj, member, method) => (__accessCheck3(obj, member, "access private method"), method);
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 = (obj, key, value) => key in obj ? __defProp5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp4(obj, typeof key !== "symbol" ? key + "" : key, value);
115791
- __accessCheck4 = (obj, member, msg) => member.has(obj) || __typeError42("Cannot " + msg);
115792
- __privateIn4 = (member, obj) => Object(obj) !== obj ? __typeError42('Cannot use the "in" operator on this value') : member.has(obj);
115793
- __privateGet4 = (obj, member, getter) => (__accessCheck4(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
115794
- __privateSet4 = (obj, member, value, setter) => (__accessCheck4(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
115795
- __privateMethod4 = (obj, member, method) => (__accessCheck4(obj, member, "access private method"), method);
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
- 4448: "alexa",
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
- 43: {
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
- note: "Google Home and Aqara show Matter speakers."
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
- note: "TV/media types only show in Aqara Home here."
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
- note: "Google Home and Aqara show pressure sensors."
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
- note: "Apple Home does not show air quality."
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: "unknown",
131537
- note: "SolarPower is only rendered standalone by SmartThings today."
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
- note: "ElectricalMeter shows in Google Home and SmartThings; Apple and Alexa do not surface standalone power/energy."
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
- note: "Aqara lists battery storage; others show battery inside a device."
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
- note: "HA and Aqara render EnergyEvse; SmartThings announced support, unconfirmed (it sets the limit via EnableCharging and addresses modes by list position). Bridged EVSE can break Alexa device recognition, keep it off Alexa bridges."
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: "unknown",
131578
+ aqara: "no",
131579
+ smartthings: "unknown",
131565
131580
  note: "Mode Select is not supported here (Google #356)."
131566
131581
  },
131567
- 66: { apple: "no", google: "no", alexa: "no", aqara: "yes" },
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
- note: "Google Home and Aqara show pumps."
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: "yes",
131621
+ alexa: "no",
131593
131622
  aqara: "yes",
131594
- note: "Newer Matter detector, can be risky on Alexa bridges (#365)."
131623
+ smartthings: "yes",
131624
+ note: "Alexa has no capability for it and it can take an Alexa bridge offline (#365)."
131595
131625
  },
131596
- 118: { apple: "yes", google: "no", alexa: "yes", aqara: "yes" },
131597
- 117: {
131598
- apple: "no",
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: "unknown",
131601
- aqara: "unknown",
131602
- note: "Appliance types have little controller support."
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
- 15: { apple: "partial", google: "no", alexa: "yes", aqara: "unknown" }
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
- return Object.assign(debouncer, {
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
- processAction(_key, calls) {
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 intent = Object.keys(action.data ?? {}).length ? "adjust" : "command";
133095
- const key = `${target}-${action.action}-${intent}`;
133096
- this.debounceContext.get(key, 100)({ ...action, entityId });
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 body = req.body;
133978
- await settingsStorage.setBackupSettings(body);
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 runDiagnostics(mdnsInterface, mdnsIpv4) {
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(str) {
135060
+ function simpleHash(str2) {
134819
135061
  let hash2 = 0;
134820
- for (let i = 0; i < str.length; i++) {
134821
- const char = str.charCodeAt(i);
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 = ["camera", "security"];
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(filepath);
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
- await this.enforceRetention();
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
- async enforceRetention() {
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 = (obj, key, value) => key in obj ? __defProp6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp5(obj, typeof key !== "symbol" ? key + "" : key, value);
140845
- var __accessCheck5 = (obj, member, msg) => member.has(obj) || __typeError50("Cannot " + msg);
140846
- var __privateIn5 = (member, obj) => Object(obj) !== obj ? __typeError50('Cannot use the "in" operator on this value') : member.has(obj);
140847
- var __privateGet5 = (obj, member, getter) => (__accessCheck5(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
140848
- var __privateSet5 = (obj, member, value, setter) => (__accessCheck5(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
140849
- var __privateMethod5 = (obj, member, method) => (__accessCheck5(obj, member, "access private method"), method);
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 onOff = values4.onOff;
142061
- if (typeof onOff !== "boolean" || this.state.onOff === onOff) {
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 (onOff) {
142324
+ if (onOff2) {
142066
142325
  return this.on();
142067
142326
  }
142068
142327
  return this.off();
142069
142328
  }
142070
- this.internal.applyScenePendingOnOff = onOff;
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 onOff = this.internal.applyScenePendingOnOff;
142344
+ const onOff2 = this.internal.applyScenePendingOnOff;
142086
142345
  this.#clearDelayedSceneApplyData();
142087
- if (onOff === void 0) {
142346
+ if (onOff2 === void 0) {
142088
142347
  return;
142089
142348
  }
142090
- if (onOff) {
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 onOff = this.agent.get(OnOffServer);
147909
- if (onOff.state.onOff) {
147910
- onOff.state.onOff = false;
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 onOff = this.agent.get(OnOffServer);
147919
- if (!onOff.state.onOff) {
147920
- onOff.state.onOff = true;
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(onOff) {
148219
+ handleOnOffChange(onOff2) {
147961
148220
  if (this.internal.blockOnOffCouplingOnce) {
147962
148221
  this.internal.blockOnOffCouplingOnce = false;
147963
148222
  return;
147964
148223
  }
147965
- if (!onOff || this.state.onLevel === null) {
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 = (obj, key, value) => key in obj ? __defProp7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp6(obj, typeof key !== "symbol" ? key + "" : key, value);
148778
- var __accessCheck6 = (obj, member, msg) => member.has(obj) || __typeError51("Cannot " + msg);
148779
- var __privateIn6 = (member, obj) => Object(obj) !== obj ? __typeError51('Cannot use the "in" operator on this value') : member.has(obj);
148780
- var __privateGet6 = (obj, member, getter) => (__accessCheck6(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
148781
- var __privateSet6 = (obj, member, value, setter) => (__accessCheck6(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
148782
- var __privateMethod6 = (obj, member, method) => (__accessCheck6(obj, member, "access private method"), method);
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 = (obj, key, value) => key in obj ? __defProp8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp7(obj, typeof key !== "symbol" ? key + "" : key, value);
149032
- var __accessCheck7 = (obj, member, msg) => member.has(obj) || __typeError52("Cannot " + msg);
149033
- var __privateIn7 = (member, obj) => Object(obj) !== obj ? __typeError52('Cannot use the "in" operator on this value') : member.has(obj);
149034
- var __privateGet7 = (obj, member, getter) => (__accessCheck7(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
149035
- var __privateSet7 = (obj, member, value, setter) => (__accessCheck7(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
149036
- var __privateMethod7 = (obj, member, method) => (__accessCheck7(obj, member, "access private method"), method);
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 = (obj, key, value) => key in obj ? __defProp9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
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 = (obj, key, value) => __defNormalProp8(obj, typeof key !== "symbol" ? key + "" : key, value);
149140
- var __accessCheck8 = (obj, member, msg) => member.has(obj) || __typeError53("Cannot " + msg);
149141
- var __privateIn8 = (member, obj) => Object(obj) !== obj ? __typeError53('Cannot use the "in" operator on this value') : member.has(obj);
149142
- var __privateGet8 = (obj, member, getter) => (__accessCheck8(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
149143
- var __privateSet8 = (obj, member, value, setter) => (__accessCheck8(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
149144
- var __privateMethod8 = (obj, member, method) => (__accessCheck8(obj, member, "access private method"), method);
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(obj) {
157186
+ function dropUndefined(obj2) {
156926
157187
  const result = {};
156927
- for (const key in obj) {
156928
- if (obj[key] !== void 0) {
156929
- result[key] = obj[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/matter/endpoints/update-entity-state.ts
158395
- init_esm();
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 onOff = this.isOn(state);
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 (onOff === optimistic.expectedOnOff) {
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) ? [this, this.agent.get(FanSpeedMemoryBehavior)] : true
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(onOff, _oldValue, context) {
162259
+ onOffChanged(onOff2, _oldValue, context) {
162020
162260
  if (transactionIsOffline(context)) {
162021
162261
  return;
162022
162262
  }
162023
162263
  const remembered = this.remembered();
162024
- if (onOff && remembered.percent > 0) {
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.state.operationalState;
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
- if (activeStates.has(previousState) && !activeStates.has(newState)) {
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
- let matchedAreaId = null;
167137
- if (segmentId != null && isAllowedArea(segmentId)) {
167138
- matchedAreaId = segmentId;
167139
- }
167140
- if (matchedAreaId === null && segmentId != null) {
167141
- for (const area of serviceArea.state.supportedAreas) {
167142
- if (isAllowedArea(area.areaId) && area.areaId % 1e4 === segmentId) {
167143
- matchedAreaId = area.areaId;
167144
- break;
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
- if (matchedAreaId === null && roomName) {
167149
- const area = serviceArea.state.supportedAreas.find(
167150
- (a) => a.areaInfo.locationInfo?.locationName?.toLowerCase() === roomName.toLowerCase()
167151
- );
167152
- if (area && isAllowedArea(area.areaId)) {
167153
- matchedAreaId = area.areaId;
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 (matchedAreaId === s.lastCurrentArea) return;
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 createCleanAreaRvcRunModeServer(cleanAreaRooms) {
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 behaviors = { ...type.behaviors };
175275
- delete behaviors.bridgedDeviceBasicInformation;
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 behaviors = type.behaviors;
175506
- if (!(BRIDGED_INFO_ID in behaviors)) {
176007
+ const behaviors2 = type.behaviors;
176008
+ if (!(BRIDGED_INFO_ID in behaviors2)) {
175507
176009
  return type;
175508
176010
  }
175509
- const kept = Object.entries(behaviors).filter(([id]) => id !== BRIDGED_INFO_ID).map(([, behavior]) => behavior);
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 = { ...state, last_updated: (/* @__PURE__ */ new Date()).toISOString() };
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("sensor.") || entityId.startsWith("binary_sensor.")) {
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
- * Find a power sensor entity (device_class: power) on the same HA device.
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
- findPowerEntityForDevice(deviceId) {
178959
- const entities = values3(this.registry.entities);
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 === SensorDeviceClass.power) {
178967
- return entity.entity_id;
180696
+ if (attrs.device_class === deviceClass) {
180697
+ candidates.push(entity.entity_id);
178968
180698
  }
178969
180699
  }
178970
- return void 0;
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
- const entities = values3(this.registry.entities);
178977
- for (const entity of entities) {
178978
- if (entity.device_id !== deviceId) continue;
178979
- if (!entity.entity_id.startsWith("sensor.")) continue;
178980
- const state = this.registry.states[entity.entity_id];
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(entity.device_id);
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(entity.device_id);
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 a sensor endpoint sharing the device,
179199
- // must not claim the slot (last writer would win) and stall the recovery.
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("sensor.") || entityId.startsWith("binary_sensor.")) {
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) this.candidates.set(deviceId, entityId);
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 = { ...state, last_updated: (/* @__PURE__ */ new Date()).toISOString() };
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 obj = error;
181947
- if (typeof obj.error === "object" && obj.error !== null) {
181948
- const inner = obj.error;
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 obj.message === "string") return obj.message;
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: