@riddix/hamh 2.0.34 → 2.0.36

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.
@@ -1743,8 +1743,8 @@ var init_Cancelable = __esm({
1743
1743
  };
1744
1744
  return result;
1745
1745
  }
1746
- static set logger(logger203) {
1747
- this.#logger = logger203;
1746
+ static set logger(logger204) {
1747
+ this.#logger = logger204;
1748
1748
  }
1749
1749
  static get logger() {
1750
1750
  return this.#logger;
@@ -3422,13 +3422,13 @@ var init_Logger = __esm({
3422
3422
  *
3423
3423
  * @deprecated use {@link destinations}
3424
3424
  */
3425
- static addLogger(identifier, logger203, options) {
3425
+ static addLogger(identifier, logger204, options) {
3426
3426
  if (identifier in this.destinations) {
3427
3427
  throw new ImplementationError(`Logger "${identifier}" already exists`);
3428
3428
  }
3429
3429
  const dest = LogDestination({ name: identifier });
3430
3430
  const legacy = adaptDestinationToLegacy(dest);
3431
- legacy.log = logger203;
3431
+ legacy.log = logger204;
3432
3432
  if (options?.defaultLogLevel !== void 0) {
3433
3433
  legacy.defaultLogLevel = options.defaultLogLevel;
3434
3434
  }
@@ -5538,8 +5538,8 @@ function Construction(subject, initializer) {
5538
5538
  }
5539
5539
  }
5540
5540
  function unhandledError(...args) {
5541
- const logger203 = Logger.get(subject.constructor.name);
5542
- logger203.error(...args);
5541
+ const logger204 = Logger.get(subject.constructor.name);
5542
+ logger204.error(...args);
5543
5543
  }
5544
5544
  function createErrorHandler(name) {
5545
5545
  return (e) => {
@@ -9825,12 +9825,12 @@ var init_StandardCrypto = __esm({
9825
9825
  implementationName = "JS";
9826
9826
  #crypto;
9827
9827
  #subtle;
9828
- constructor(crypto7 = globalThis.crypto) {
9829
- const { subtle } = crypto7;
9830
- assertInterface("crypto", crypto7, requiredCryptoMethods);
9828
+ constructor(crypto8 = globalThis.crypto) {
9829
+ const { subtle } = crypto8;
9830
+ assertInterface("crypto", crypto8, requiredCryptoMethods);
9831
9831
  assertInterface("crypto.subtle", subtle, requiredSubtleMethods);
9832
9832
  super();
9833
- this.#crypto = crypto7;
9833
+ this.#crypto = crypto8;
9834
9834
  this.#subtle = subtle;
9835
9835
  }
9836
9836
  get subtle() {
@@ -10013,9 +10013,9 @@ var init_StandardCrypto = __esm({
10013
10013
  }
10014
10014
  };
10015
10015
  if ("crypto" in globalThis && globalThis.crypto?.subtle) {
10016
- const crypto7 = new StandardCrypto();
10017
- Environment.default.set(Entropy, crypto7);
10018
- Environment.default.set(Crypto, crypto7);
10016
+ const crypto8 = new StandardCrypto();
10017
+ Environment.default.set(Entropy, crypto8);
10018
+ Environment.default.set(Crypto, crypto8);
10019
10019
  }
10020
10020
  }
10021
10021
  });
@@ -10025,9 +10025,9 @@ function MockCrypto(index = 128, implementation = StandardCrypto) {
10025
10025
  if (index < 0 || index > 255) {
10026
10026
  throw new ImplementationError(`Index for stable crypto must be 0-255`);
10027
10027
  }
10028
- const crypto7 = new implementation();
10029
- const { randomBytes: randomBytes2, createKeyPair } = crypto7;
10030
- Object.defineProperties(crypto7, {
10028
+ const crypto8 = new implementation();
10029
+ const { randomBytes: randomBytes2, createKeyPair } = crypto8;
10030
+ Object.defineProperties(crypto8, {
10031
10031
  index: {
10032
10032
  get() {
10033
10033
  return index;
@@ -10038,12 +10038,12 @@ function MockCrypto(index = 128, implementation = StandardCrypto) {
10038
10038
  },
10039
10039
  entropic: {
10040
10040
  get() {
10041
- return crypto7.randomBytes === randomBytes2;
10041
+ return crypto8.randomBytes === randomBytes2;
10042
10042
  },
10043
10043
  set(entropic) {
10044
10044
  if (entropic) {
10045
- crypto7.randomBytes = randomBytes2;
10046
- crypto7.createKeyPair = createKeyPair;
10045
+ crypto8.randomBytes = randomBytes2;
10046
+ crypto8.createKeyPair = createKeyPair;
10047
10047
  } else {
10048
10048
  disableEntropy();
10049
10049
  }
@@ -10051,15 +10051,15 @@ function MockCrypto(index = 128, implementation = StandardCrypto) {
10051
10051
  }
10052
10052
  });
10053
10053
  disableEntropy();
10054
- return crypto7;
10054
+ return crypto8;
10055
10055
  function disableEntropy() {
10056
- crypto7.randomBytes = function getRandomDataNONENTROPIC(length) {
10056
+ crypto8.randomBytes = function getRandomDataNONENTROPIC(length) {
10057
10057
  const result = new Uint8Array(length);
10058
10058
  result.fill(index);
10059
10059
  return result;
10060
10060
  };
10061
- crypto7.createKeyPair = function getRandomDataNONENTROPIC() {
10062
- const privateBits = ec.mapHashToField(Bytes.of(crypto7.randomBytes(48)), ec.p256.Point.CURVE().n);
10061
+ crypto8.createKeyPair = function getRandomDataNONENTROPIC() {
10062
+ const privateBits = ec.mapHashToField(Bytes.of(crypto8.randomBytes(48)), ec.p256.Point.CURVE().n);
10063
10063
  return Key({
10064
10064
  kty: KeyType.EC,
10065
10065
  crv: CurveType.p256,
@@ -10092,9 +10092,9 @@ var init_NodeJsStyleCrypto = __esm({
10092
10092
  NodeJsStyleCrypto = class extends Crypto {
10093
10093
  implementationName = "Node.js";
10094
10094
  #crypto;
10095
- constructor(crypto7) {
10095
+ constructor(crypto8) {
10096
10096
  super();
10097
- this.#crypto = crypto7;
10097
+ this.#crypto = crypto8;
10098
10098
  }
10099
10099
  encrypt(key, data, nonce, aad) {
10100
10100
  const cipher = this.#crypto.createCipheriv(CRYPTO_ENCRYPT_ALGORITHM, Bytes.of(key), Bytes.of(nonce), {
@@ -10368,29 +10368,29 @@ var init_Spake2p = __esm({
10368
10368
  #context;
10369
10369
  #random;
10370
10370
  #w0;
10371
- static async computeW0W1(crypto7, { iterations, salt }, pin) {
10371
+ static async computeW0W1(crypto8, { iterations, salt }, pin) {
10372
10372
  const pinWriter = new DataWriter(Endian.Little);
10373
10373
  pinWriter.writeUInt32(pin);
10374
10374
  const ws2 = Bytes.of(
10375
- await crypto7.createPbkdf2Key(pinWriter.toByteArray(), salt, iterations, CRYPTO_W_SIZE_BYTES * 2)
10375
+ await crypto8.createPbkdf2Key(pinWriter.toByteArray(), salt, iterations, CRYPTO_W_SIZE_BYTES * 2)
10376
10376
  );
10377
10377
  const curve = Point2.CURVE();
10378
10378
  const w0 = mod2(bytesToNumberBE(ws2.slice(0, 40)), curve.n);
10379
10379
  const w1 = mod2(bytesToNumberBE(ws2.slice(40, 80)), curve.n);
10380
10380
  return { w0, w1 };
10381
10381
  }
10382
- static async computeW0L(crypto7, pbkdfParameters, pin) {
10383
- const { w0, w1 } = await this.computeW0W1(crypto7, pbkdfParameters, pin);
10382
+ static async computeW0L(crypto8, pbkdfParameters, pin) {
10383
+ const { w0, w1 } = await this.computeW0W1(crypto8, pbkdfParameters, pin);
10384
10384
  const L = Point2.BASE.multiply(w1).toBytes(false);
10385
10385
  return { w0, L };
10386
10386
  }
10387
- static create(crypto7, context, w0) {
10387
+ static create(crypto8, context, w0) {
10388
10388
  const curve = Point2.CURVE();
10389
- const random = crypto7.randomBigInt(32, curve.p);
10390
- return new _Spake2p(crypto7, context, random, w0);
10389
+ const random = crypto8.randomBigInt(32, curve.p);
10390
+ return new _Spake2p(crypto8, context, random, w0);
10391
10391
  }
10392
- constructor(crypto7, context, random, w0) {
10393
- this.#crypto = crypto7;
10392
+ constructor(crypto8, context, random, w0) {
10393
+ this.#crypto = crypto8;
10394
10394
  this.#context = context;
10395
10395
  this.#random = random;
10396
10396
  this.#w0 = w0;
@@ -10521,10 +10521,10 @@ var init_X509 = __esm({
10521
10521
  init_Pem();
10522
10522
  init_X962();
10523
10523
  ((X5092) => {
10524
- async function sign(crypto7, key, cert) {
10524
+ async function sign(crypto8, key, cert) {
10525
10525
  return {
10526
10526
  ...cert,
10527
- signature: (await crypto7.signEcdsa(key, certificateToDer(cert))).der
10527
+ signature: (await crypto8.signEcdsa(key, certificateToDer(cert))).der
10528
10528
  };
10529
10529
  }
10530
10530
  X5092.sign = sign;
@@ -46460,9 +46460,9 @@ var init_NodeId = __esm({
46460
46460
  return hex.fixed(nodeId3, 16);
46461
46461
  }
46462
46462
  NodeId2.strOf = strOf;
46463
- NodeId2.randomOperationalNodeId = (crypto7) => {
46463
+ NodeId2.randomOperationalNodeId = (crypto8) => {
46464
46464
  while (true) {
46465
- const randomBigInt = crypto7.randomBigInt(8);
46465
+ const randomBigInt = crypto8.randomBigInt(8);
46466
46466
  if (randomBigInt >= OPERATIONAL_NODE_MIN && randomBigInt <= OPERATIONAL_NODE_MAX) {
46467
46467
  return NodeId2(randomBigInt);
46468
46468
  }
@@ -88338,11 +88338,11 @@ var init_GlobalFabricId = __esm({
88338
88338
  return hex.fixed(id, 16);
88339
88339
  }
88340
88340
  GlobalFabricId2.strOf = strOf;
88341
- async function compute(crypto7, id, caKey) {
88341
+ async function compute(crypto8, id, caKey) {
88342
88342
  const saltWriter = new DataWriter();
88343
88343
  saltWriter.writeUInt64(id);
88344
88344
  return GlobalFabricId2(
88345
- await crypto7.createHkdfKey(
88345
+ await crypto8.createHkdfKey(
88346
88346
  Bytes.of(caKey).slice(1),
88347
88347
  saltWriter.toByteArray(),
88348
88348
  COMPRESSED_FABRIC_ID_INFO,
@@ -90621,8 +90621,8 @@ var init_Certificate = __esm({
90621
90621
  * Sign the certificate using the provided crypto and key.
90622
90622
  * It throws a CertificateError if the certificate is already signed.
90623
90623
  */
90624
- async sign(crypto7, key) {
90625
- this.signature = await crypto7.signEcdsa(key, this.asUnsignedDer());
90624
+ async sign(crypto8, key) {
90625
+ this.signature = await crypto8.signEcdsa(key, this.asUnsignedDer());
90626
90626
  }
90627
90627
  /**
90628
90628
  * Serialize as signed DER.
@@ -90684,7 +90684,7 @@ var init_Certificate = __esm({
90684
90684
  }
90685
90685
  };
90686
90686
  ((Certificate2) => {
90687
- async function createCertificateSigningRequest(crypto7, key) {
90687
+ async function createCertificateSigningRequest(crypto8, key) {
90688
90688
  const request = {
90689
90689
  version: 0,
90690
90690
  subject: { organization: X520.OrganisationName("CSR") },
@@ -90694,7 +90694,7 @@ var init_Certificate = __esm({
90694
90694
  return DerCodec.encode({
90695
90695
  request,
90696
90696
  signAlgorithm: X962.EcdsaWithSHA256,
90697
- signature: DerBitString((await crypto7.signEcdsa(key, DerCodec.encode(request))).der)
90697
+ signature: DerBitString((await crypto8.signEcdsa(key, DerCodec.encode(request))).der)
90698
90698
  });
90699
90699
  }
90700
90700
  Certificate2.createCertificateSigningRequest = createCertificateSigningRequest;
@@ -90973,7 +90973,7 @@ var init_Certificate = __esm({
90973
90973
  };
90974
90974
  }
90975
90975
  Certificate2.parseAsn1Certificate = parseAsn1Certificate;
90976
- async function getPublicKeyFromCsr(crypto7, encodedCsr) {
90976
+ async function getPublicKeyFromCsr(crypto8, encodedCsr) {
90977
90977
  const { _elements: rootElements } = DerCodec.decode(encodedCsr);
90978
90978
  if (rootElements?.length !== 3) {
90979
90979
  throw new CertificateError("Invalid CSR data");
@@ -91011,7 +91011,7 @@ var init_Certificate = __esm({
91011
91011
  if (signatureAlgorithmBytes === void 0 || !Bytes.areEqual(X962.EcdsaWithSHA256._objectId._bytes, signatureAlgorithmBytes)) {
91012
91012
  throw new CertificateError("Unsupported signature algorithm in CSR");
91013
91013
  }
91014
- await crypto7.verifyEcdsa(
91014
+ await crypto8.verifyEcdsa(
91015
91015
  PublicKey(publicKey),
91016
91016
  DerCodec.encode(requestNode),
91017
91017
  new EcdsaSignature(signatureNode._bytes, "der")
@@ -91240,7 +91240,7 @@ var init_Icac = __esm({
91240
91240
  * Verify requirements a Matter Intermediate CA certificate must fulfill.
91241
91241
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
91242
91242
  */
91243
- async verify(crypto7, root) {
91243
+ async verify(crypto8, root) {
91244
91244
  this.generalVerify();
91245
91245
  const {
91246
91246
  subject,
@@ -91316,7 +91316,7 @@ var init_Icac = __esm({
91316
91316
  `Ica certificate authorityKeyIdentifier must be equal to root cert subjectKeyIdentifier.`
91317
91317
  );
91318
91318
  }
91319
- await crypto7.verifyEcdsa(PublicKey(root.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
91319
+ await crypto8.verifyEcdsa(PublicKey(root.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
91320
91320
  }
91321
91321
  };
91322
91322
  }
@@ -91367,7 +91367,7 @@ var init_Noc = __esm({
91367
91367
  * Verify requirements a Matter Node Operational certificate must fulfill.
91368
91368
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
91369
91369
  */
91370
- async verify(crypto7, root, ica) {
91370
+ async verify(crypto8, root, ica) {
91371
91371
  this.generalVerify();
91372
91372
  const {
91373
91373
  subject,
@@ -91452,7 +91452,7 @@ var init_Noc = __esm({
91452
91452
  `Noc certificate authorityKeyIdentifier must be equal to Root/Ica subjectKeyIdentifier.`
91453
91453
  );
91454
91454
  }
91455
- await crypto7.verifyEcdsa(PublicKey(issuer.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
91455
+ await crypto8.verifyEcdsa(PublicKey(issuer.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
91456
91456
  }
91457
91457
  };
91458
91458
  }
@@ -93037,10 +93037,10 @@ var init_MessageCounter = __esm({
93037
93037
  * counter is not allowed to rollover and the callback is called before a rollover would happen. Optionally provide
93038
93038
  * a number of messages before the rollover callback is called (Default 1000).
93039
93039
  */
93040
- constructor(crypto7, onRollover, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93040
+ constructor(crypto8, onRollover, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93041
93041
  this.onRollover = onRollover;
93042
93042
  this.rolloverInfoDifference = rolloverInfoDifference;
93043
- this.messageCounter = (crypto7.randomUint32 >>> 4) + 1;
93043
+ this.messageCounter = (crypto8.randomUint32 >>> 4) + 1;
93044
93044
  }
93045
93045
  messageCounter;
93046
93046
  async getIncrementedCounter() {
@@ -93058,8 +93058,8 @@ var init_MessageCounter = __esm({
93058
93058
  }
93059
93059
  };
93060
93060
  PersistedMessageCounter = class _PersistedMessageCounter extends MessageCounter {
93061
- constructor(crypto7, storageContext, storageKey, aboutToRolloverCallback, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93062
- super(crypto7, aboutToRolloverCallback, rolloverInfoDifference);
93061
+ constructor(crypto8, storageContext, storageKey, aboutToRolloverCallback, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93062
+ super(crypto8, aboutToRolloverCallback, rolloverInfoDifference);
93063
93063
  this.storageContext = storageContext;
93064
93064
  this.storageKey = storageKey;
93065
93065
  this.#construction = Construction(this, async () => {
@@ -93078,10 +93078,10 @@ var init_MessageCounter = __esm({
93078
93078
  get construction() {
93079
93079
  return this.#construction;
93080
93080
  }
93081
- static async create(crypto7, storageContext, storageKey, aboutToRolloverCallback, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93081
+ static async create(crypto8, storageContext, storageKey, aboutToRolloverCallback, rolloverInfoDifference = ROLLOVER_INFO_DIFFERENCE) {
93082
93082
  return asyncNew(
93083
93083
  _PersistedMessageCounter,
93084
- crypto7,
93084
+ crypto8,
93085
93085
  storageContext,
93086
93086
  storageKey,
93087
93087
  aboutToRolloverCallback,
@@ -93994,7 +93994,7 @@ var init_NodeSession = __esm({
93994
93994
  }
93995
93995
  constructor(config10) {
93996
93996
  const {
93997
- crypto: crypto7,
93997
+ crypto: crypto8,
93998
93998
  manager,
93999
93999
  id,
94000
94000
  fabric,
@@ -94012,14 +94012,14 @@ var init_NodeSession = __esm({
94012
94012
  setActiveTimestamp: true,
94013
94013
  // We always set the active timestamp for Secure sessions
94014
94014
  // Can be changed to a PersistedMessageCounter if we implement session storage
94015
- messageCounter: new MessageCounter(crypto7, async () => {
94015
+ messageCounter: new MessageCounter(crypto8, async () => {
94016
94016
  await this.initiateClose(async () => {
94017
94017
  await this.closeSubscriptions(true);
94018
94018
  });
94019
94019
  }),
94020
94020
  messageReceptionState: new MessageReceptionStateEncryptedWithoutRollover(0)
94021
94021
  });
94022
- this.#crypto = crypto7;
94022
+ this.#crypto = crypto8;
94023
94023
  this.#id = id;
94024
94024
  this.#fabric = fabric;
94025
94025
  this.#peerNodeId = peerNodeId;
@@ -94468,22 +94468,22 @@ var init_CaseClient = __esm({
94468
94468
  }
94469
94469
  }
94470
94470
  async #doPair(messenger, exchange, fabric, peerNodeId, caseAuthenticatedTags) {
94471
- const { crypto: crypto7 } = fabric;
94472
- const initiatorRandom = crypto7.randomBytes(32);
94471
+ const { crypto: crypto8 } = fabric;
94472
+ const initiatorRandom = crypto8.randomBytes(32);
94473
94473
  const initiatorSessionId = await this.#sessions.getNextAvailableSessionId();
94474
94474
  const { operationalIdentityProtectionKey, operationalCert: localNoc, intermediateCACert: localIcac } = fabric;
94475
- const localKey = await crypto7.createKeyPair();
94475
+ const localKey = await crypto8.createKeyPair();
94476
94476
  let sigma1Bytes;
94477
94477
  let resumed = false;
94478
94478
  let resumptionRecord = this.#sessions.findResumptionRecordByAddress(fabric.addressOf(peerNodeId));
94479
94479
  if (resumptionRecord !== void 0) {
94480
94480
  const { sharedSecret, resumptionId } = resumptionRecord;
94481
- const resumeKey = await crypto7.createHkdfKey(
94481
+ const resumeKey = await crypto8.createHkdfKey(
94482
94482
  sharedSecret,
94483
94483
  Bytes.concat(initiatorRandom, resumptionId),
94484
94484
  KDFSR1_KEY_INFO
94485
94485
  );
94486
- const initiatorResumeMic = crypto7.encrypt(resumeKey, new Uint8Array(0), RESUME1_MIC_NONCE);
94486
+ const initiatorResumeMic = crypto8.encrypt(resumeKey, new Uint8Array(0), RESUME1_MIC_NONCE);
94487
94487
  sigma1Bytes = await messenger.sendSigma1({
94488
94488
  initiatorSessionId,
94489
94489
  destinationId: await fabric.currentDestinationIdFor(peerNodeId, initiatorRandom),
@@ -94518,8 +94518,8 @@ var init_CaseClient = __esm({
94518
94518
  ...resumptionSessionParams ?? {}
94519
94519
  };
94520
94520
  const resumeSalt = Bytes.concat(initiatorRandom, resumptionId);
94521
- const resumeKey = await crypto7.createHkdfKey(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
94522
- crypto7.decrypt(resumeKey, resumeMic, RESUME2_MIC_NONCE);
94521
+ const resumeKey = await crypto8.createHkdfKey(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
94522
+ crypto8.decrypt(resumeKey, resumeMic, RESUME2_MIC_NONCE);
94523
94523
  const secureSessionSalt = Bytes.concat(initiatorRandom, resumptionRecord.resumptionId);
94524
94524
  secureSession = await this.#sessions.createSecureSession({
94525
94525
  channel: exchange.channel.channel,
@@ -94563,15 +94563,15 @@ var init_CaseClient = __esm({
94563
94563
  ...exchange.session.parameters,
94564
94564
  ...responderSessionParams ?? {}
94565
94565
  };
94566
- const sharedSecret = await crypto7.generateDhSecret(localKey, PublicKey(peerKey));
94566
+ const sharedSecret = await crypto8.generateDhSecret(localKey, PublicKey(peerKey));
94567
94567
  const sigma2Salt = Bytes.concat(
94568
94568
  operationalIdentityProtectionKey,
94569
94569
  responderRandom,
94570
94570
  peerKey,
94571
- await crypto7.computeHash(sigma1Bytes)
94571
+ await crypto8.computeHash(sigma1Bytes)
94572
94572
  );
94573
- const sigma2Key = await crypto7.createHkdfKey(sharedSecret, sigma2Salt, KDFSR2_INFO);
94574
- const peerEncryptedData = crypto7.decrypt(sigma2Key, peerEncrypted, TBE_DATA2_NONCE);
94573
+ const sigma2Key = await crypto8.createHkdfKey(sharedSecret, sigma2Salt, KDFSR2_INFO);
94574
+ const peerEncryptedData = crypto8.decrypt(sigma2Key, peerEncrypted, TBE_DATA2_NONCE);
94575
94575
  const {
94576
94576
  responderNoc: peerNoc,
94577
94577
  responderIcac: peerIcac,
@@ -94588,7 +94588,7 @@ var init_CaseClient = __esm({
94588
94588
  ellipticCurvePublicKey: peerPublicKey,
94589
94589
  subject: { fabricId: peerFabricIdNOCert, nodeId: peerNodeIdNOCert }
94590
94590
  } = Noc.fromTlv(peerNoc).cert;
94591
- await crypto7.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, new EcdsaSignature(peerSignature));
94591
+ await crypto8.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, new EcdsaSignature(peerSignature));
94592
94592
  if (peerNodeIdNOCert !== peerNodeId) {
94593
94593
  throw new UnexpectedDataError(
94594
94594
  `The node ID in the peer certificate ${peerNodeIdNOCert} doesn't match the expected peer node ID ${peerNodeId}`
@@ -94612,9 +94612,9 @@ var init_CaseClient = __esm({
94612
94612
  await fabric.verifyCredentials(peerNoc, peerIcac);
94613
94613
  const sigma3Salt = Bytes.concat(
94614
94614
  operationalIdentityProtectionKey,
94615
- await crypto7.computeHash([sigma1Bytes, sigma2Bytes])
94615
+ await crypto8.computeHash([sigma1Bytes, sigma2Bytes])
94616
94616
  );
94617
- const sigma3Key = await crypto7.createHkdfKey(sharedSecret, sigma3Salt, KDFSR3_INFO);
94617
+ const sigma3Key = await crypto8.createHkdfKey(sharedSecret, sigma3Salt, KDFSR3_INFO);
94618
94618
  const signatureData = TlvSignedData.encode({
94619
94619
  responderNoc: localNoc,
94620
94620
  responderIcac: localIcac,
@@ -94627,13 +94627,13 @@ var init_CaseClient = __esm({
94627
94627
  responderIcac: localIcac,
94628
94628
  signature: signature.bytes
94629
94629
  });
94630
- const encrypted = crypto7.encrypt(sigma3Key, encryptedData, TBE_DATA3_NONCE);
94630
+ const encrypted = crypto8.encrypt(sigma3Key, encryptedData, TBE_DATA3_NONCE);
94631
94631
  const sigma3Bytes = await messenger.sendSigma3({ encrypted });
94632
94632
  await messenger.waitForSuccess("Sigma3-Success");
94633
94633
  const sessionCaseAuthenticatedTags = caseAuthenticatedTags ?? resumptionRecord?.caseAuthenticatedTags;
94634
94634
  const secureSessionSalt = Bytes.concat(
94635
94635
  operationalIdentityProtectionKey,
94636
- await crypto7.computeHash([sigma1Bytes, sigma2Bytes, sigma3Bytes])
94636
+ await crypto8.computeHash([sigma1Bytes, sigma2Bytes, sigma3Bytes])
94637
94637
  );
94638
94638
  secureSession = await this.#sessions.createSecureSession({
94639
94639
  channel: exchange.channel.channel,
@@ -94711,7 +94711,7 @@ var init_Rcac = __esm({
94711
94711
  * Verify requirements a Matter Root certificate must fulfill.
94712
94712
  * Rules for this are listed in @see {@link MatterSpecification.v12.Core} §6.5.x
94713
94713
  */
94714
- async verify(crypto7) {
94714
+ async verify(crypto8) {
94715
94715
  this.generalVerify();
94716
94716
  const { subject, extensions } = this.cert;
94717
94717
  const { fabricId: fabricId3, rcacId } = subject;
@@ -94768,7 +94768,7 @@ var init_Rcac = __esm({
94768
94768
  `Root certificate authorityKeyIdentifier must be equal to subjectKeyIdentifier.`
94769
94769
  );
94770
94770
  }
94771
- await crypto7.verifyEcdsa(PublicKey(this.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
94771
+ await crypto8.verifyEcdsa(PublicKey(this.cert.ellipticCurvePublicKey), this.asUnsignedDer(), this.signature);
94772
94772
  }
94773
94773
  };
94774
94774
  }
@@ -94903,8 +94903,8 @@ var init_KeySets = __esm({
94903
94903
  };
94904
94904
  }
94905
94905
  /** Calculates a group session id based on the operational group key. */
94906
- async sessionIdFromKey(crypto7, operationalGroupKey) {
94907
- const groupKeyHash = await crypto7.createHkdfKey(operationalGroupKey, new Uint8Array(), GROUP_KEY_INFO, 2);
94906
+ async sessionIdFromKey(crypto8, operationalGroupKey) {
94907
+ const groupKeyHash = await crypto8.createHkdfKey(operationalGroupKey, new Uint8Array(), GROUP_KEY_INFO, 2);
94908
94908
  return new DataReader(groupKeyHash).readUInt16();
94909
94909
  }
94910
94910
  /**
@@ -94957,8 +94957,8 @@ var init_MessagingState = __esm({
94957
94957
  #messageDataReceptionState = /* @__PURE__ */ new Map();
94958
94958
  #crypto;
94959
94959
  #storage;
94960
- constructor(crypto7, storage2) {
94961
- this.#crypto = crypto7;
94960
+ constructor(crypto8, storage2) {
94961
+ this.#crypto = crypto8;
94962
94962
  if (storage2 !== void 0) {
94963
94963
  this.#storage = storage2;
94964
94964
  }
@@ -95491,8 +95491,8 @@ var init_Fabric = __esm({
95491
95491
  * Certain derived fields that require async crypto operations to compute must be supplied here. Use {@link create}
95492
95492
  * to populate these fields automatically.
95493
95493
  */
95494
- constructor(crypto7, config10) {
95495
- this.#crypto = crypto7;
95494
+ constructor(crypto8, config10) {
95495
+ this.#crypto = crypto8;
95496
95496
  this.fabricIndex = config10.fabricIndex;
95497
95497
  this.fabricId = config10.fabricId;
95498
95498
  this.nodeId = config10.nodeId;
@@ -95521,20 +95521,20 @@ var init_Fabric = __esm({
95521
95521
  *
95522
95522
  * This async creation path populates derived fields that require async crypto operations to compute.
95523
95523
  */
95524
- static async create(crypto7, config10) {
95524
+ static async create(crypto8, config10) {
95525
95525
  let { globalId, operationalIdentityProtectionKey } = config10;
95526
95526
  if (globalId === void 0) {
95527
95527
  const caKey = config10.rootPublicKey ?? Rcac.publicKeyOfTlv(config10.rootCert);
95528
- globalId = await GlobalFabricId.compute(crypto7, config10.fabricId, caKey);
95528
+ globalId = await GlobalFabricId.compute(crypto8, config10.fabricId, caKey);
95529
95529
  }
95530
95530
  if (operationalIdentityProtectionKey === void 0) {
95531
- operationalIdentityProtectionKey = await crypto7.createHkdfKey(
95531
+ operationalIdentityProtectionKey = await crypto8.createHkdfKey(
95532
95532
  config10.identityProtectionKey,
95533
95533
  Bytes.fromBigInt(globalId, 8),
95534
95534
  GROUP_SECURITY_INFO
95535
95535
  );
95536
95536
  }
95537
- return new _Fabric(crypto7, {
95537
+ return new _Fabric(crypto8, {
95538
95538
  ...config10,
95539
95539
  globalId,
95540
95540
  operationalIdentityProtectionKey
@@ -95791,12 +95791,12 @@ var init_Fabric = __esm({
95791
95791
  #vvsc;
95792
95792
  #fabricIndex;
95793
95793
  #label = "";
95794
- constructor(crypto7, key) {
95795
- this.#crypto = crypto7;
95794
+ constructor(crypto8, key) {
95795
+ this.#crypto = crypto8;
95796
95796
  this.#keyPair = key;
95797
95797
  }
95798
- static async create(crypto7) {
95799
- return new _FabricBuilder(crypto7, await crypto7.createKeyPair());
95798
+ static async create(crypto8) {
95799
+ return new _FabricBuilder(crypto8, await crypto8.createKeyPair());
95800
95800
  }
95801
95801
  get publicKey() {
95802
95802
  return this.#keyPair.publicKey;
@@ -95955,8 +95955,8 @@ var init_FabricManager = __esm({
95955
95955
  failsafeClosed: Observable()
95956
95956
  };
95957
95957
  #construction;
95958
- constructor(crypto7, storage2) {
95959
- this.#crypto = crypto7;
95958
+ constructor(crypto8, storage2) {
95959
+ this.#crypto = crypto8;
95960
95960
  this.#storage = storage2;
95961
95961
  let construct;
95962
95962
  if (this.#storage === void 0) {
@@ -95969,7 +95969,7 @@ var init_FabricManager = __esm({
95969
95969
  }
95970
95970
  const fabrics = await this.#storage.get("fabrics", []);
95971
95971
  for (const fabricConfig of fabrics) {
95972
- this.#addNewFabric(await Fabric.create(crypto7, fabricConfig));
95972
+ this.#addNewFabric(await Fabric.create(crypto8, fabricConfig));
95973
95973
  }
95974
95974
  this.#nextFabricIndex = await this.#storage.get("nextFabricIndex", this.#nextFabricIndex);
95975
95975
  this.#initializationDone = true;
@@ -96265,14 +96265,14 @@ var init_CaseServer = __esm({
96265
96265
  return false;
96266
96266
  }
96267
96267
  const { sharedSecret, fabric, peerNodeId, caseAuthenticatedTags } = cx.resumptionRecord;
96268
- const { crypto: crypto7 } = this.#fabrics;
96269
- const peerResumeKey = await crypto7.createHkdfKey(
96268
+ const { crypto: crypto8 } = this.#fabrics;
96269
+ const peerResumeKey = await crypto8.createHkdfKey(
96270
96270
  sharedSecret,
96271
96271
  Bytes.concat(cx.peerRandom, cx.peerResumptionId),
96272
96272
  KDFSR1_KEY_INFO
96273
96273
  );
96274
96274
  try {
96275
- crypto7.decrypt(peerResumeKey, cx.peerResumeMic, RESUME1_MIC_NONCE);
96275
+ crypto8.decrypt(peerResumeKey, cx.peerResumeMic, RESUME1_MIC_NONCE);
96276
96276
  } catch (e) {
96277
96277
  CryptoDecryptError.accept(e);
96278
96278
  cx.peerResumptionId = cx.peerResumeMic = void 0;
@@ -96296,8 +96296,8 @@ var init_CaseServer = __esm({
96296
96296
  // Session establishment could still fail, so add session ourselves to the manager
96297
96297
  });
96298
96298
  const resumeSalt = Bytes.concat(cx.peerRandom, cx.localResumptionId);
96299
- const resumeKey = await crypto7.createHkdfKey(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
96300
- const resumeMic = crypto7.encrypt(resumeKey, new Uint8Array(0), RESUME2_MIC_NONCE);
96299
+ const resumeKey = await crypto8.createHkdfKey(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
96300
+ const resumeMic = crypto8.encrypt(resumeKey, new Uint8Array(0), RESUME2_MIC_NONCE);
96301
96301
  try {
96302
96302
  const responderSessionParams = this.#sessions.sessionParameters;
96303
96303
  await cx.messenger.sendSigma2Resume({
@@ -96326,20 +96326,20 @@ var init_CaseServer = __esm({
96326
96326
  ) {
96327
96327
  return false;
96328
96328
  }
96329
- const { crypto: crypto7 } = this.#fabrics;
96330
- const responderRandom = crypto7.randomBytes(32);
96329
+ const { crypto: crypto8 } = this.#fabrics;
96330
+ const responderRandom = crypto8.randomBytes(32);
96331
96331
  const fabric = await this.#fabrics.findFabricFromDestinationId(cx.destinationId, cx.peerRandom);
96332
96332
  const { operationalCert: nodeOpCert, intermediateCACert, operationalIdentityProtectionKey } = fabric;
96333
- const key = await crypto7.createKeyPair();
96333
+ const key = await crypto8.createKeyPair();
96334
96334
  const responderEcdhPublicKey = key.publicBits;
96335
- const sharedSecret = await crypto7.generateDhSecret(key, PublicKey(cx.peerEcdhPublicKey));
96335
+ const sharedSecret = await crypto8.generateDhSecret(key, PublicKey(cx.peerEcdhPublicKey));
96336
96336
  const sigma2Salt = Bytes.concat(
96337
96337
  operationalIdentityProtectionKey,
96338
96338
  responderRandom,
96339
96339
  responderEcdhPublicKey,
96340
- await crypto7.computeHash(cx.bytes)
96340
+ await crypto8.computeHash(cx.bytes)
96341
96341
  );
96342
- const sigma2Key = await crypto7.createHkdfKey(sharedSecret, sigma2Salt, KDFSR2_INFO);
96342
+ const sigma2Key = await crypto8.createHkdfKey(sharedSecret, sigma2Salt, KDFSR2_INFO);
96343
96343
  const signatureData = TlvSignedData.encode({
96344
96344
  responderNoc: nodeOpCert,
96345
96345
  responderIcac: intermediateCACert,
@@ -96353,7 +96353,7 @@ var init_CaseServer = __esm({
96353
96353
  signature: signature.bytes,
96354
96354
  resumptionId: cx.localResumptionId
96355
96355
  });
96356
- const encrypted = crypto7.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);
96356
+ const encrypted = crypto8.encrypt(sigma2Key, encryptedData, TBE_DATA2_NONCE);
96357
96357
  const responderSessionId = await this.#sessions.getNextAvailableSessionId();
96358
96358
  const sigma2Bytes = await cx.messenger.sendSigma2({
96359
96359
  responderRandom,
@@ -96369,10 +96369,10 @@ var init_CaseServer = __esm({
96369
96369
  } = await cx.messenger.readSigma3();
96370
96370
  const sigma3Salt = Bytes.concat(
96371
96371
  operationalIdentityProtectionKey,
96372
- await crypto7.computeHash([cx.bytes, sigma2Bytes])
96372
+ await crypto8.computeHash([cx.bytes, sigma2Bytes])
96373
96373
  );
96374
- const sigma3Key = await crypto7.createHkdfKey(sharedSecret, sigma3Salt, KDFSR3_INFO);
96375
- const peerDecryptedData = crypto7.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);
96374
+ const sigma3Key = await crypto8.createHkdfKey(sharedSecret, sigma3Salt, KDFSR3_INFO);
96375
+ const peerDecryptedData = crypto8.decrypt(sigma3Key, peerEncrypted, TBE_DATA3_NONCE);
96376
96376
  const {
96377
96377
  responderNoc: peerNewOpCert,
96378
96378
  responderIcac: peerIntermediateCACert,
@@ -96392,10 +96392,10 @@ var init_CaseServer = __esm({
96392
96392
  if (fabric.fabricId !== peerFabricId) {
96393
96393
  throw new UnexpectedDataError(`Fabric ID mismatch: ${fabric.fabricId} !== ${peerFabricId}`);
96394
96394
  }
96395
- await crypto7.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, new EcdsaSignature(peerSignature));
96395
+ await crypto8.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, new EcdsaSignature(peerSignature));
96396
96396
  const secureSessionSalt = Bytes.concat(
96397
96397
  operationalIdentityProtectionKey,
96398
- await crypto7.computeHash([cx.bytes, sigma2Bytes, sigma3Bytes])
96398
+ await crypto8.computeHash([cx.bytes, sigma2Bytes, sigma3Bytes])
96399
96399
  );
96400
96400
  const secureSession = await this.#sessions.createSecureSession({
96401
96401
  channel,
@@ -96440,8 +96440,8 @@ var init_CaseServer = __esm({
96440
96440
  peerSessionParams;
96441
96441
  resumptionRecord;
96442
96442
  #localResumptionId;
96443
- constructor(crypto7, messenger, bytes, sigma1, resumptionRecord) {
96444
- this.crypto = crypto7;
96443
+ constructor(crypto8, messenger, bytes, sigma1, resumptionRecord) {
96444
+ this.crypto = crypto8;
96445
96445
  this.messenger = messenger;
96446
96446
  this.bytes = bytes;
96447
96447
  this.peerSessionId = sigma1.initiatorSessionId;
@@ -96937,25 +96937,25 @@ var init_PaseClient = __esm({
96937
96937
  constructor(sessions) {
96938
96938
  this.#sessions = sessions;
96939
96939
  }
96940
- static async generatePakePasscodeVerifier(crypto7, setupPinCode, pbkdfParameters) {
96941
- const { w0, L } = await Spake2p.computeW0L(crypto7, pbkdfParameters, setupPinCode);
96940
+ static async generatePakePasscodeVerifier(crypto8, setupPinCode, pbkdfParameters) {
96941
+ const { w0, L } = await Spake2p.computeW0L(crypto8, pbkdfParameters, setupPinCode);
96942
96942
  return Bytes.concat(numberToBytesBE3(w0, 32), L);
96943
96943
  }
96944
- static generateRandomPasscode(crypto7) {
96944
+ static generateRandomPasscode(crypto8) {
96945
96945
  let passcode;
96946
- passcode = crypto7.randomUint32 % 99999998 + 1;
96946
+ passcode = crypto8.randomUint32 % 99999998 + 1;
96947
96947
  if (CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode)) {
96948
96948
  passcode += 1;
96949
96949
  }
96950
96950
  return passcode;
96951
96951
  }
96952
- static generateRandomDiscriminator(crypto7) {
96953
- return crypto7.randomUint16 % 4096;
96952
+ static generateRandomDiscriminator(crypto8) {
96953
+ return crypto8.randomUint16 % 4096;
96954
96954
  }
96955
96955
  async pair(initiatorSessionParams, exchange, channel, setupPin) {
96956
96956
  const messenger = new PaseClientMessenger(exchange);
96957
- const { crypto: crypto7 } = this.#sessions;
96958
- const initiatorRandom = crypto7.randomBytes(32);
96957
+ const { crypto: crypto8 } = this.#sessions;
96958
+ const initiatorRandom = crypto8.randomBytes(32);
96959
96959
  const initiatorSessionId = await this.#sessions.getNextAvailableSessionId();
96960
96960
  const requestPayload = await messenger.sendPbkdfParamRequest({
96961
96961
  initiatorRandom,
@@ -96979,10 +96979,10 @@ var init_PaseClient = __esm({
96979
96979
  ...exchange.session.parameters,
96980
96980
  ...responderSessionParams ?? {}
96981
96981
  };
96982
- const { w0, w1 } = await Spake2p.computeW0W1(crypto7, pbkdfParameters, setupPin);
96982
+ const { w0, w1 } = await Spake2p.computeW0W1(crypto8, pbkdfParameters, setupPin);
96983
96983
  const spake2p = Spake2p.create(
96984
- crypto7,
96985
- await crypto7.computeHash([SPAKE_CONTEXT, requestPayload, responsePayload]),
96984
+ crypto8,
96985
+ await crypto8.computeHash([SPAKE_CONTEXT, requestPayload, responsePayload]),
96986
96986
  w0
96987
96987
  );
96988
96988
  const X = spake2p.computeX();
@@ -97093,7 +97093,7 @@ var init_PaseServer = __esm({
97093
97093
  }
97094
97094
  }
97095
97095
  }
97096
- async handlePairingRequest(crypto7, channel) {
97096
+ async handlePairingRequest(crypto8, channel) {
97097
97097
  const messenger = this.#pairingMessenger;
97098
97098
  logger40.info("Received pairing request", Mark.INBOUND, Diagnostic.via(messenger.channelName));
97099
97099
  this.#pairingTimer = Time.getTimer(
@@ -97115,7 +97115,7 @@ var init_PaseServer = __esm({
97115
97115
  throw new UnexpectedDataError(`Unsupported passcode ID ${passcodeId}.`);
97116
97116
  }
97117
97117
  const responderSessionId = await this.sessions.getNextAvailableSessionId();
97118
- const responderRandom = crypto7.randomBytes(32);
97118
+ const responderRandom = crypto8.randomBytes(32);
97119
97119
  const responderSessionParams = this.sessions.sessionParameters;
97120
97120
  if (initiatorSessionParams !== void 0) {
97121
97121
  messenger.channel.session.timingParameters = initiatorSessionParams;
@@ -97128,8 +97128,8 @@ var init_PaseServer = __esm({
97128
97128
  responderSessionParams
97129
97129
  });
97130
97130
  const spake2p = Spake2p.create(
97131
- crypto7,
97132
- await crypto7.computeHash([SPAKE_CONTEXT, requestPayload, responsePayload]),
97131
+ crypto8,
97132
+ await crypto8.computeHash([SPAKE_CONTEXT, requestPayload, responsePayload]),
97133
97133
  this.w0
97134
97134
  );
97135
97135
  const { x: X } = await messenger.readPasePake1();
@@ -97197,14 +97197,14 @@ var init_UnsecuredSession = __esm({
97197
97197
  supportsMRP = true;
97198
97198
  type = SessionType.Unicast;
97199
97199
  constructor(config10) {
97200
- const { crypto: crypto7, initiatorNodeId, isInitiator } = config10;
97200
+ const { crypto: crypto8, initiatorNodeId, isInitiator } = config10;
97201
97201
  super({
97202
97202
  ...config10,
97203
97203
  setActiveTimestamp: !isInitiator,
97204
97204
  // When we are the initiator we assume the node is in idle mode
97205
97205
  messageReceptionState: new MessageReceptionStateUnencryptedWithRollover()
97206
97206
  });
97207
- this.#initiatorNodeId = initiatorNodeId ?? NodeId.randomOperationalNodeId(crypto7);
97207
+ this.#initiatorNodeId = initiatorNodeId ?? NodeId.randomOperationalNodeId(crypto8);
97208
97208
  }
97209
97209
  get isSecure() {
97210
97210
  return false;
@@ -97291,11 +97291,11 @@ var init_SessionManager = __esm({
97291
97291
  constructor(context) {
97292
97292
  this.#context = context;
97293
97293
  const {
97294
- fabrics: { crypto: crypto7 }
97294
+ fabrics: { crypto: crypto8 }
97295
97295
  } = context;
97296
97296
  this.#sessionParameters = SessionParameters({ ...SessionParameters.defaults, ...context.parameters });
97297
- this.#nextSessionId = crypto7.randomUint16;
97298
- this.#globalUnencryptedMessageCounter = new MessageCounter(crypto7);
97297
+ this.#nextSessionId = crypto8.randomUint16;
97298
+ this.#globalUnencryptedMessageCounter = new MessageCounter(crypto8);
97299
97299
  this.#observers.on(context.fabrics.events.deleting, async (fabric) => {
97300
97300
  await this.deleteResumptionRecordsForFabric(fabric);
97301
97301
  });
@@ -100871,18 +100871,18 @@ var init_ProtocolMocks = __esm({
100871
100871
  init_MessageExchange();
100872
100872
  ((ProtocolMocks2) => {
100873
100873
  class Fabric2 extends Fabric {
100874
- constructor(config10, crypto7) {
100875
- if (!crypto7) {
100876
- crypto7 = Environment.default.maybeGet(Crypto);
100877
- if (!(crypto7 instanceof MockCrypto)) {
100878
- crypto7 = MockCrypto();
100874
+ constructor(config10, crypto8) {
100875
+ if (!crypto8) {
100876
+ crypto8 = Environment.default.maybeGet(Crypto);
100877
+ if (!(crypto8 instanceof MockCrypto)) {
100878
+ crypto8 = MockCrypto();
100879
100879
  }
100880
100880
  }
100881
- const keyPair = config10?.keyPair ?? crypto7.createKeyPair();
100881
+ const keyPair = config10?.keyPair ?? crypto8.createKeyPair();
100882
100882
  if (MaybePromise.is(keyPair)) {
100883
100883
  throw new ImplementationError("Must provide key pair with async crypto");
100884
100884
  }
100885
- super(crypto7, {
100885
+ super(crypto8, {
100886
100886
  ...Fabric2.defaults,
100887
100887
  ...config10,
100888
100888
  keyPair
@@ -100912,7 +100912,7 @@ var init_ProtocolMocks = __esm({
100912
100912
  constructor(config10) {
100913
100913
  const index = config10?.index ?? 1;
100914
100914
  const fabricIndex = config10?.fabricIndex ?? 1;
100915
- const crypto7 = config10?.crypto ?? Environment.default.get(Crypto);
100915
+ const crypto8 = config10?.crypto ?? Environment.default.get(Crypto);
100916
100916
  const fabric = config10 && "fabric" in config10 ? config10.fabric : new Fabric2({ fabricIndex });
100917
100917
  const maxPayloadSize = config10?.maxPayloadSize;
100918
100918
  let channel;
@@ -100931,7 +100931,7 @@ var init_ProtocolMocks = __esm({
100931
100931
  encryptKey: Bytes.empty,
100932
100932
  isInitiator: true,
100933
100933
  ...config10,
100934
- crypto: crypto7,
100934
+ crypto: crypto8,
100935
100935
  fabric
100936
100936
  };
100937
100937
  delete fullConfig.channel;
@@ -100940,11 +100940,11 @@ var init_ProtocolMocks = __esm({
100940
100940
  this.lifetime = Lifetime.mock;
100941
100941
  }
100942
100942
  static async create(config10) {
100943
- const crypto7 = config10?.crypto ?? config10?.manager?.crypto ?? Environment.default.get(Crypto);
100943
+ const crypto8 = config10?.crypto ?? config10?.manager?.crypto ?? Environment.default.get(Crypto);
100944
100944
  const index = config10?.index ?? 1;
100945
100945
  return NodeSession.create.call(this, {
100946
100946
  id: index,
100947
- crypto: crypto7,
100947
+ crypto: crypto8,
100948
100948
  peerNodeId: NodeId(0),
100949
100949
  peerSessionId: index,
100950
100950
  sharedSecret: Bytes.empty,
@@ -106671,9 +106671,9 @@ var init_MdnsAdvertiser = __esm({
106671
106671
  init_CommissionerMdnsAdvertisement();
106672
106672
  init_OperationalMdnsAdvertisement();
106673
106673
  MdnsAdvertiser = class _MdnsAdvertiser extends Advertiser {
106674
- constructor(crypto7, server, options) {
106674
+ constructor(crypto8, server, options) {
106675
106675
  super(options?.lifetime);
106676
- this.crypto = crypto7;
106676
+ this.crypto = crypto8;
106677
106677
  this.server = server;
106678
106678
  this.port = options?.port ?? STANDARD_MATTER_PORT;
106679
106679
  this.omitPrivateDetails = options?.omitPrivateDetails ?? false;
@@ -108118,8 +108118,8 @@ var init_AttestationCertificates = __esm({
108118
108118
  * Sign the certificate using the provided crypto and key.
108119
108119
  * If the certificate is already signed, it throws a CertificateError.
108120
108120
  */
108121
- async sign(crypto7, key) {
108122
- this.signature = await crypto7.signEcdsa(key, this.asUnsignedDer());
108121
+ async sign(crypto8, key) {
108122
+ this.signature = await crypto8.signEcdsa(key, this.asUnsignedDer());
108123
108123
  }
108124
108124
  };
108125
108125
  Paa = class _Paa extends AttestationBaseCertificate {
@@ -108168,17 +108168,17 @@ var init_AttestationCertificateManager = __esm({
108168
108168
  #paiCertId = BigInt(1);
108169
108169
  #paiCertBytes;
108170
108170
  #nextCertificateId = 2;
108171
- constructor(crypto7, vendorId3, paiKeyPair, paiKeyIdentifier) {
108172
- this.#crypto = crypto7;
108171
+ constructor(crypto8, vendorId3, paiKeyPair, paiKeyIdentifier) {
108172
+ this.#crypto = crypto8;
108173
108173
  this.#vendorId = vendorId3;
108174
108174
  this.#paiKeyPair = paiKeyPair;
108175
108175
  this.#paiKeyIdentifier = paiKeyIdentifier;
108176
108176
  this.#paiCertBytes = this.generatePAICert(vendorId3);
108177
108177
  }
108178
- static async create(crypto7, vendorId3) {
108179
- const key = await crypto7.createKeyPair();
108180
- const identifier = Bytes.of(await crypto7.computeHash(key.publicKey));
108181
- return new _AttestationCertificateManager(crypto7, vendorId3, key, identifier.slice(0, 20));
108178
+ static async create(crypto8, vendorId3) {
108179
+ const key = await crypto8.createKeyPair();
108180
+ const identifier = Bytes.of(await crypto8.computeHash(key.publicKey));
108181
+ return new _AttestationCertificateManager(crypto8, vendorId3, key, identifier.slice(0, 20));
108182
108182
  }
108183
108183
  getPAICert() {
108184
108184
  return this.#paiCertBytes;
@@ -108324,11 +108324,11 @@ var init_CertificateAuthority = __esm({
108324
108324
  get construction() {
108325
108325
  return this.#construction;
108326
108326
  }
108327
- static async create(crypto7, options, generateIntermediateCert) {
108328
- return asyncNew(_CertificateAuthority, crypto7, options, generateIntermediateCert);
108327
+ static async create(crypto8, options, generateIntermediateCert) {
108328
+ return asyncNew(_CertificateAuthority, crypto8, options, generateIntermediateCert);
108329
108329
  }
108330
- constructor(crypto7, options, generateIntermediateCert) {
108331
- this.#crypto = crypto7;
108330
+ constructor(crypto8, options, generateIntermediateCert) {
108331
+ this.#crypto = crypto8;
108332
108332
  this.#construction = Construction(this, async () => {
108333
108333
  if (typeof options === "boolean") {
108334
108334
  generateIntermediateCert = options;
@@ -108610,7 +108610,7 @@ var init_CertificationDeclaration = __esm({
108610
108610
  * Generator which is the main usage for the class from outside.
108611
108611
  * It constructs the class with the relevant details and returns a signed ASN.1 DER version of the CD.
108612
108612
  */
108613
- static generate(crypto7, vendorId3, productId, provisional = false) {
108613
+ static generate(crypto8, vendorId3, productId, provisional = false) {
108614
108614
  const cd = new _CertificationDeclaration(
108615
108615
  {
108616
108616
  formatVersion: 1,
@@ -108626,7 +108626,7 @@ var init_CertificationDeclaration = __esm({
108626
108626
  },
108627
108627
  TestCMS_SignerSubjectKeyIdentifier
108628
108628
  );
108629
- return cd.asSignedAsn1(crypto7, PrivateKey(TestCMS_SignerPrivateKey));
108629
+ return cd.asSignedAsn1(crypto8, PrivateKey(TestCMS_SignerPrivateKey));
108630
108630
  }
108631
108631
  constructor(content, subjectKeyIdentifier) {
108632
108632
  this.#eContent = CertificationDeclaration.TlvDc.encode(content);
@@ -108635,7 +108635,7 @@ var init_CertificationDeclaration = __esm({
108635
108635
  /**
108636
108636
  * Returns the signed certificate in ASN.1 DER format.
108637
108637
  */
108638
- async asSignedAsn1(crypto7, privateKey) {
108638
+ async asSignedAsn1(crypto8, privateKey) {
108639
108639
  const cert = {
108640
108640
  version: 3,
108641
108641
  digestAlgorithm: [Shs.SHA256_CMS],
@@ -108646,7 +108646,7 @@ var init_CertificationDeclaration = __esm({
108646
108646
  subjectKeyIdentifier: ContextTaggedBytes(0, this.#subjectKeyIdentifier),
108647
108647
  digestAlgorithm: Shs.SHA256_CMS,
108648
108648
  signatureAlgorithm: X962.EcdsaWithSHA256,
108649
- signature: (await crypto7.signEcdsa(privateKey, this.#eContent)).der
108649
+ signature: (await crypto8.signEcdsa(privateKey, this.#eContent)).der
108650
108650
  }
108651
108651
  ]
108652
108652
  };
@@ -108684,8 +108684,8 @@ var init_DeviceCertification = __esm({
108684
108684
  get declaration() {
108685
108685
  return this.#assertInitialized().declaration;
108686
108686
  }
108687
- constructor(crypto7, config10, product) {
108688
- this.#crypto = crypto7;
108687
+ constructor(crypto8, config10, product) {
108688
+ this.#crypto = crypto8;
108689
108689
  let configProvider;
108690
108690
  if (typeof config10 === "function") {
108691
108691
  configProvider = config10;
@@ -108696,13 +108696,13 @@ var init_DeviceCertification = __esm({
108696
108696
  if (product === void 0) {
108697
108697
  throw new ImplementationError(`Cannot generate device certification without product information`);
108698
108698
  }
108699
- const paa = await AttestationCertificateManager.create(crypto7, product.vendorId);
108699
+ const paa = await AttestationCertificateManager.create(crypto8, product.vendorId);
108700
108700
  const { keyPair: dacKeyPair, dac } = await paa.getDACert(product.productId);
108701
108701
  return {
108702
108702
  privateKey: PrivateKey(dacKeyPair.privateKey),
108703
108703
  certificate: dac,
108704
108704
  intermediateCertificate: await paa.getPAICert(),
108705
- declaration: await CertificationDeclaration2.generate(crypto7, product.vendorId, product.productId)
108705
+ declaration: await CertificationDeclaration2.generate(crypto8, product.vendorId, product.productId)
108706
108706
  };
108707
108707
  };
108708
108708
  }
@@ -109493,8 +109493,8 @@ var init_OtaImageReader = __esm({
109493
109493
  return reader.#headerData;
109494
109494
  }
109495
109495
  /** Read and validate the full OTA image file from the stream and returns the header data on success. */
109496
- static async file(streamReader, crypto7, expectedTotalSize, options) {
109497
- const reader = new _OtaImageReader(streamReader, crypto7);
109496
+ static async file(streamReader, crypto8, expectedTotalSize, options) {
109497
+ const reader = new _OtaImageReader(streamReader, crypto8);
109498
109498
  if (options?.checksumType !== void 0) {
109499
109499
  reader.#fullFileChecksumType = options.checksumType;
109500
109500
  }
@@ -109528,8 +109528,8 @@ var init_OtaImageReader = __esm({
109528
109528
  * Read and validate OTA file, extracting payload to a writable stream.
109529
109529
  * Returns the header information after successful validation and extraction.
109530
109530
  */
109531
- static async extractPayload(streamReader, payloadWriter, crypto7, expectedTotalSize) {
109532
- const reader = new _OtaImageReader(streamReader, crypto7);
109531
+ static async extractPayload(streamReader, payloadWriter, crypto8, expectedTotalSize) {
109532
+ const reader = new _OtaImageReader(streamReader, crypto8);
109533
109533
  const { remainingData } = await reader.#processHeader(false);
109534
109534
  if (reader.#headerData === void 0) {
109535
109535
  throw new InternalError("OTA header not read");
@@ -109545,9 +109545,9 @@ var init_OtaImageReader = __esm({
109545
109545
  }
109546
109546
  return reader.#headerData;
109547
109547
  }
109548
- constructor(streamReader, crypto7) {
109548
+ constructor(streamReader, crypto8) {
109549
109549
  this.#streamReader = streamReader;
109550
- this.#crypto = crypto7;
109550
+ this.#crypto = crypto8;
109551
109551
  }
109552
109552
  get totalSize() {
109553
109553
  return this.#totalSize;
@@ -125833,8 +125833,8 @@ var init_VendorIdVerification = __esm({
125833
125833
  }
125834
125834
  VendorIdVerification2.dataToSign = dataToSign;
125835
125835
  async function verify(node, options) {
125836
- const crypto7 = node.env.get(Crypto);
125837
- const clientChallenge = crypto7.randomBytes(32);
125836
+ const crypto8 = node.env.get(Crypto);
125837
+ const clientChallenge = crypto8.randomBytes(32);
125838
125838
  const {
125839
125839
  fabric: { fabricIndex }
125840
125840
  } = options;
@@ -125857,7 +125857,7 @@ var init_VendorIdVerification = __esm({
125857
125857
  return void 0;
125858
125858
  }
125859
125859
  const { noc, rcac, fabric } = options;
125860
- return await verifyData(crypto7, {
125860
+ return await verifyData(crypto8, {
125861
125861
  clientChallenge,
125862
125862
  attChallenge: session.attestationChallengeKey,
125863
125863
  signVerificationResponse,
@@ -125867,7 +125867,7 @@ var init_VendorIdVerification = __esm({
125867
125867
  });
125868
125868
  }
125869
125869
  VendorIdVerification2.verify = verify;
125870
- async function verifyData(crypto7, options) {
125870
+ async function verifyData(crypto8, options) {
125871
125871
  const {
125872
125872
  clientChallenge,
125873
125873
  attChallenge,
@@ -125893,14 +125893,14 @@ var init_VendorIdVerification = __esm({
125893
125893
  }
125894
125894
  }).signatureData;
125895
125895
  try {
125896
- await crypto7.verifyEcdsa(PublicKey(rootPublicKey), tbs, new EcdsaSignature(signature));
125896
+ await crypto8.verifyEcdsa(PublicKey(rootPublicKey), tbs, new EcdsaSignature(signature));
125897
125897
  const rootCert = Rcac.fromTlv(rcac);
125898
125898
  const nocCert = Noc.fromTlv(noc);
125899
125899
  const icaCert = icac ? Icac.fromTlv(icac) : void 0;
125900
125900
  if (icaCert !== void 0) {
125901
- await icaCert.verify(crypto7, rootCert);
125901
+ await icaCert.verify(crypto8, rootCert);
125902
125902
  }
125903
- await nocCert.verify(crypto7, rootCert, icaCert);
125903
+ await nocCert.verify(crypto8, rootCert, icaCert);
125904
125904
  } catch (error) {
125905
125905
  CryptoError.accept(error);
125906
125906
  logger103.error("Could not verify VendorId", error);
@@ -125930,13 +125930,13 @@ var init_VendorIdVerification = __esm({
125930
125930
  `VVSC SubjectKeyIdentifier does not match signerSkid in VID Verification Statement`
125931
125931
  );
125932
125932
  }
125933
- await vvscCert.verify(crypto7);
125933
+ await vvscCert.verify(crypto8);
125934
125934
  const ourStatement = createStatementBytes({
125935
125935
  version: vidStatementVersion,
125936
125936
  fabricBindingMessage: tbs,
125937
125937
  signerSkid
125938
125938
  });
125939
- await crypto7.verifyEcdsa(
125939
+ await crypto8.verifyEcdsa(
125940
125940
  PublicKey(ellipticCurvePublicKey),
125941
125941
  ourStatement,
125942
125942
  new EcdsaSignature(vidStatementSignature)
@@ -130205,8 +130205,8 @@ var init_OtaSoftwareUpdateProviderServer = __esm({
130205
130205
  // the usual bdx session timeout is 5 minutes, so let's use this
130206
130206
  };
130207
130207
  }
130208
- const crypto7 = this.env.get(Crypto);
130209
- const updateToken = crypto7.randomBytes(OTA_UPDATE_TOKEN_LENGTH_BYTES);
130208
+ const crypto8 = this.env.get(Crypto);
130209
+ const updateToken = crypto8.randomBytes(OTA_UPDATE_TOKEN_LENGTH_BYTES);
130210
130210
  if (consentRequired && !request.requestorCanConsent) {
130211
130211
  this.#updateInProgressDetails(peerAddress, updateToken, OtaUpdateStatus.WaitForConsent, newSoftwareVersion);
130212
130212
  const { consentState, delayTime = Seconds(120) } = await this.requestUserConsentForUpdate(
@@ -132681,8 +132681,8 @@ var init_OtaSoftwareUpdateRequestorServer = __esm({
132681
132681
  try {
132682
132682
  const blob = await this.downloadLocation.openBlob();
132683
132683
  totalSize = blob.size;
132684
- const crypto7 = this.env.get(Crypto);
132685
- const header = await OtaImageReader.file(blob.stream().getReader(), crypto7);
132684
+ const crypto8 = this.env.get(Crypto);
132685
+ const header = await OtaImageReader.file(blob.stream().getReader(), crypto8);
132686
132686
  const { softwareVersion: otaFileSoftwareVersion } = header;
132687
132687
  if (newSoftwareVersion === void 0) {
132688
132688
  const { softwareVersion: currentSoftwareVersion } = this.#basicInformationState();
@@ -135998,11 +135998,11 @@ var init_Api = __esm({
135998
135998
  }
135999
135999
  Api2.resourceFor = resourceFor;
136000
136000
  function log(level, facility, id, ...message) {
136001
- let logger203 = loggers.get(facility);
136002
- if (!logger203) {
136003
- loggers.set(facility, logger203 = Logger.get(facility));
136001
+ let logger204 = loggers.get(facility);
136002
+ if (!logger204) {
136003
+ loggers.set(facility, logger204 = Logger.get(facility));
136004
136004
  }
136005
- logger203[level](Diagnostic.via(id || "(anon)"), message);
136005
+ logger204[level](Diagnostic.via(id || "(anon)"), message);
136006
136006
  }
136007
136007
  Api2.log = log;
136008
136008
  function logRequest(facility, id, method, target) {
@@ -141853,9 +141853,9 @@ var init_ServerNetworkRuntime = __esm({
141853
141853
  lifetime: this.construction,
141854
141854
  ...this.owner.state.commissioning.mdns
141855
141855
  };
141856
- const crypto7 = this.owner.env.get(Crypto);
141856
+ const crypto8 = this.owner.env.get(Crypto);
141857
141857
  const { server } = this.#services.get(MdnsService);
141858
- this.#mdnsAdvertiser = new MdnsAdvertiser(crypto7, server, { ...options, port });
141858
+ this.#mdnsAdvertiser = new MdnsAdvertiser(crypto8, server, { ...options, port });
141859
141859
  }
141860
141860
  return this.#mdnsAdvertiser;
141861
141861
  }
@@ -145421,14 +145421,14 @@ function rootDirOf(env) {
145421
145421
  function configureCrypto(env) {
145422
145422
  Boot.init(() => {
145423
145423
  if (env.vars.boolean("nodejs.crypto")) {
145424
- let crypto7;
145424
+ let crypto8;
145425
145425
  if (!isBunjs()) {
145426
- crypto7 = new NodeJsCrypto();
145426
+ crypto8 = new NodeJsCrypto();
145427
145427
  } else {
145428
- crypto7 = new StandardCrypto(global.crypto);
145428
+ crypto8 = new StandardCrypto(global.crypto);
145429
145429
  }
145430
- env.set(Entropy, crypto7);
145431
- env.set(Crypto, crypto7);
145430
+ env.set(Entropy, crypto8);
145431
+ env.set(Crypto, crypto8);
145432
145432
  return;
145433
145433
  }
145434
145434
  if (Environment.default.has(Entropy)) {
@@ -146411,6 +146411,7 @@ var init_vacuum = __esm({
146411
146411
  VacuumDeviceFeature2[VacuumDeviceFeature2["MAP"] = 2048] = "MAP";
146412
146412
  VacuumDeviceFeature2[VacuumDeviceFeature2["STATE"] = 4096] = "STATE";
146413
146413
  VacuumDeviceFeature2[VacuumDeviceFeature2["START"] = 8192] = "START";
146414
+ VacuumDeviceFeature2[VacuumDeviceFeature2["CLEAN_AREA"] = 16384] = "CLEAN_AREA";
146414
146415
  })(VacuumDeviceFeature || (VacuumDeviceFeature = {}));
146415
146416
  (function(VacuumFanSpeed2) {
146416
146417
  VacuumFanSpeed2["off"] = "off";
@@ -147220,10 +147221,10 @@ var init_home_assistant_actions = __esm({
147220
147221
  circuitBreakerResetMs: 3e4
147221
147222
  };
147222
147223
  HomeAssistantActions = class extends Service {
147223
- constructor(logger203, client, config10) {
147224
+ constructor(logger204, client, config10) {
147224
147225
  super("HomeAssistantActions");
147225
147226
  this.client = client;
147226
- this.log = logger203.get(this);
147227
+ this.log = logger204.get(this);
147227
147228
  this.config = { ...defaultConfig, ...config10 };
147228
147229
  this.circuitBreaker = new CircuitBreaker(
147229
147230
  this.config.circuitBreakerThreshold,
@@ -147566,7 +147567,7 @@ var DiagnosticService = class {
147566
147567
  }
147567
147568
  }
147568
147569
  }
147569
- const entities = this.collectEntities(bridge.aggregator);
147570
+ const entities = bridge.aggregator ? this.collectEntities(bridge.aggregator) : [];
147570
147571
  return {
147571
147572
  bridgeId: data.id,
147572
147573
  bridgeName: data.name,
@@ -147595,10 +147596,10 @@ var DiagnosticService = class {
147595
147596
  };
147596
147597
 
147597
147598
  // src/api/access-log.ts
147598
- function accessLogger(logger203) {
147599
+ function accessLogger(logger204) {
147599
147600
  return (req, res, next) => {
147600
147601
  res.on("finish", () => {
147601
- logger203.debug(
147602
+ logger204.debug(
147602
147603
  `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}`
147603
147604
  );
147604
147605
  });
@@ -147765,6 +147766,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147765
147766
  disabled: config10.disabled,
147766
147767
  filterLifeEntity: config10.filterLifeEntity,
147767
147768
  cleaningModeEntity: config10.cleaningModeEntity,
147769
+ temperatureEntity: config10.temperatureEntity,
147768
147770
  humidityEntity: config10.humidityEntity,
147769
147771
  pressureEntity: config10.pressureEntity,
147770
147772
  batteryEntity: config10.batteryEntity,
@@ -147773,7 +147775,9 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147773
147775
  powerEntity: config10.powerEntity,
147774
147776
  energyEntity: config10.energyEntity,
147775
147777
  suctionLevelEntity: config10.suctionLevelEntity,
147776
- mopIntensityEntity: config10.mopIntensityEntity
147778
+ mopIntensityEntity: config10.mopIntensityEntity,
147779
+ valetudoIdentifier: config10.valetudoIdentifier,
147780
+ coverSwapOpenClose: config10.coverSwapOpenClose
147777
147781
  });
147778
147782
  mappingsRestored++;
147779
147783
  }
@@ -148512,6 +148516,15 @@ function logsApi(_logger) {
148512
148516
  }
148513
148517
 
148514
148518
  // src/api/diagnostic-api.ts
148519
+ function endpointTreeNode(ep) {
148520
+ return {
148521
+ id: ep.id,
148522
+ endpoint: ep.number,
148523
+ deviceType: `0x${ep.type.deviceType.toString(16).padStart(4, "0")}`,
148524
+ deviceTypeName: ep.type.name,
148525
+ parts: [...ep.parts].map((p) => endpointTreeNode(p))
148526
+ };
148527
+ }
148515
148528
  function detectEnvironment() {
148516
148529
  if (process.env.SUPERVISOR_TOKEN || process.env.HASSIO_TOKEN) {
148517
148530
  return "Home Assistant Add-on";
@@ -148586,7 +148599,14 @@ function diagnosticApi(bridgeService, haClient, haRegistry, version, startTime)
148586
148599
  failedEntities: failedEntities.map((fe) => ({
148587
148600
  entityId: anonymize ? anonymizeEntityId(fe.entityId) : fe.entityId,
148588
148601
  reason: fe.reason
148589
- }))
148602
+ })),
148603
+ endpointTree: (() => {
148604
+ try {
148605
+ return endpointTreeNode(b.server);
148606
+ } catch {
148607
+ return void 0;
148608
+ }
148609
+ })()
148590
148610
  };
148591
148611
  });
148592
148612
  const recentLogs = logBuffer.entries.slice(-logLimit).map((entry) => ({
@@ -148658,6 +148678,7 @@ function entityMappingApi(mappingStorage) {
148658
148678
  disabled: body.disabled,
148659
148679
  filterLifeEntity: body.filterLifeEntity,
148660
148680
  cleaningModeEntity: body.cleaningModeEntity,
148681
+ temperatureEntity: body.temperatureEntity,
148661
148682
  humidityEntity: body.humidityEntity,
148662
148683
  pressureEntity: body.pressureEntity,
148663
148684
  batteryEntity: body.batteryEntity,
@@ -148668,7 +148689,10 @@ function entityMappingApi(mappingStorage) {
148668
148689
  suctionLevelEntity: body.suctionLevelEntity,
148669
148690
  mopIntensityEntity: body.mopIntensityEntity,
148670
148691
  customServiceAreas: body.customServiceAreas,
148671
- customFanSpeedTags: body.customFanSpeedTags
148692
+ customFanSpeedTags: body.customFanSpeedTags,
148693
+ valetudoIdentifier: body.valetudoIdentifier,
148694
+ coverSwapOpenClose: body.coverSwapOpenClose,
148695
+ composedEntities: body.composedEntities
148672
148696
  };
148673
148697
  const config10 = await mappingStorage.setMapping(request);
148674
148698
  res.status(200).json(config10);
@@ -149118,6 +149142,7 @@ function configToProfileEntry(config10) {
149118
149142
  disabled: config10.disabled,
149119
149143
  filterLifeEntity: config10.filterLifeEntity,
149120
149144
  cleaningModeEntity: config10.cleaningModeEntity,
149145
+ temperatureEntity: config10.temperatureEntity,
149121
149146
  humidityEntity: config10.humidityEntity,
149122
149147
  pressureEntity: config10.pressureEntity,
149123
149148
  batteryEntity: config10.batteryEntity,
@@ -149128,7 +149153,9 @@ function configToProfileEntry(config10) {
149128
149153
  suctionLevelEntity: config10.suctionLevelEntity,
149129
149154
  mopIntensityEntity: config10.mopIntensityEntity,
149130
149155
  customServiceAreas: config10.customServiceAreas,
149131
- customFanSpeedTags: config10.customFanSpeedTags
149156
+ customFanSpeedTags: config10.customFanSpeedTags,
149157
+ valetudoIdentifier: config10.valetudoIdentifier,
149158
+ coverSwapOpenClose: config10.coverSwapOpenClose
149132
149159
  };
149133
149160
  }
149134
149161
  function mappingProfileApi(mappingStorage) {
@@ -149236,6 +149263,7 @@ function mappingProfileApi(mappingStorage) {
149236
149263
  disabled: entry.disabled,
149237
149264
  filterLifeEntity: entry.filterLifeEntity,
149238
149265
  cleaningModeEntity: entry.cleaningModeEntity,
149266
+ temperatureEntity: entry.temperatureEntity,
149239
149267
  humidityEntity: entry.humidityEntity,
149240
149268
  pressureEntity: entry.pressureEntity,
149241
149269
  batteryEntity: entry.batteryEntity,
@@ -149246,7 +149274,9 @@ function mappingProfileApi(mappingStorage) {
149246
149274
  suctionLevelEntity: entry.suctionLevelEntity,
149247
149275
  mopIntensityEntity: entry.mopIntensityEntity,
149248
149276
  customServiceAreas: entry.customServiceAreas,
149249
- customFanSpeedTags: entry.customFanSpeedTags
149277
+ customFanSpeedTags: entry.customFanSpeedTags,
149278
+ valetudoIdentifier: entry.valetudoIdentifier,
149279
+ coverSwapOpenClose: entry.coverSwapOpenClose
149250
149280
  });
149251
149281
  applied++;
149252
149282
  } catch (e) {
@@ -149283,6 +149313,9 @@ function testMatchers(matchers, device, entity, mode = "any", entityState, label
149283
149313
  );
149284
149314
  }
149285
149315
  function testMatcher(matcher, device, entity, entityState, labels) {
149316
+ if (matcher.value == null) {
149317
+ return false;
149318
+ }
149286
149319
  switch (matcher.type) {
149287
149320
  case "domain":
149288
149321
  return entity.entity_id.split(".")[0] === matcher.value;
@@ -151079,7 +151112,7 @@ var WebSocketApi = class {
151079
151112
 
151080
151113
  // src/api/web-api.ts
151081
151114
  var WebApi = class extends Service {
151082
- constructor(logger203, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151115
+ constructor(logger204, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151083
151116
  super("WebApi");
151084
151117
  this.bridgeService = bridgeService;
151085
151118
  this.haClient = haClient;
@@ -151090,8 +151123,8 @@ var WebApi = class extends Service {
151090
151123
  this.settingsStorage = settingsStorage;
151091
151124
  this.backupService = backupService;
151092
151125
  this.props = props;
151093
- this.logger = logger203;
151094
- this.log = logger203.get(this);
151126
+ this.logger = logger204;
151127
+ this.log = logger204.get(this);
151095
151128
  this.accessLogger = accessLogger(this.log.createChild("Access Log"));
151096
151129
  this.startTime = Date.now();
151097
151130
  this.wsApi = new WebSocketApi(
@@ -151529,12 +151562,12 @@ var CustomStorage = class extends StorageBackendDisk {
151529
151562
 
151530
151563
  // src/core/app/storage.ts
151531
151564
  function storage(environment, options) {
151532
- const logger203 = environment.get(LoggerService).get("CustomStorage");
151565
+ const logger204 = environment.get(LoggerService).get("CustomStorage");
151533
151566
  const location2 = resolveStorageLocation(options.location);
151534
151567
  fs8.mkdirSync(location2, { recursive: true });
151535
151568
  const storageService = environment.get(StorageService);
151536
151569
  storageService.location = location2;
151537
- storageService.factory = (ns) => new CustomStorage(logger203, path8.resolve(location2, ns));
151570
+ storageService.factory = (ns) => new CustomStorage(logger204, path8.resolve(location2, ns));
151538
151571
  }
151539
151572
  function resolveStorageLocation(storageLocation) {
151540
151573
  const homedir = os3.homedir();
@@ -151560,17 +151593,17 @@ import { createRequire } from "node:module";
151560
151593
  import os4 from "node:os";
151561
151594
  import path9 from "node:path";
151562
151595
  function resolveAppVersion() {
151563
- if (process.env.APP_VERSION) {
151564
- return process.env.APP_VERSION;
151565
- }
151566
151596
  try {
151567
151597
  const require2 = createRequire(import.meta.url);
151568
151598
  const pkg = require2("home-assistant-matter-hub/package.json");
151569
- if (pkg.version) {
151599
+ if (pkg.version && pkg.version !== "0.0.0") {
151570
151600
  return pkg.version;
151571
151601
  }
151572
151602
  } catch {
151573
151603
  }
151604
+ if (process.env.APP_VERSION) {
151605
+ return process.env.APP_VERSION;
151606
+ }
151574
151607
  return "0.0.0-dev";
151575
151608
  }
151576
151609
  var Options = class {
@@ -151983,6 +152016,7 @@ var BridgeService = class extends Service {
151983
152016
  for (const bridge of this.bridges) {
151984
152017
  try {
151985
152018
  await bridge.refreshDevices();
152019
+ this.onBridgeChanged?.(bridge.id);
151986
152020
  } catch (e) {
151987
152021
  this.log.error(`Failed to refresh bridge ${bridge.id}:`, e);
151988
152022
  }
@@ -152083,10 +152117,10 @@ import {
152083
152117
  getConfig
152084
152118
  } from "home-assistant-js-websocket";
152085
152119
  var HomeAssistantClient = class extends Service {
152086
- constructor(logger203, options) {
152120
+ constructor(logger204, options) {
152087
152121
  super("HomeAssistantClient");
152088
152122
  this.options = options;
152089
- this.log = logger203.get(this);
152123
+ this.log = logger204.get(this);
152090
152124
  }
152091
152125
  static Options = /* @__PURE__ */ Symbol.for("HomeAssistantClientProps");
152092
152126
  _connection;
@@ -152273,7 +152307,7 @@ var HomeAssistantRegistry = class extends Service {
152273
152307
  try {
152274
152308
  const changed = await this.reload();
152275
152309
  if (changed) {
152276
- onRefresh();
152310
+ await onRefresh();
152277
152311
  }
152278
152312
  } catch (e) {
152279
152313
  logger145.warn("Failed to refresh registry, will retry next interval:", e);
@@ -152317,14 +152351,20 @@ var HomeAssistantRegistry = class extends Service {
152317
152351
  const hash2 = createHash("md5");
152318
152352
  for (const e of entityRegistry) {
152319
152353
  hash2.update(
152320
- `${e.entity_id}\0${e.device_id ?? ""}\0${e.disabled_by ?? ""}\0${e.hidden_by ?? ""}
152354
+ `${e.entity_id}\0${e.device_id ?? ""}\0${e.disabled_by ?? ""}\0${e.hidden_by ?? ""}\0${e.area_id ?? ""}\0${(e.labels ?? []).join(",")}\0${e.platform ?? ""}\0${e.entity_category ?? ""}
152321
152355
  `
152322
152356
  );
152323
152357
  }
152324
- for (const s of statesList) hash2.update(`${s.entity_id}
152325
- `);
152326
- for (const d of deviceRegistry) hash2.update(`${d.id}
152358
+ for (const s of statesList) {
152359
+ hash2.update(`${s.entity_id}\0${s.attributes?.device_class ?? ""}
152327
152360
  `);
152361
+ }
152362
+ for (const d of deviceRegistry) {
152363
+ hash2.update(
152364
+ `${d.id}\0${(d.labels ?? []).join(",")}\0${d.area_id ?? ""}\0${d.name_by_user ?? ""}\0${d.name ?? ""}\0${d.model ?? ""}
152365
+ `
152366
+ );
152367
+ }
152328
152368
  for (const l of labels) hash2.update(`${l.label_id}
152329
152369
  `);
152330
152370
  for (const a of areas) hash2.update(`${a.area_id}\0${a.name}
@@ -152658,6 +152698,7 @@ var EntityMappingStorage = class extends Service {
152658
152698
  disabled: request.disabled,
152659
152699
  filterLifeEntity: request.filterLifeEntity?.trim() || void 0,
152660
152700
  cleaningModeEntity: request.cleaningModeEntity?.trim() || void 0,
152701
+ temperatureEntity: request.temperatureEntity?.trim() || void 0,
152661
152702
  humidityEntity: request.humidityEntity?.trim() || void 0,
152662
152703
  batteryEntity: request.batteryEntity?.trim() || void 0,
152663
152704
  roomEntities: roomEntities.length > 0 ? roomEntities : void 0,
@@ -152670,9 +152711,12 @@ var EntityMappingStorage = class extends Service {
152670
152711
  customServiceAreas: request.customServiceAreas?.filter(
152671
152712
  (a) => a.name?.trim() && a.service?.trim()
152672
152713
  ) ?? void 0,
152673
- customFanSpeedTags: request.customFanSpeedTags && Object.keys(request.customFanSpeedTags).length > 0 ? request.customFanSpeedTags : void 0
152714
+ customFanSpeedTags: request.customFanSpeedTags && Object.keys(request.customFanSpeedTags).length > 0 ? request.customFanSpeedTags : void 0,
152715
+ valetudoIdentifier: request.valetudoIdentifier?.trim() || void 0,
152716
+ coverSwapOpenClose: request.coverSwapOpenClose || void 0,
152717
+ composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
152674
152718
  };
152675
- if (!config10.matterDeviceType && !config10.customName && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0)) {
152719
+ if (!config10.matterDeviceType && !config10.customName && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.temperatureEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0) && !config10.valetudoIdentifier && !config10.coverSwapOpenClose && (!config10.composedEntities || config10.composedEntities.length === 0)) {
152676
152720
  bridgeMap.delete(request.entityId);
152677
152721
  } else {
152678
152722
  bridgeMap.set(request.entityId, config10);
@@ -159413,6 +159457,7 @@ var DimmablePlugInUnitDeviceDefinition = MutableEndpoint({
159413
159457
  )
159414
159458
  });
159415
159459
  Object.freeze(DimmablePlugInUnitDeviceDefinition);
159460
+ var DimmablePlugInUnitDevice = DimmablePlugInUnitDeviceDefinition;
159416
159461
 
159417
159462
  // ../../node_modules/.pnpm/@matter+node@0.16.10/node_modules/@matter/node/dist/esm/devices/dimmer-switch.js
159418
159463
  init_IdentifyServer();
@@ -164692,10 +164737,6 @@ import * as path12 from "node:path";
164692
164737
  // src/plugins/plugin-device-factory.ts
164693
164738
  init_esm();
164694
164739
 
164695
- // src/matter/behaviors/basic-information-server.ts
164696
- init_esm7();
164697
- import crypto4 from "node:crypto";
164698
-
164699
164740
  // ../../node_modules/.pnpm/@matter+main@0.16.10/node_modules/@matter/main/dist/esm/behaviors.js
164700
164741
  init_nodejs();
164701
164742
 
@@ -165653,6 +165694,65 @@ init_window_covering();
165653
165694
  init_ClientBehavior();
165654
165695
  var WindowCoveringClientConstructor = ClientBehavior(WindowCovering3.Complete);
165655
165696
 
165697
+ // src/matter/behaviors/identify-server.ts
165698
+ var IdentifyServer2 = class extends IdentifyServer {
165699
+ };
165700
+
165701
+ // src/matter/endpoints/validate-endpoint-type.ts
165702
+ init_esm();
165703
+ init_esm7();
165704
+ var logger158 = Logger.get("EndpointValidation");
165705
+ function toCamelCase(name) {
165706
+ return name.charAt(0).toLowerCase() + name.slice(1);
165707
+ }
165708
+ function validateEndpointType(endpointType, entityId) {
165709
+ const deviceTypeModel = Matter.deviceTypes.find(
165710
+ (dt) => dt.id === endpointType.deviceType
165711
+ );
165712
+ if (!deviceTypeModel) {
165713
+ return void 0;
165714
+ }
165715
+ const serverClusterReqs = deviceTypeModel.requirements.filter(
165716
+ (r) => r.element === "serverCluster"
165717
+ );
165718
+ const behaviorKeys = new Set(Object.keys(endpointType.behaviors));
165719
+ const missingMandatory = [];
165720
+ const availableOptional = [];
165721
+ const presentClusters = [];
165722
+ for (const req of serverClusterReqs) {
165723
+ const key = toCamelCase(req.name);
165724
+ if (behaviorKeys.has(key)) {
165725
+ presentClusters.push(req.name);
165726
+ } else if (req.isMandatory) {
165727
+ missingMandatory.push(req.name);
165728
+ } else {
165729
+ availableOptional.push(req.name);
165730
+ }
165731
+ }
165732
+ const prefix = entityId ? `[${entityId}] ` : "";
165733
+ if (missingMandatory.length > 0) {
165734
+ logger158.warn(
165735
+ `${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): missing mandatory clusters: ${missingMandatory.join(", ")}`
165736
+ );
165737
+ }
165738
+ if (availableOptional.length > 0) {
165739
+ logger158.debug(
165740
+ `${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): optional clusters not used: ${availableOptional.join(", ")}`
165741
+ );
165742
+ }
165743
+ return {
165744
+ deviceTypeName: deviceTypeModel.name,
165745
+ deviceTypeId: endpointType.deviceType,
165746
+ missingMandatory,
165747
+ availableOptional,
165748
+ presentClusters
165749
+ };
165750
+ }
165751
+
165752
+ // src/plugins/plugin-basic-information-server.ts
165753
+ init_esm7();
165754
+ import crypto4 from "node:crypto";
165755
+
165656
165756
  // src/services/bridges/bridge-data-provider.ts
165657
165757
  init_service();
165658
165758
  import { values as values2 } from "lodash-es";
@@ -165741,7 +165841,7 @@ var BridgeDataProvider = class extends Service {
165741
165841
 
165742
165842
  // src/utils/apply-patch-state.ts
165743
165843
  init_esm();
165744
- var logger158 = Logger.get("ApplyPatchState");
165844
+ var logger159 = Logger.get("ApplyPatchState");
165745
165845
  function applyPatchState(state, patch, options) {
165746
165846
  return applyPatch(state, patch, options?.force);
165747
165847
  }
@@ -165768,23 +165868,23 @@ function applyPatch(state, patch, force = false) {
165768
165868
  if (errorMessage.includes(
165769
165869
  "Endpoint storage inaccessible because endpoint is not a node and is not owned by another endpoint"
165770
165870
  )) {
165771
- logger158.debug(
165871
+ logger159.debug(
165772
165872
  `Suppressed endpoint storage error, patch not applied: ${JSON.stringify(actualPatch)}`
165773
165873
  );
165774
165874
  return actualPatch;
165775
165875
  }
165776
165876
  if (errorMessage.includes("synchronous-transaction-conflict")) {
165777
- logger158.warn(
165877
+ logger159.warn(
165778
165878
  `Transaction conflict, state update DROPPED: ${JSON.stringify(actualPatch)}`
165779
165879
  );
165780
165880
  return actualPatch;
165781
165881
  }
165782
165882
  failedKeys.push(key);
165783
- logger158.warn(`Failed to set property '${key}': ${errorMessage}`);
165883
+ logger159.warn(`Failed to set property '${key}': ${errorMessage}`);
165784
165884
  }
165785
165885
  }
165786
165886
  if (failedKeys.length > 0) {
165787
- logger158.warn(
165887
+ logger159.warn(
165788
165888
  `${failedKeys.length} properties failed to update: [${failedKeys.join(", ")}]`
165789
165889
  );
165790
165890
  }
@@ -165807,56 +165907,6 @@ function deepEqual(a, b) {
165807
165907
  return a === b;
165808
165908
  }
165809
165909
 
165810
- // src/matter/behaviors/basic-information-server.ts
165811
- init_home_assistant_entity_behavior();
165812
- var BasicInformationServer2 = class extends BridgedDeviceBasicInformationServer {
165813
- async initialize() {
165814
- await super.initialize();
165815
- const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
165816
- this.update(homeAssistant.entity);
165817
- this.reactTo(homeAssistant.onChange, this.update);
165818
- }
165819
- update(entity) {
165820
- if (!entity.state) {
165821
- return;
165822
- }
165823
- const { basicInformation } = this.env.get(BridgeDataProvider);
165824
- const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
165825
- const device = entity.deviceRegistry;
165826
- applyPatchState(this.state, {
165827
- vendorId: VendorId(basicInformation.vendorId),
165828
- vendorName: ellipse(32, device?.manufacturer) ?? hash(32, basicInformation.vendorName),
165829
- productName: ellipse(32, device?.model_id) ?? ellipse(32, device?.model) ?? hash(32, basicInformation.productName),
165830
- productLabel: ellipse(64, device?.model) ?? hash(64, basicInformation.productLabel),
165831
- hardwareVersion: basicInformation.hardwareVersion,
165832
- softwareVersion: basicInformation.softwareVersion,
165833
- hardwareVersionString: ellipse(64, device?.hw_version),
165834
- softwareVersionString: ellipse(64, device?.sw_version),
165835
- nodeLabel: ellipse(32, homeAssistant.state.customName) ?? ellipse(32, entity.state?.attributes?.friendly_name) ?? ellipse(32, entity.entity_id),
165836
- reachable: entity.state?.state != null && entity.state.state !== "unavailable",
165837
- // The device serial number is available in `device?.serial_number`, but
165838
- // we're keeping it as the entity ID for now to avoid breaking existing
165839
- // deployments.
165840
- serialNumber: hash(32, entity.entity_id),
165841
- // UniqueId helps controllers (especially Alexa) identify devices across
165842
- // multiple fabric connections. Using MD5 hash of entity_id for stability.
165843
- uniqueId: crypto4.createHash("md5").update(entity.entity_id).digest("hex").substring(0, 32)
165844
- });
165845
- }
165846
- };
165847
- function ellipse(maxLength, value) {
165848
- return trimToLength(value, maxLength, "...");
165849
- }
165850
- function hash(maxLength, value) {
165851
- const hashLength = 4;
165852
- const suffix = crypto4.createHash("md5").update(value ?? "").digest("hex").substring(0, hashLength);
165853
- return trimToLength(value, maxLength, suffix);
165854
- }
165855
-
165856
- // src/matter/behaviors/identify-server.ts
165857
- var IdentifyServer2 = class extends IdentifyServer {
165858
- };
165859
-
165860
165910
  // src/plugins/plugin-behavior.ts
165861
165911
  init_esm7();
165862
165912
  var PluginDeviceBehavior = class extends Behavior {
@@ -165880,72 +165930,142 @@ var PluginDeviceBehavior = class extends Behavior {
165880
165930
  PluginDeviceBehavior2.Events = Events2;
165881
165931
  })(PluginDeviceBehavior || (PluginDeviceBehavior = {}));
165882
165932
 
165933
+ // src/plugins/plugin-basic-information-server.ts
165934
+ var PluginBasicInformationServer = class extends BridgedDeviceBasicInformationServer {
165935
+ async initialize() {
165936
+ await super.initialize();
165937
+ const pluginDevice = this.agent.get(PluginDeviceBehavior);
165938
+ const device = pluginDevice.device;
165939
+ const { basicInformation } = this.env.get(BridgeDataProvider);
165940
+ applyPatchState(this.state, {
165941
+ vendorId: VendorId(basicInformation.vendorId),
165942
+ vendorName: truncate(32, pluginDevice.pluginName),
165943
+ productName: truncate(32, device.deviceType),
165944
+ nodeLabel: truncate(32, device.name),
165945
+ serialNumber: crypto4.createHash("md5").update(`plugin_${device.id}`).digest("hex").substring(0, 32),
165946
+ uniqueId: crypto4.createHash("md5").update(`plugin_${device.id}`).digest("hex").substring(0, 32),
165947
+ reachable: true
165948
+ });
165949
+ }
165950
+ };
165951
+ function truncate(maxLength, value) {
165952
+ if (value.length <= maxLength) return value;
165953
+ return `${value.substring(0, maxLength - 3)}...`;
165954
+ }
165955
+
165883
165956
  // src/plugins/plugin-device-factory.ts
165884
- var logger159 = Logger.get("PluginDeviceFactory");
165957
+ var logger160 = Logger.get("PluginDeviceFactory");
165885
165958
  var deviceTypeMap = {
165886
165959
  on_off_light: () => OnOffLightDevice.with(
165887
165960
  IdentifyServer2,
165888
- BasicInformationServer2,
165961
+ PluginBasicInformationServer,
165889
165962
  PluginDeviceBehavior
165890
165963
  ),
165891
165964
  dimmable_light: () => DimmableLightDevice.with(
165892
165965
  IdentifyServer2,
165893
- BasicInformationServer2,
165966
+ PluginBasicInformationServer,
165967
+ PluginDeviceBehavior
165968
+ ),
165969
+ color_temperature_light: () => ColorTemperatureLightDevice.with(
165970
+ IdentifyServer2,
165971
+ PluginBasicInformationServer,
165972
+ PluginDeviceBehavior
165973
+ ),
165974
+ extended_color_light: () => ExtendedColorLightDevice.with(
165975
+ IdentifyServer2,
165976
+ PluginBasicInformationServer,
165894
165977
  PluginDeviceBehavior
165895
165978
  ),
165896
165979
  on_off_plugin_unit: () => OnOffPlugInUnitDevice.with(
165897
165980
  IdentifyServer2,
165898
- BasicInformationServer2,
165981
+ PluginBasicInformationServer,
165982
+ PluginDeviceBehavior
165983
+ ),
165984
+ dimmable_plug_in_unit: () => DimmablePlugInUnitDevice.with(
165985
+ IdentifyServer2,
165986
+ PluginBasicInformationServer,
165899
165987
  PluginDeviceBehavior
165900
165988
  ),
165901
165989
  temperature_sensor: () => TemperatureSensorDevice.with(
165902
165990
  IdentifyServer2,
165903
- BasicInformationServer2,
165991
+ PluginBasicInformationServer,
165904
165992
  PluginDeviceBehavior
165905
165993
  ),
165906
165994
  humidity_sensor: () => HumiditySensorDevice.with(
165907
165995
  IdentifyServer2,
165908
- BasicInformationServer2,
165996
+ PluginBasicInformationServer,
165997
+ PluginDeviceBehavior
165998
+ ),
165999
+ pressure_sensor: () => PressureSensorDevice.with(
166000
+ IdentifyServer2,
166001
+ PluginBasicInformationServer,
166002
+ PluginDeviceBehavior
166003
+ ),
166004
+ flow_sensor: () => FlowSensorDevice.with(
166005
+ IdentifyServer2,
166006
+ PluginBasicInformationServer,
165909
166007
  PluginDeviceBehavior
165910
166008
  ),
165911
166009
  light_sensor: () => LightSensorDevice.with(
165912
166010
  IdentifyServer2,
165913
- BasicInformationServer2,
166011
+ PluginBasicInformationServer,
165914
166012
  PluginDeviceBehavior
165915
166013
  ),
165916
166014
  occupancy_sensor: () => OccupancySensorDevice.with(
165917
166015
  IdentifyServer2,
165918
- BasicInformationServer2,
166016
+ PluginBasicInformationServer,
165919
166017
  PluginDeviceBehavior
165920
166018
  ),
165921
166019
  contact_sensor: () => ContactSensorDevice.with(
165922
166020
  IdentifyServer2,
165923
- BasicInformationServer2,
166021
+ PluginBasicInformationServer,
166022
+ PluginDeviceBehavior
166023
+ ),
166024
+ air_quality_sensor: () => AirQualitySensorDevice.with(
166025
+ IdentifyServer2,
166026
+ PluginBasicInformationServer,
165924
166027
  PluginDeviceBehavior
165925
166028
  ),
165926
166029
  thermostat: () => ThermostatDevice.with(
165927
166030
  IdentifyServer2,
165928
- BasicInformationServer2,
166031
+ PluginBasicInformationServer,
165929
166032
  PluginDeviceBehavior
165930
166033
  ),
165931
166034
  door_lock: () => DoorLockDevice.with(
165932
166035
  IdentifyServer2,
165933
- BasicInformationServer2,
166036
+ PluginBasicInformationServer,
165934
166037
  PluginDeviceBehavior
165935
166038
  ),
165936
166039
  fan: () => FanDevice.with(
165937
166040
  IdentifyServer2,
165938
- BasicInformationServer2,
166041
+ PluginBasicInformationServer,
166042
+ PluginDeviceBehavior
166043
+ ),
166044
+ window_covering: () => WindowCoveringDevice.with(
166045
+ IdentifyServer2,
166046
+ PluginBasicInformationServer,
166047
+ PluginDeviceBehavior
166048
+ ),
166049
+ generic_switch: () => GenericSwitchDevice.with(
166050
+ IdentifyServer2,
166051
+ PluginBasicInformationServer,
166052
+ PluginDeviceBehavior
166053
+ ),
166054
+ water_leak_detector: () => WaterLeakDetectorDevice.with(
166055
+ IdentifyServer2,
166056
+ PluginBasicInformationServer,
165939
166057
  PluginDeviceBehavior
165940
166058
  )
165941
166059
  };
165942
166060
  function createPluginEndpointType(deviceType) {
165943
166061
  const factory = deviceTypeMap[deviceType];
165944
166062
  if (!factory) {
165945
- logger159.warn(`Unsupported plugin device type: "${deviceType}"`);
166063
+ logger160.warn(`Unsupported plugin device type: "${deviceType}"`);
165946
166064
  return void 0;
165947
166065
  }
165948
- return factory();
166066
+ const endpoint = factory();
166067
+ validateEndpointType(endpoint, `plugin:${deviceType}`);
166068
+ return endpoint;
165949
166069
  }
165950
166070
  function getSupportedPluginDeviceTypes() {
165951
166071
  return Object.keys(deviceTypeMap);
@@ -165955,11 +166075,13 @@ function getSupportedPluginDeviceTypes() {
165955
166075
  init_esm();
165956
166076
  import * as fs10 from "node:fs";
165957
166077
  import * as path11 from "node:path";
165958
- var logger160 = Logger.get("PluginStorage");
166078
+ var logger161 = Logger.get("PluginStorage");
166079
+ var SAVE_DEBOUNCE_MS = 500;
165959
166080
  var FilePluginStorage = class {
165960
166081
  data = {};
165961
166082
  dirty = false;
165962
166083
  filePath;
166084
+ saveTimer;
165963
166085
  constructor(storageDir, pluginName) {
165964
166086
  const safePluginName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
165965
166087
  this.filePath = path11.join(storageDir, `plugin-${safePluginName}.json`);
@@ -165972,12 +166094,12 @@ var FilePluginStorage = class {
165972
166094
  async set(key, value) {
165973
166095
  this.data[key] = value;
165974
166096
  this.dirty = true;
165975
- this.save();
166097
+ this.scheduleSave();
165976
166098
  }
165977
166099
  async delete(key) {
165978
166100
  delete this.data[key];
165979
166101
  this.dirty = true;
165980
- this.save();
166102
+ this.scheduleSave();
165981
166103
  }
165982
166104
  async keys() {
165983
166105
  return Object.keys(this.data);
@@ -165989,12 +166111,20 @@ var FilePluginStorage = class {
165989
166111
  this.data = JSON.parse(raw);
165990
166112
  }
165991
166113
  } catch (e) {
165992
- logger160.warn(`Failed to load plugin storage from ${this.filePath}:`, e);
166114
+ logger161.warn(`Failed to load plugin storage from ${this.filePath}:`, e);
165993
166115
  this.data = {};
165994
166116
  }
165995
166117
  }
166118
+ scheduleSave() {
166119
+ if (this.saveTimer) clearTimeout(this.saveTimer);
166120
+ this.saveTimer = setTimeout(() => this.save(), SAVE_DEBOUNCE_MS);
166121
+ }
165996
166122
  save() {
165997
166123
  if (!this.dirty) return;
166124
+ if (this.saveTimer) {
166125
+ clearTimeout(this.saveTimer);
166126
+ this.saveTimer = void 0;
166127
+ }
165998
166128
  try {
165999
166129
  const dir = path11.dirname(this.filePath);
166000
166130
  if (!fs10.existsSync(dir)) {
@@ -166003,14 +166133,17 @@ var FilePluginStorage = class {
166003
166133
  fs10.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
166004
166134
  this.dirty = false;
166005
166135
  } catch (e) {
166006
- logger160.warn(`Failed to save plugin storage to ${this.filePath}:`, e);
166136
+ logger161.warn(`Failed to save plugin storage to ${this.filePath}:`, e);
166007
166137
  }
166008
166138
  }
166139
+ flush() {
166140
+ this.save();
166141
+ }
166009
166142
  };
166010
166143
 
166011
166144
  // src/plugins/safe-plugin-runner.ts
166012
166145
  init_esm();
166013
- var logger161 = Logger.get("SafePluginRunner");
166146
+ var logger162 = Logger.get("SafePluginRunner");
166014
166147
  var DEFAULT_TIMEOUT_MS = 1e4;
166015
166148
  var CIRCUIT_BREAKER_THRESHOLD = 3;
166016
166149
  var SafePluginRunner = class {
@@ -166043,7 +166176,7 @@ var SafePluginRunner = class {
166043
166176
  async run(pluginName, operation, fn, timeoutMs = DEFAULT_TIMEOUT_MS) {
166044
166177
  const state = this.getState(pluginName);
166045
166178
  if (state.disabled) {
166046
- logger161.debug(
166179
+ logger162.debug(
166047
166180
  `Plugin "${pluginName}" is disabled (circuit breaker open), skipping ${operation}`
166048
166181
  );
166049
166182
  return void 0;
@@ -166061,13 +166194,13 @@ var SafePluginRunner = class {
166061
166194
  timeout.clear();
166062
166195
  state.failures++;
166063
166196
  state.lastError = error instanceof Error ? error.message : String(error);
166064
- logger161.error(
166197
+ logger162.error(
166065
166198
  `Plugin "${pluginName}" failed during ${operation} (failure ${state.failures}/${CIRCUIT_BREAKER_THRESHOLD}): ${state.lastError}`
166066
166199
  );
166067
166200
  if (state.failures >= CIRCUIT_BREAKER_THRESHOLD) {
166068
166201
  state.disabled = true;
166069
166202
  state.disabledAt = Date.now();
166070
- logger161.error(
166203
+ logger162.error(
166071
166204
  `Plugin "${pluginName}" DISABLED after ${CIRCUIT_BREAKER_THRESHOLD} consecutive failures. Last error: ${state.lastError}`
166072
166205
  );
166073
166206
  }
@@ -166089,13 +166222,13 @@ var SafePluginRunner = class {
166089
166222
  } catch (error) {
166090
166223
  state.failures++;
166091
166224
  state.lastError = error instanceof Error ? error.message : String(error);
166092
- logger161.error(
166225
+ logger162.error(
166093
166226
  `Plugin "${pluginName}" failed during ${operation} (sync, failure ${state.failures}/${CIRCUIT_BREAKER_THRESHOLD}): ${state.lastError}`
166094
166227
  );
166095
166228
  if (state.failures >= CIRCUIT_BREAKER_THRESHOLD) {
166096
166229
  state.disabled = true;
166097
166230
  state.disabledAt = Date.now();
166098
- logger161.error(
166231
+ logger162.error(
166099
166232
  `Plugin "${pluginName}" DISABLED after ${CIRCUIT_BREAKER_THRESHOLD} consecutive failures.`
166100
166233
  );
166101
166234
  }
@@ -166121,12 +166254,16 @@ var SafePluginRunner = class {
166121
166254
  };
166122
166255
 
166123
166256
  // src/plugins/plugin-manager.ts
166124
- var logger162 = Logger.get("PluginManager");
166257
+ var logger163 = Logger.get("PluginManager");
166258
+ var PLUGIN_API_VERSION = 1;
166259
+ var MAX_PLUGIN_DEVICE_ID_LENGTH = 100;
166125
166260
  function validatePluginDevice(device) {
166126
166261
  if (!device || typeof device !== "object") return "device must be an object";
166127
166262
  const d = device;
166128
166263
  if (!d.id || typeof d.id !== "string")
166129
166264
  return "device.id must be a non-empty string";
166265
+ if (d.id.length > MAX_PLUGIN_DEVICE_ID_LENGTH)
166266
+ return `device.id too long (${d.id.length} chars, max ${MAX_PLUGIN_DEVICE_ID_LENGTH})`;
166130
166267
  if (!d.name || typeof d.name !== "string")
166131
166268
  return "device.name must be a non-empty string";
166132
166269
  if (!d.deviceType || typeof d.deviceType !== "string")
@@ -166146,6 +166283,7 @@ function validatePluginDevice(device) {
166146
166283
  }
166147
166284
  var PluginManager = class {
166148
166285
  instances = /* @__PURE__ */ new Map();
166286
+ domainMappings = /* @__PURE__ */ new Map();
166149
166287
  storageDir;
166150
166288
  bridgeId;
166151
166289
  runner = new SafePluginRunner();
@@ -166193,6 +166331,11 @@ var PluginManager = class {
166193
166331
  if (!manifest.main || typeof manifest.main !== "string") {
166194
166332
  throw new Error(`Plugin at ${packagePath} package.json missing "main"`);
166195
166333
  }
166334
+ if (manifest.hamhPluginApiVersion != null && manifest.hamhPluginApiVersion !== PLUGIN_API_VERSION) {
166335
+ logger163.warn(
166336
+ `Plugin "${manifest.name}" declares API version ${manifest.hamhPluginApiVersion}, current is ${PLUGIN_API_VERSION}. It may not work correctly.`
166337
+ );
166338
+ }
166196
166339
  const module = await this.runner.run(
166197
166340
  manifest.name,
166198
166341
  "import",
@@ -166220,7 +166363,7 @@ var PluginManager = class {
166220
166363
  };
166221
166364
  await this.register(plugin, metadata);
166222
166365
  } catch (e) {
166223
- logger162.error(`Failed to load external plugin from ${packagePath}:`, e);
166366
+ logger163.error(`Failed to load external plugin from ${packagePath}:`, e);
166224
166367
  throw e;
166225
166368
  }
166226
166369
  }
@@ -166272,6 +166415,21 @@ var PluginManager = class {
166272
166415
  clusterId3,
166273
166416
  attributes7
166274
166417
  );
166418
+ },
166419
+ registerDomainMapping: (mapping) => {
166420
+ if (!mapping.domain || typeof mapping.domain !== "string" || !mapping.matterDeviceType || typeof mapping.matterDeviceType !== "string") {
166421
+ pluginLogger.warn("Invalid domain mapping, skipping");
166422
+ return;
166423
+ }
166424
+ if (this.domainMappings.has(mapping.domain)) {
166425
+ pluginLogger.warn(
166426
+ `Domain "${mapping.domain}" already mapped by another plugin, overwriting`
166427
+ );
166428
+ }
166429
+ this.domainMappings.set(mapping.domain, mapping);
166430
+ pluginLogger.info(
166431
+ `Registered domain mapping: ${mapping.domain} \u2192 ${mapping.matterDeviceType}`
166432
+ );
166275
166433
  }
166276
166434
  };
166277
166435
  this.instances.set(plugin.name, {
@@ -166281,7 +166439,7 @@ var PluginManager = class {
166281
166439
  devices,
166282
166440
  started: false
166283
166441
  });
166284
- logger162.info(
166442
+ logger163.info(
166285
166443
  `Registered plugin: ${plugin.name} v${plugin.version} (${metadata.source})`
166286
166444
  );
166287
166445
  }
@@ -166292,13 +166450,13 @@ var PluginManager = class {
166292
166450
  for (const [name, instance] of this.instances) {
166293
166451
  if (!instance.metadata.enabled) continue;
166294
166452
  if (this.runner.isDisabled(name)) {
166295
- logger162.warn(
166453
+ logger163.warn(
166296
166454
  `Plugin "${name}" is disabled (circuit breaker), skipping start`
166297
166455
  );
166298
166456
  instance.metadata.enabled = false;
166299
166457
  continue;
166300
166458
  }
166301
- logger162.info(`Starting plugin: ${name}`);
166459
+ logger163.info(`Starting plugin: ${name}`);
166302
166460
  await this.runner.run(
166303
166461
  name,
166304
166462
  "onStart",
@@ -166341,8 +166499,12 @@ var PluginManager = class {
166341
166499
  () => instance.plugin.onShutdown(reason)
166342
166500
  );
166343
166501
  }
166502
+ const storage2 = instance.context.storage;
166503
+ if (storage2 instanceof FilePluginStorage) {
166504
+ storage2.flush();
166505
+ }
166344
166506
  instance.started = false;
166345
- logger162.info(`Plugin "${name}" shut down`);
166507
+ logger163.info(`Plugin "${name}" shut down`);
166346
166508
  }
166347
166509
  this.instances.clear();
166348
166510
  }
@@ -166393,6 +166555,9 @@ var PluginManager = class {
166393
166555
  if (!instance) return void 0;
166394
166556
  return instance.plugin.getConfigSchema?.();
166395
166557
  }
166558
+ getDomainMappings() {
166559
+ return new Map(this.domainMappings);
166560
+ }
166396
166561
  async updateConfig(pluginName, config10) {
166397
166562
  const instance = this.instances.get(pluginName);
166398
166563
  if (!instance) return false;
@@ -166738,11 +166903,12 @@ function ensureCommissioningConfig(server) {
166738
166903
 
166739
166904
  // src/services/bridges/bridge.ts
166740
166905
  var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
166906
+ var DEAD_SESSION_TIMEOUT_MS = 6e4;
166741
166907
  var Bridge = class {
166742
- constructor(env, logger203, dataProvider, endpointManager) {
166908
+ constructor(env, logger204, dataProvider, endpointManager) {
166743
166909
  this.dataProvider = dataProvider;
166744
166910
  this.endpointManager = endpointManager;
166745
- this.log = logger203.get(`Bridge / ${dataProvider.id}`);
166911
+ this.log = logger204.get(`Bridge / ${dataProvider.id}`);
166746
166912
  this.server = new BridgeServerNode(
166747
166913
  env,
166748
166914
  this.dataProvider,
@@ -166760,6 +166926,8 @@ var Bridge = class {
166760
166926
  // (e.g. Stopped → Starting → Running).
166761
166927
  onStatusChange;
166762
166928
  autoForceSyncTimer = null;
166929
+ deadSessionTimer = null;
166930
+ staleSessionTimers = /* @__PURE__ */ new Map();
166763
166931
  // Tracks the last synced state JSON per entity to avoid pushing unchanged states.
166764
166932
  // Key: entity_id, Value: JSON.stringify of entity.state
166765
166933
  lastSyncedStates = /* @__PURE__ */ new Map();
@@ -166936,6 +167104,33 @@ ${e?.toString()}`);
166936
167104
  this.log.warn(
166937
167105
  `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
166938
167106
  );
167107
+ if (!this.deadSessionTimer) {
167108
+ this.deadSessionTimer = setTimeout(() => {
167109
+ this.deadSessionTimer = null;
167110
+ this.closeDeadSessions();
167111
+ }, DEAD_SESSION_TIMEOUT_MS);
167112
+ this.log.info(
167113
+ `Scheduled dead session cleanup in ${DEAD_SESSION_TIMEOUT_MS / 1e3}s`
167114
+ );
167115
+ }
167116
+ } else if (totalSubs > 0 && this.deadSessionTimer) {
167117
+ clearTimeout(this.deadSessionTimer);
167118
+ this.deadSessionTimer = null;
167119
+ this.log.info(
167120
+ "Subscriptions recovered, canceled dead session cleanup"
167121
+ );
167122
+ }
167123
+ if (session.subscriptions.size === 0 && !this.staleSessionTimers.has(session.id)) {
167124
+ this.staleSessionTimers.set(
167125
+ session.id,
167126
+ setTimeout(() => {
167127
+ this.staleSessionTimers.delete(session.id);
167128
+ this.closeStaleSession(session.id);
167129
+ }, DEAD_SESSION_TIMEOUT_MS)
167130
+ );
167131
+ } else if (session.subscriptions.size > 0 && this.staleSessionTimers.has(session.id)) {
167132
+ clearTimeout(this.staleSessionTimers.get(session.id));
167133
+ this.staleSessionTimers.delete(session.id);
166939
167134
  }
166940
167135
  };
166941
167136
  sessionManager.subscriptionsChanged.on(this.sessionDiagHandler);
@@ -166964,6 +167159,62 @@ ${e?.toString()}`);
166964
167159
  } catch {
166965
167160
  }
166966
167161
  }
167162
+ closeStaleSession(sessionId) {
167163
+ try {
167164
+ const sessionManager = this.server.env.get(SessionManager);
167165
+ for (const s of [...sessionManager.sessions]) {
167166
+ if (s.id === sessionId && !s.isClosing && s.subscriptions.size === 0) {
167167
+ this.log.warn(
167168
+ `Closing stale session ${s.id} (peer ${s.peerNodeId}, no subscriptions for ${DEAD_SESSION_TIMEOUT_MS / 1e3}s)`
167169
+ );
167170
+ s.initiateClose().catch(() => {
167171
+ return s.initiateForceClose();
167172
+ }).catch(() => {
167173
+ }).finally(() => this.triggerMdnsReAnnounce());
167174
+ break;
167175
+ }
167176
+ }
167177
+ } catch {
167178
+ }
167179
+ }
167180
+ closeDeadSessions() {
167181
+ try {
167182
+ const sessionManager = this.server.env.get(SessionManager);
167183
+ const sessions = [...sessionManager.sessions];
167184
+ const closes = [];
167185
+ for (const s of sessions) {
167186
+ if (!s.isClosing && s.subscriptions.size === 0) {
167187
+ this.log.warn(
167188
+ `Closing dead session ${s.id} (peer ${s.peerNodeId}, no subscriptions for ${DEAD_SESSION_TIMEOUT_MS / 1e3}s)`
167189
+ );
167190
+ closes.push(
167191
+ s.initiateClose().catch(() => {
167192
+ return s.initiateForceClose();
167193
+ })
167194
+ );
167195
+ }
167196
+ }
167197
+ if (closes.length > 0) {
167198
+ Promise.allSettled(closes).then(() => this.triggerMdnsReAnnounce());
167199
+ }
167200
+ } catch {
167201
+ }
167202
+ }
167203
+ /**
167204
+ * Force a fresh mDNS operational advertisement after session cleanup.
167205
+ * matter.js DeviceAdvertiser only re-announces when a subscription is
167206
+ * canceled BY THE PEER. When the server cancels after 3 delivery
167207
+ * timeouts, no re-announcement happens and the controller may not
167208
+ * realize it should reconnect (#266).
167209
+ */
167210
+ triggerMdnsReAnnounce() {
167211
+ try {
167212
+ const advertiser = this.server.env.get(DeviceAdvertiser);
167213
+ advertiser.restartAdvertisement();
167214
+ this.log.info("Triggered mDNS re-announcement after session cleanup");
167215
+ } catch {
167216
+ }
167217
+ }
166967
167218
  unwireSessionDiagnostics() {
166968
167219
  try {
166969
167220
  const sessionManager = this.server.env.get(SessionManager);
@@ -166981,6 +167232,14 @@ ${e?.toString()}`);
166981
167232
  this.sessionDiagHandler = void 0;
166982
167233
  this.sessionAddedHandler = void 0;
166983
167234
  this.sessionDeletedHandler = void 0;
167235
+ if (this.deadSessionTimer) {
167236
+ clearTimeout(this.deadSessionTimer);
167237
+ this.deadSessionTimer = null;
167238
+ }
167239
+ for (const timer of this.staleSessionTimers.values()) {
167240
+ clearTimeout(timer);
167241
+ }
167242
+ this.staleSessionTimers.clear();
166984
167243
  }
166985
167244
  stopAutoForceSync() {
166986
167245
  if (this.autoForceSyncTimer) {
@@ -167114,7 +167373,7 @@ var AggregatorEndpoint2 = class extends Endpoint {
167114
167373
  init_dist();
167115
167374
  init_esm();
167116
167375
  init_home_assistant_entity_behavior();
167117
- import debounce4 from "debounce";
167376
+ import debounce5 from "debounce";
167118
167377
 
167119
167378
  // src/matter/endpoints/entity-endpoint.ts
167120
167379
  init_esm7();
@@ -167148,6 +167407,7 @@ function getMappedEntityIds(mapping) {
167148
167407
  if (!mapping) return [];
167149
167408
  const ids = [];
167150
167409
  if (mapping.batteryEntity) ids.push(mapping.batteryEntity);
167410
+ if (mapping.temperatureEntity) ids.push(mapping.temperatureEntity);
167151
167411
  if (mapping.humidityEntity) ids.push(mapping.humidityEntity);
167152
167412
  if (mapping.pressureEntity) ids.push(mapping.pressureEntity);
167153
167413
  if (mapping.cleaningModeEntity) ids.push(mapping.cleaningModeEntity);
@@ -167156,6 +167416,11 @@ function getMappedEntityIds(mapping) {
167156
167416
  if (mapping.filterLifeEntity) ids.push(mapping.filterLifeEntity);
167157
167417
  if (mapping.powerEntity) ids.push(mapping.powerEntity);
167158
167418
  if (mapping.energyEntity) ids.push(mapping.energyEntity);
167419
+ if (mapping.composedEntities) {
167420
+ for (const sub of mapping.composedEntities) {
167421
+ if (sub.entityId) ids.push(sub.entityId);
167422
+ }
167423
+ }
167159
167424
  return ids;
167160
167425
  }
167161
167426
 
@@ -167304,13 +167569,61 @@ function testBit(value, bitValue) {
167304
167569
  return !!(value & bitValue);
167305
167570
  }
167306
167571
 
167572
+ // src/matter/behaviors/basic-information-server.ts
167573
+ init_esm7();
167574
+ import crypto6 from "node:crypto";
167575
+ init_home_assistant_entity_behavior();
167576
+ var BasicInformationServer2 = class extends BridgedDeviceBasicInformationServer {
167577
+ async initialize() {
167578
+ await super.initialize();
167579
+ const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
167580
+ this.update(homeAssistant.entity);
167581
+ this.reactTo(homeAssistant.onChange, this.update);
167582
+ }
167583
+ update(entity) {
167584
+ if (!entity.state) {
167585
+ return;
167586
+ }
167587
+ const { basicInformation } = this.env.get(BridgeDataProvider);
167588
+ const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
167589
+ const device = entity.deviceRegistry;
167590
+ applyPatchState(this.state, {
167591
+ vendorId: VendorId(basicInformation.vendorId),
167592
+ vendorName: ellipse(32, device?.manufacturer) ?? hash(32, basicInformation.vendorName),
167593
+ productName: ellipse(32, device?.model_id) ?? ellipse(32, device?.model) ?? hash(32, basicInformation.productName),
167594
+ productLabel: ellipse(64, device?.model) ?? hash(64, basicInformation.productLabel),
167595
+ hardwareVersion: basicInformation.hardwareVersion,
167596
+ softwareVersion: basicInformation.softwareVersion,
167597
+ hardwareVersionString: ellipse(64, device?.hw_version),
167598
+ softwareVersionString: ellipse(64, device?.sw_version),
167599
+ nodeLabel: ellipse(32, homeAssistant.state.customName) ?? ellipse(32, entity.state?.attributes?.friendly_name) ?? ellipse(32, entity.entity_id),
167600
+ reachable: entity.state?.state != null && entity.state.state !== "unavailable",
167601
+ // The device serial number is available in `device?.serial_number`, but
167602
+ // we're keeping it as the entity ID for now to avoid breaking existing
167603
+ // deployments.
167604
+ serialNumber: hash(32, entity.entity_id),
167605
+ // UniqueId helps controllers (especially Alexa) identify devices across
167606
+ // multiple fabric connections. Using MD5 hash of entity_id for stability.
167607
+ uniqueId: crypto6.createHash("md5").update(entity.entity_id).digest("hex").substring(0, 32)
167608
+ });
167609
+ }
167610
+ };
167611
+ function ellipse(maxLength, value) {
167612
+ return trimToLength(value, maxLength, "...");
167613
+ }
167614
+ function hash(maxLength, value) {
167615
+ const hashLength = 4;
167616
+ const suffix = crypto6.createHash("md5").update(value ?? "").digest("hex").substring(0, hashLength);
167617
+ return trimToLength(value, maxLength, suffix);
167618
+ }
167619
+
167307
167620
  // src/matter/endpoints/composed/composed-air-purifier-endpoint.ts
167308
167621
  init_home_assistant_entity_behavior();
167309
167622
 
167310
167623
  // src/matter/behaviors/humidity-measurement-server.ts
167311
167624
  init_esm();
167312
167625
  init_home_assistant_entity_behavior();
167313
- var logger163 = Logger.get("HumidityMeasurementServer");
167626
+ var logger164 = Logger.get("HumidityMeasurementServer");
167314
167627
  var HumidityMeasurementServerBase = class extends RelativeHumidityMeasurementServer {
167315
167628
  async initialize() {
167316
167629
  await super.initialize();
@@ -167323,7 +167636,7 @@ var HumidityMeasurementServerBase = class extends RelativeHumidityMeasurementSer
167323
167636
  return;
167324
167637
  }
167325
167638
  const humidity = this.getHumidity(this.state.config, entity.state);
167326
- logger163.debug(
167639
+ logger164.debug(
167327
167640
  `Humidity ${entity.state.entity_id} raw=${entity.state.state} measuredValue=${humidity}`
167328
167641
  );
167329
167642
  applyPatchState(this.state, {
@@ -167359,7 +167672,7 @@ init_clusters();
167359
167672
 
167360
167673
  // src/matter/behaviors/power-source-server.ts
167361
167674
  init_home_assistant_entity_behavior();
167362
- var logger164 = Logger.get("PowerSourceServer");
167675
+ var logger165 = Logger.get("PowerSourceServer");
167363
167676
  var FeaturedBase = PowerSourceServer.with("Battery", "Rechargeable");
167364
167677
  var PowerSourceServerBase = class extends FeaturedBase {
167365
167678
  async initialize() {
@@ -167371,17 +167684,17 @@ var PowerSourceServerBase = class extends FeaturedBase {
167371
167684
  applyPatchState(this.state, {
167372
167685
  endpointList: [endpointNumber]
167373
167686
  });
167374
- logger164.debug(
167687
+ logger165.debug(
167375
167688
  `[${entityId}] PowerSource initialized with endpointList=[${endpointNumber}]`
167376
167689
  );
167377
167690
  } else {
167378
- logger164.warn(
167691
+ logger165.warn(
167379
167692
  `[${entityId}] PowerSource endpoint number is null during initialize - endpointList will be empty!`
167380
167693
  );
167381
167694
  }
167382
167695
  const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
167383
167696
  if (batteryEntity) {
167384
- logger164.debug(
167697
+ logger165.debug(
167385
167698
  `[${entityId}] PowerSource using mapped battery entity: ${batteryEntity}`
167386
167699
  );
167387
167700
  }
@@ -167444,6 +167757,26 @@ function PowerSourceServer2(config10) {
167444
167757
  order: PowerSource3.Cluster.id
167445
167758
  });
167446
167759
  }
167760
+ var defaultBatteryConfig = {
167761
+ getBatteryPercent: (entity, agent) => {
167762
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
167763
+ const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
167764
+ if (batteryEntity) {
167765
+ const stateProvider = agent.env.get(EntityStateProvider);
167766
+ const battery = stateProvider.getBatteryPercent(batteryEntity);
167767
+ if (battery != null) {
167768
+ return Math.max(0, Math.min(100, battery));
167769
+ }
167770
+ }
167771
+ const attrs = entity.attributes;
167772
+ const level = attrs.battery_level ?? attrs.battery;
167773
+ if (level == null || Number.isNaN(Number(level))) {
167774
+ return null;
167775
+ }
167776
+ return Number(level);
167777
+ }
167778
+ };
167779
+ var DefaultPowerSourceServer = PowerSourceServer2(defaultBatteryConfig);
167447
167780
 
167448
167781
  // src/matter/behaviors/temperature-measurement-server.ts
167449
167782
  init_home_assistant_entity_behavior();
@@ -167688,7 +168021,7 @@ var OPTIMISTIC_TOLERANCE = 5;
167688
168021
  function notifyLightTurnedOn(entityId) {
167689
168022
  lastTurnOnTimestamps.set(entityId, Date.now());
167690
168023
  }
167691
- var logger165 = Logger.get("LevelControlServer");
168024
+ var logger166 = Logger.get("LevelControlServer");
167692
168025
  var FeaturedBase3 = LevelControlServer.with("OnOff", "Lighting");
167693
168026
  var LevelControlServerBase = class extends FeaturedBase3 {
167694
168027
  pendingTransitionTime;
@@ -167703,12 +168036,12 @@ var LevelControlServerBase = class extends FeaturedBase3 {
167703
168036
  this.state.maxLevel = 254;
167704
168037
  }
167705
168038
  this.state.onLevel = null;
167706
- logger165.debug(`initialize: calling super.initialize()`);
168039
+ logger166.debug(`initialize: calling super.initialize()`);
167707
168040
  try {
167708
168041
  await super.initialize();
167709
- logger165.debug(`initialize: super.initialize() completed successfully`);
168042
+ logger166.debug(`initialize: super.initialize() completed successfully`);
167710
168043
  } catch (error) {
167711
- logger165.error(`initialize: super.initialize() FAILED:`, error);
168044
+ logger166.error(`initialize: super.initialize() FAILED:`, error);
167712
168045
  throw error;
167713
168046
  }
167714
168047
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
@@ -167784,7 +168117,7 @@ var LevelControlServerBase = class extends FeaturedBase3 {
167784
168117
  const timeSinceTurnOn = lastTurnOn ? Date.now() - lastTurnOn : Infinity;
167785
168118
  const isMaxBrightness = level >= this.maxLevel;
167786
168119
  if (isMaxBrightness && timeSinceTurnOn < 200) {
167787
- logger165.debug(
168120
+ logger166.debug(
167788
168121
  `[${entityId}] Ignoring moveToLevel(${level}) - Alexa brightness reset detected (${timeSinceTurnOn}ms after turn-on)`
167789
168122
  );
167790
168123
  return;
@@ -167827,7 +168160,7 @@ function LevelControlServer2(config10) {
167827
168160
  }
167828
168161
 
167829
168162
  // src/matter/behaviors/on-off-server.ts
167830
- var logger166 = Logger.get("OnOffServer");
168163
+ var logger167 = Logger.get("OnOffServer");
167831
168164
  var optimisticOnOffState = /* @__PURE__ */ new Map();
167832
168165
  var OPTIMISTIC_TIMEOUT_MS2 = 3e3;
167833
168166
  var OnOffServerBase = class extends OnOffServer {
@@ -167862,12 +168195,13 @@ var OnOffServerBase = class extends OnOffServer {
167862
168195
  return;
167863
168196
  }
167864
168197
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
167865
- const action = turnOn?.(void 0, this.agent) ?? {
167866
- action: "homeassistant.turn_on"
167867
- };
167868
- logger166.info(`[${homeAssistant.entityId}] Turning ON -> ${action.action}`);
167869
- notifyLightTurnedOn(homeAssistant.entityId);
168198
+ const action = turnOn ? turnOn(void 0, this.agent) : { action: "homeassistant.turn_on" };
167870
168199
  applyPatchState(this.state, { onOff: true });
168200
+ if (!action) {
168201
+ return;
168202
+ }
168203
+ logger167.info(`[${homeAssistant.entityId}] Turning ON -> ${action.action}`);
168204
+ notifyLightTurnedOn(homeAssistant.entityId);
167871
168205
  optimisticOnOffState.set(homeAssistant.entityId, {
167872
168206
  expectedOnOff: true,
167873
168207
  timestamp: Date.now()
@@ -167884,11 +168218,12 @@ var OnOffServerBase = class extends OnOffServer {
167884
168218
  return;
167885
168219
  }
167886
168220
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
167887
- const action = turnOff?.(void 0, this.agent) ?? {
167888
- action: "homeassistant.turn_off"
167889
- };
167890
- logger166.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
168221
+ const action = turnOff ? turnOff(void 0, this.agent) : { action: "homeassistant.turn_off" };
167891
168222
  applyPatchState(this.state, { onOff: false });
168223
+ if (!action) {
168224
+ return;
168225
+ }
168226
+ logger167.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
167892
168227
  optimisticOnOffState.set(homeAssistant.entityId, {
167893
168228
  expectedOnOff: false,
167894
168229
  timestamp: Date.now()
@@ -167897,6 +168232,7 @@ var OnOffServerBase = class extends OnOffServer {
167897
168232
  }
167898
168233
  autoReset() {
167899
168234
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168235
+ optimisticOnOffState.delete(homeAssistant.entityId);
167900
168236
  this.update(homeAssistant.entity);
167901
168237
  }
167902
168238
  };
@@ -167917,7 +168253,7 @@ function setOptimisticOnOff(entityId, expectedOnOff) {
167917
168253
  }
167918
168254
 
167919
168255
  // src/matter/behaviors/fan-control-server.ts
167920
- var logger167 = Logger.get("FanControlServer");
168256
+ var logger168 = Logger.get("FanControlServer");
167921
168257
  var defaultStepSize = 33.33;
167922
168258
  var minSpeedMax = 3;
167923
168259
  var maxSpeedMax = 100;
@@ -168276,7 +168612,7 @@ var FanControlServerBase = class extends FeaturedBase4 {
168276
168612
  const entityId = this.agent.get(HomeAssistantEntityBehavior).entity.entity_id;
168277
168613
  setOptimisticOnOff(entityId, on);
168278
168614
  } catch (e) {
168279
- logger167.debug(
168615
+ logger168.debug(
168280
168616
  `syncOnOff(${on}) failed: ${e instanceof Error ? e.message : String(e)}`
168281
168617
  );
168282
168618
  }
@@ -168364,36 +168700,26 @@ var FanOnOffServer = OnOffServer2({
168364
168700
  });
168365
168701
 
168366
168702
  // src/matter/endpoints/composed/composed-air-purifier-endpoint.ts
168367
- var logger168 = Logger.get("ComposedAirPurifierEndpoint");
168368
- function createTemperatureConfig(temperatureEntityId) {
168369
- return {
168370
- getValue(_entity, agent) {
168371
- const stateProvider = agent.env.get(EntityStateProvider);
168372
- const tempState = stateProvider.getState(temperatureEntityId);
168373
- if (!tempState) return void 0;
168374
- const temperature3 = Number.parseFloat(tempState.state);
168375
- if (Number.isNaN(temperature3)) return void 0;
168376
- const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
168377
- const attrs = tempState.attributes;
168378
- return Temperature.withUnit(
168379
- temperature3,
168380
- attrs.unit_of_measurement ?? fallbackUnit
168381
- );
168382
- }
168383
- };
168384
- }
168385
- function createHumidityConfig(humidityEntityId) {
168386
- return {
168387
- getValue(_entity, agent) {
168388
- const stateProvider = agent.env.get(EntityStateProvider);
168389
- const humState = stateProvider.getState(humidityEntityId);
168390
- if (!humState) return null;
168391
- const humidity = Number.parseFloat(humState.state);
168392
- if (Number.isNaN(humidity)) return null;
168393
- return humidity;
168394
- }
168395
- };
168396
- }
168703
+ var logger169 = Logger.get("ComposedAirPurifierEndpoint");
168704
+ var temperatureConfig = {
168705
+ getValue(entity, agent) {
168706
+ const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
168707
+ const state = entity.state;
168708
+ const attributes7 = entity.attributes;
168709
+ const temperature3 = state == null || Number.isNaN(+state) ? null : +state;
168710
+ if (temperature3 == null) return void 0;
168711
+ return Temperature.withUnit(
168712
+ temperature3,
168713
+ attributes7.unit_of_measurement ?? fallbackUnit
168714
+ );
168715
+ }
168716
+ };
168717
+ var humidityConfig = {
168718
+ getValue({ state }) {
168719
+ if (state == null || Number.isNaN(+state)) return null;
168720
+ return +state;
168721
+ }
168722
+ };
168397
168723
  var batteryConfig = {
168398
168724
  getBatteryPercent: (_entity, agent) => {
168399
168725
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
@@ -168406,6 +168732,16 @@ var batteryConfig = {
168406
168732
  return null;
168407
168733
  }
168408
168734
  };
168735
+ var TemperatureSubType = TemperatureSensorDevice.with(
168736
+ IdentifyServer2,
168737
+ HomeAssistantEntityBehavior,
168738
+ TemperatureMeasurementServer2(temperatureConfig)
168739
+ );
168740
+ var HumiditySubType = HumiditySensorDevice.with(
168741
+ IdentifyServer2,
168742
+ HomeAssistantEntityBehavior,
168743
+ HumidityMeasurementServer(humidityConfig)
168744
+ );
168409
168745
  function createEndpointId2(entityId, customName) {
168410
168746
  const baseName = customName || entityId;
168411
168747
  return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
@@ -168425,9 +168761,9 @@ function buildEntityPayload(registry2, entityId) {
168425
168761
  var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends Endpoint {
168426
168762
  entityId;
168427
168763
  mappedEntityIds;
168428
- trackedEntityIds;
168764
+ subEndpoints = /* @__PURE__ */ new Map();
168429
168765
  lastStates = /* @__PURE__ */ new Map();
168430
- debouncedFlush;
168766
+ debouncedUpdates = /* @__PURE__ */ new Map();
168431
168767
  static async create(config10) {
168432
168768
  const { registry: registry2, primaryEntityId } = config10;
168433
168769
  const primaryPayload = buildEntityPayload(registry2, primaryEntityId);
@@ -168455,8 +168791,7 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168455
168791
  if (hasWindModes) {
168456
168792
  features2.add("Wind");
168457
168793
  }
168458
- let parentType = AirPurifierDevice.with(
168459
- BasicInformationServer2,
168794
+ let airPurifierSubType = AirPurifierDevice.with(
168460
168795
  IdentifyServer2,
168461
168796
  HomeAssistantEntityBehavior,
168462
168797
  FanOnOffServer,
@@ -168464,27 +168799,23 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168464
168799
  );
168465
168800
  const hasFilterLife = airPurifierAttributes.filter_life != null || airPurifierAttributes.filter_life_remaining != null || airPurifierAttributes.filter_life_level != null || !!config10.mapping?.filterLifeEntity;
168466
168801
  if (hasFilterLife) {
168467
- parentType = parentType.with(AirPurifierHepaFilterMonitoringServer);
168468
- }
168469
- if (config10.temperatureEntityId) {
168470
- parentType = parentType.with(
168471
- TemperatureMeasurementServer2(
168472
- createTemperatureConfig(config10.temperatureEntityId)
168473
- )
168474
- );
168475
- }
168476
- if (config10.humidityEntityId) {
168477
- parentType = parentType.with(
168478
- HumidityMeasurementServer(
168479
- createHumidityConfig(config10.humidityEntityId)
168480
- )
168802
+ airPurifierSubType = airPurifierSubType.with(
168803
+ AirPurifierHepaFilterMonitoringServer
168481
168804
  );
168482
168805
  }
168483
- const mapping = {
168806
+ const airPurifierMapping = {
168484
168807
  entityId: primaryEntityId,
168485
- ...config10.batteryEntityId ? { batteryEntity: config10.batteryEntityId } : {},
168486
168808
  ...config10.mapping?.filterLifeEntity ? { filterLifeEntity: config10.mapping.filterLifeEntity } : {}
168487
168809
  };
168810
+ let parentType = BridgedNodeEndpoint.with(
168811
+ BasicInformationServer2,
168812
+ IdentifyServer2,
168813
+ HomeAssistantEntityBehavior
168814
+ );
168815
+ const parentMapping = {
168816
+ entityId: primaryEntityId,
168817
+ ...config10.batteryEntityId ? { batteryEntity: config10.batteryEntityId } : {}
168818
+ };
168488
168819
  if (config10.batteryEntityId) {
168489
168820
  parentType = parentType.with(PowerSourceServer2(batteryConfig));
168490
168821
  }
@@ -168497,75 +168828,125 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168497
168828
  );
168498
168829
  }
168499
168830
  const endpointId = createEndpointId2(primaryEntityId, config10.customName);
168831
+ const parts = [];
168832
+ const airPurifierSub = new Endpoint(
168833
+ airPurifierSubType.set({
168834
+ homeAssistantEntity: {
168835
+ entity: primaryPayload,
168836
+ mapping: airPurifierMapping
168837
+ }
168838
+ }),
168839
+ { id: `${endpointId}_air_purifier` }
168840
+ );
168841
+ parts.push(airPurifierSub);
168842
+ let tempSub;
168843
+ if (config10.temperatureEntityId) {
168844
+ const tempPayload = buildEntityPayload(
168845
+ registry2,
168846
+ config10.temperatureEntityId
168847
+ );
168848
+ if (tempPayload) {
168849
+ tempSub = new Endpoint(
168850
+ TemperatureSubType.set({
168851
+ homeAssistantEntity: { entity: tempPayload }
168852
+ }),
168853
+ { id: `${endpointId}_temp` }
168854
+ );
168855
+ parts.push(tempSub);
168856
+ }
168857
+ }
168858
+ let humSub;
168859
+ if (config10.humidityEntityId) {
168860
+ const humPayload = buildEntityPayload(registry2, config10.humidityEntityId);
168861
+ if (humPayload) {
168862
+ humSub = new Endpoint(
168863
+ HumiditySubType.set({
168864
+ homeAssistantEntity: { entity: humPayload }
168865
+ }),
168866
+ { id: `${endpointId}_humidity` }
168867
+ );
168868
+ parts.push(humSub);
168869
+ }
168870
+ }
168500
168871
  const parentTypeWithState = parentType.set({
168501
168872
  homeAssistantEntity: {
168502
168873
  entity: primaryPayload,
168503
168874
  customName: config10.customName,
168504
- mapping
168875
+ mapping: parentMapping
168505
168876
  }
168506
168877
  });
168507
- const trackedEntityIds = [primaryEntityId];
168508
- if (config10.temperatureEntityId)
168509
- trackedEntityIds.push(config10.temperatureEntityId);
168510
- if (config10.humidityEntityId) trackedEntityIds.push(config10.humidityEntityId);
168511
- const mappedIds = trackedEntityIds.filter((id) => id !== primaryEntityId);
168878
+ const mappedIds = [];
168879
+ if (config10.temperatureEntityId) mappedIds.push(config10.temperatureEntityId);
168880
+ if (config10.humidityEntityId) mappedIds.push(config10.humidityEntityId);
168881
+ if (config10.mapping?.filterLifeEntity)
168882
+ mappedIds.push(config10.mapping.filterLifeEntity);
168512
168883
  const endpoint = new _ComposedAirPurifierEndpoint(
168513
168884
  parentTypeWithState,
168514
168885
  primaryEntityId,
168515
168886
  endpointId,
168516
- trackedEntityIds,
168887
+ parts,
168517
168888
  mappedIds
168518
168889
  );
168890
+ endpoint.subEndpoints.set(primaryEntityId, airPurifierSub);
168891
+ if (config10.temperatureEntityId && tempSub) {
168892
+ endpoint.subEndpoints.set(config10.temperatureEntityId, tempSub);
168893
+ }
168894
+ if (config10.humidityEntityId && humSub) {
168895
+ endpoint.subEndpoints.set(config10.humidityEntityId, humSub);
168896
+ }
168519
168897
  const clusterLabels = [
168520
168898
  "AirPurifier",
168521
168899
  config10.temperatureEntityId ? "+Temp" : "",
168522
168900
  config10.humidityEntityId ? "+Hum" : "",
168523
- config10.batteryEntityId ? "+Bat" : ""
168901
+ config10.batteryEntityId ? "+Bat" : "",
168902
+ hasFilterLife ? "+HEPA" : ""
168524
168903
  ].filter(Boolean).join("");
168525
- logger168.info(`Created air purifier ${primaryEntityId}: ${clusterLabels}`);
168904
+ logger169.info(
168905
+ `Created composed air purifier ${primaryEntityId}: ${clusterLabels}`
168906
+ );
168526
168907
  return endpoint;
168527
168908
  }
168528
- constructor(type, entityId, id, trackedEntityIds, mappedEntityIds) {
168529
- super(type, { id });
168909
+ constructor(type, entityId, id, parts, mappedEntityIds) {
168910
+ super(type, { id, parts });
168530
168911
  this.entityId = entityId;
168531
- this.trackedEntityIds = trackedEntityIds;
168532
168912
  this.mappedEntityIds = mappedEntityIds;
168533
168913
  }
168534
168914
  async updateStates(states) {
168535
- let anyChanged = false;
168536
- for (const entityId of this.trackedEntityIds) {
168537
- const state = states[entityId];
168538
- if (!state) continue;
168539
- const stateJson = JSON.stringify({
168540
- s: state.state,
168541
- a: state.attributes
168542
- });
168543
- if (this.lastStates.get(entityId) !== stateJson) {
168544
- this.lastStates.set(entityId, stateJson);
168545
- anyChanged = true;
168546
- }
168915
+ this.scheduleUpdate(this, this.entityId, states);
168916
+ for (const [entityId, sub] of this.subEndpoints) {
168917
+ this.scheduleUpdate(sub, entityId, states);
168547
168918
  }
168548
- if (!anyChanged) return;
168549
- const primaryState = states[this.entityId];
168550
- if (!primaryState) return;
168551
- if (!this.debouncedFlush) {
168552
- this.debouncedFlush = debounce2(
168553
- (s) => this.flushUpdate(s),
168919
+ }
168920
+ scheduleUpdate(endpoint, entityId, states) {
168921
+ const state = states[entityId];
168922
+ if (!state) return;
168923
+ const key = endpoint === this ? `_parent_:${entityId}` : entityId;
168924
+ const stateJson = JSON.stringify({
168925
+ s: state.state,
168926
+ a: state.attributes
168927
+ });
168928
+ if (this.lastStates.get(key) === stateJson) return;
168929
+ this.lastStates.set(key, stateJson);
168930
+ let debouncedFn = this.debouncedUpdates.get(key);
168931
+ if (!debouncedFn) {
168932
+ debouncedFn = debounce2(
168933
+ (ep, s) => this.flushUpdate(ep, s),
168554
168934
  50
168555
168935
  );
168936
+ this.debouncedUpdates.set(key, debouncedFn);
168556
168937
  }
168557
- this.debouncedFlush(primaryState);
168938
+ debouncedFn(endpoint, state);
168558
168939
  }
168559
- async flushUpdate(state) {
168940
+ async flushUpdate(endpoint, state) {
168560
168941
  try {
168561
- await this.construction.ready;
168942
+ await endpoint.construction.ready;
168562
168943
  } catch {
168563
168944
  return;
168564
168945
  }
168565
168946
  try {
168566
- const current = this.stateOf(HomeAssistantEntityBehavior).entity;
168567
- await this.setStateOf(HomeAssistantEntityBehavior, {
168568
- entity: { ...current, state: { ...state } }
168947
+ const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
168948
+ await endpoint.setStateOf(HomeAssistantEntityBehavior, {
168949
+ entity: { ...current, state }
168569
168950
  });
168570
168951
  } catch (error) {
168571
168952
  if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
@@ -168581,7 +168962,9 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168581
168962
  }
168582
168963
  }
168583
168964
  async delete() {
168584
- this.debouncedFlush?.clear();
168965
+ for (const fn of this.debouncedUpdates.values()) {
168966
+ fn.clear();
168967
+ }
168585
168968
  await super.delete();
168586
168969
  }
168587
168970
  };
@@ -168618,7 +169001,7 @@ init_home_assistant_entity_behavior();
168618
169001
  // src/matter/behaviors/pressure-measurement-server.ts
168619
169002
  init_esm();
168620
169003
  init_home_assistant_entity_behavior();
168621
- var logger169 = Logger.get("PressureMeasurementServer");
169004
+ var logger170 = Logger.get("PressureMeasurementServer");
168622
169005
  var MIN_PRESSURE = 300;
168623
169006
  var MAX_PRESSURE = 1100;
168624
169007
  var PressureMeasurementServerBase = class extends PressureMeasurementServer {
@@ -168646,7 +169029,7 @@ var PressureMeasurementServerBase = class extends PressureMeasurementServer {
168646
169029
  }
168647
169030
  const rounded = Math.round(value);
168648
169031
  if (rounded < MIN_PRESSURE || rounded > MAX_PRESSURE) {
168649
- logger169.warn(
169032
+ logger170.warn(
168650
169033
  `Pressure value ${rounded} (raw: ${value}) for ${entity.entity_id} is outside valid range [${MIN_PRESSURE}-${MAX_PRESSURE}], ignoring`
168651
169034
  );
168652
169035
  return null;
@@ -168665,8 +169048,8 @@ function PressureMeasurementServer2(config10) {
168665
169048
  }
168666
169049
 
168667
169050
  // src/matter/endpoints/composed/composed-sensor-endpoint.ts
168668
- var logger170 = Logger.get("ComposedSensorEndpoint");
168669
- var temperatureConfig = {
169051
+ var logger171 = Logger.get("ComposedSensorEndpoint");
169052
+ var temperatureConfig2 = {
168670
169053
  getValue(entity, agent) {
168671
169054
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
168672
169055
  const state = entity.state;
@@ -168679,7 +169062,7 @@ var temperatureConfig = {
168679
169062
  );
168680
169063
  }
168681
169064
  };
168682
- var humidityConfig = {
169065
+ var humidityConfig2 = {
168683
169066
  getValue({ state }) {
168684
169067
  if (state == null || Number.isNaN(+state)) return null;
168685
169068
  return +state;
@@ -168706,15 +169089,15 @@ var batteryConfig2 = {
168706
169089
  return null;
168707
169090
  }
168708
169091
  };
168709
- var TemperatureSubType = TemperatureSensorDevice.with(
169092
+ var TemperatureSubType2 = TemperatureSensorDevice.with(
168710
169093
  IdentifyServer2,
168711
169094
  HomeAssistantEntityBehavior,
168712
- TemperatureMeasurementServer2(temperatureConfig)
169095
+ TemperatureMeasurementServer2(temperatureConfig2)
168713
169096
  );
168714
- var HumiditySubType = HumiditySensorDevice.with(
169097
+ var HumiditySubType2 = HumiditySensorDevice.with(
168715
169098
  IdentifyServer2,
168716
169099
  HomeAssistantEntityBehavior,
168717
- HumidityMeasurementServer(humidityConfig)
169100
+ HumidityMeasurementServer(humidityConfig2)
168718
169101
  );
168719
169102
  var PressureSubType = PressureSensorDevice.with(
168720
169103
  IdentifyServer2,
@@ -168770,7 +169153,7 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168770
169153
  const endpointId = createEndpointId3(primaryEntityId, config10.customName);
168771
169154
  const parts = [];
168772
169155
  const tempSub = new Endpoint(
168773
- TemperatureSubType.set({
169156
+ TemperatureSubType2.set({
168774
169157
  homeAssistantEntity: { entity: primaryPayload }
168775
169158
  }),
168776
169159
  { id: `${endpointId}_temp` }
@@ -168781,7 +169164,7 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168781
169164
  const humPayload = buildEntityPayload2(registry2, config10.humidityEntityId);
168782
169165
  if (humPayload) {
168783
169166
  humSub = new Endpoint(
168784
- HumiditySubType.set({
169167
+ HumiditySubType2.set({
168785
169168
  homeAssistantEntity: { entity: humPayload }
168786
169169
  }),
168787
169170
  { id: `${endpointId}_humidity` }
@@ -168829,7 +169212,7 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168829
169212
  if (config10.pressureEntityId && pressSub) {
168830
169213
  endpoint.subEndpoints.set(config10.pressureEntityId, pressSub);
168831
169214
  }
168832
- logger170.info(
169215
+ logger171.info(
168833
169216
  `Created composed sensor ${primaryEntityId} with ${parts.length} sub-endpoint(s): T${humSub ? "+H" : ""}${pressSub ? "+P" : ""}${config10.batteryEntityId ? "+Bat" : ""}`
168834
169217
  );
168835
169218
  return endpoint;
@@ -168897,56 +169280,14 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168897
169280
  }
168898
169281
  };
168899
169282
 
168900
- // src/matter/endpoints/validate-endpoint-type.ts
169283
+ // src/matter/endpoints/composed/user-composed-endpoint.ts
168901
169284
  init_esm();
168902
169285
  init_esm7();
168903
- var logger171 = Logger.get("EndpointValidation");
168904
- function toCamelCase(name) {
168905
- return name.charAt(0).toLowerCase() + name.slice(1);
168906
- }
168907
- function validateEndpointType(endpointType, entityId) {
168908
- const deviceTypeModel = Matter.deviceTypes.find(
168909
- (dt) => dt.id === endpointType.deviceType
168910
- );
168911
- if (!deviceTypeModel) {
168912
- return void 0;
168913
- }
168914
- const serverClusterReqs = deviceTypeModel.requirements.filter(
168915
- (r) => r.element === "serverCluster"
168916
- );
168917
- const behaviorKeys = new Set(Object.keys(endpointType.behaviors));
168918
- const missingMandatory = [];
168919
- const availableOptional = [];
168920
- const presentClusters = [];
168921
- for (const req of serverClusterReqs) {
168922
- const key = toCamelCase(req.name);
168923
- if (behaviorKeys.has(key)) {
168924
- presentClusters.push(req.name);
168925
- } else if (req.isMandatory) {
168926
- missingMandatory.push(req.name);
168927
- } else {
168928
- availableOptional.push(req.name);
168929
- }
168930
- }
168931
- const prefix = entityId ? `[${entityId}] ` : "";
168932
- if (missingMandatory.length > 0) {
168933
- logger171.warn(
168934
- `${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): missing mandatory clusters: ${missingMandatory.join(", ")}`
168935
- );
168936
- }
168937
- if (availableOptional.length > 0) {
168938
- logger171.debug(
168939
- `${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): optional clusters not used: ${availableOptional.join(", ")}`
168940
- );
168941
- }
168942
- return {
168943
- deviceTypeName: deviceTypeModel.name,
168944
- deviceTypeId: endpointType.deviceType,
168945
- missingMandatory,
168946
- availableOptional,
168947
- presentClusters
168948
- };
168949
- }
169286
+ import debounce4 from "debounce";
169287
+ init_home_assistant_entity_behavior();
169288
+
169289
+ // src/matter/endpoints/legacy/create-legacy-endpoint-type.ts
169290
+ init_esm();
168950
169291
 
168951
169292
  // src/matter/endpoints/legacy/air-purifier/index.ts
168952
169293
  init_dist();
@@ -169208,10 +169549,6 @@ function AutomationDevice(homeAssistantEntity) {
169208
169549
  return AutomationDeviceType.set({ homeAssistantEntity });
169209
169550
  }
169210
169551
 
169211
- // src/matter/endpoints/legacy/binary-sensor/index.ts
169212
- init_dist();
169213
- init_esm();
169214
-
169215
169552
  // ../../node_modules/.pnpm/@matter+main@0.16.10/node_modules/@matter/main/dist/esm/forwards/behaviors/boolean-state.js
169216
169553
  init_nodejs();
169217
169554
 
@@ -169281,6 +169618,10 @@ var ContactSensorWithBatteryType = ContactSensorDevice.with(
169281
169618
  })
169282
169619
  );
169283
169620
 
169621
+ // src/matter/endpoints/legacy/binary-sensor/index.ts
169622
+ init_dist();
169623
+ init_esm();
169624
+
169284
169625
  // src/matter/endpoints/legacy/binary-sensor/motion-sensor.ts
169285
169626
  init_home_assistant_entity_behavior();
169286
169627
 
@@ -169741,7 +170082,7 @@ var ClimateFanControlServer = FanControlServer2(config3).with(
169741
170082
  );
169742
170083
 
169743
170084
  // src/matter/endpoints/legacy/climate/behaviors/climate-humidity-measurement-server.ts
169744
- var humidityConfig2 = {
170085
+ var humidityConfig3 = {
169745
170086
  getValue(entity) {
169746
170087
  const attributes7 = entity.attributes;
169747
170088
  const humidity = attributes7.current_humidity;
@@ -169751,11 +170092,18 @@ var humidityConfig2 = {
169751
170092
  return +humidity;
169752
170093
  }
169753
170094
  };
169754
- var ClimateHumidityMeasurementServer = HumidityMeasurementServer(humidityConfig2);
170095
+ var ClimateHumidityMeasurementServer = HumidityMeasurementServer(humidityConfig3);
169755
170096
 
169756
170097
  // src/matter/endpoints/legacy/climate/behaviors/climate-on-off-server.ts
170098
+ init_home_assistant_entity_behavior();
169757
170099
  var ClimateOnOffServer = OnOffServer2({
169758
- turnOn: () => ({ action: "climate.turn_on" }),
170100
+ turnOn: (_value, agent) => {
170101
+ const entity = agent.get(HomeAssistantEntityBehavior).entity;
170102
+ if (entity.state.state !== "off") {
170103
+ return void 0;
170104
+ }
170105
+ return { action: "climate.turn_on" };
170106
+ },
169759
170107
  turnOff: () => ({ action: "climate.turn_off" })
169760
170108
  }).with("Lighting");
169761
170109
 
@@ -169932,7 +170280,8 @@ var ThermostatServerBase = class extends FullFeaturedBase {
169932
170280
  logger174.debug(
169933
170281
  `update: limits heat=[${minHeatLimit}, ${maxHeatLimit}], cool=[${minCoolLimit}, ${maxCoolLimit}], systemMode=${systemMode}, runningMode=${runningMode}`
169934
170282
  );
169935
- const controlSequence = config10.getControlSequence(entity.state, this.agent);
170283
+ let controlSequence = config10.getControlSequence(entity.state, this.agent);
170284
+ controlSequence = this.clampControlSequence(controlSequence);
169936
170285
  this.internal.controlSequenceOfOperation = controlSequence;
169937
170286
  applyPatchState(this.state, {
169938
170287
  ...this.features.heating ? {
@@ -170187,6 +170536,26 @@ var ThermostatServerBase = class extends FullFeaturedBase {
170187
170536
  }
170188
170537
  }
170189
170538
  }
170539
+ clampControlSequence(value) {
170540
+ const hasHeat = this.features.heating;
170541
+ const hasCool = this.features.cooling;
170542
+ const needsHeat = value === Thermostat3.ControlSequenceOfOperation.HeatingOnly || value === Thermostat3.ControlSequenceOfOperation.HeatingWithReheat;
170543
+ const needsCool = value === Thermostat3.ControlSequenceOfOperation.CoolingOnly || value === Thermostat3.ControlSequenceOfOperation.CoolingWithReheat;
170544
+ const needsBoth = value === Thermostat3.ControlSequenceOfOperation.CoolingAndHeating || value === Thermostat3.ControlSequenceOfOperation.CoolingAndHeatingWithReheat;
170545
+ if (needsHeat && !hasHeat) {
170546
+ return Thermostat3.ControlSequenceOfOperation.CoolingOnly;
170547
+ }
170548
+ if (needsCool && !hasCool) {
170549
+ return Thermostat3.ControlSequenceOfOperation.HeatingOnly;
170550
+ }
170551
+ if (needsBoth && !hasHeat) {
170552
+ return Thermostat3.ControlSequenceOfOperation.CoolingOnly;
170553
+ }
170554
+ if (needsBoth && !hasCool) {
170555
+ return Thermostat3.ControlSequenceOfOperation.HeatingOnly;
170556
+ }
170557
+ return value;
170558
+ }
170190
170559
  clampSetpoint(value, min, max, type) {
170191
170560
  const effectiveMin = min ?? 0;
170192
170561
  const effectiveMax = max ?? 5e3;
@@ -170435,7 +170804,7 @@ var config4 = {
170435
170804
  (m) => m === ClimateHvacMode.cool || m === ClimateHvacMode.heat_cool
170436
170805
  );
170437
170806
  const hasHeating = modes.some(
170438
- (m) => m === ClimateHvacMode.heat || m === ClimateHvacMode.heat_cool || m === ClimateHvacMode.auto
170807
+ (m) => m === ClimateHvacMode.heat || m === ClimateHvacMode.heat_cool
170439
170808
  );
170440
170809
  if (hasCooling && hasHeating) {
170441
170810
  const hasAutoMode = modes.includes(ClimateHvacMode.heat_cool) && (modes.includes(ClimateHvacMode.heat) || modes.includes(ClimateHvacMode.cool));
@@ -170495,25 +170864,6 @@ function ClimateThermostatServer(initialState = {}, features2) {
170495
170864
  }
170496
170865
 
170497
170866
  // src/matter/endpoints/legacy/climate/index.ts
170498
- var ClimatePowerSourceServer = PowerSourceServer2({
170499
- getBatteryPercent: (entity, agent) => {
170500
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
170501
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
170502
- if (batteryEntity) {
170503
- const stateProvider = agent.env.get(EntityStateProvider);
170504
- const battery = stateProvider.getBatteryPercent(batteryEntity);
170505
- if (battery != null) {
170506
- return Math.max(0, Math.min(100, battery));
170507
- }
170508
- }
170509
- const attrs = entity.attributes;
170510
- const level = attrs.battery_level ?? attrs.battery;
170511
- if (level == null || Number.isNaN(Number(level))) {
170512
- return null;
170513
- }
170514
- return Number(level);
170515
- }
170516
- });
170517
170867
  var ClimateDeviceType = (supportsOnOff, supportsHumidity, supportsFanMode, hasBattery, features2, initialState = {}) => {
170518
170868
  const additionalClusters = [];
170519
170869
  if (supportsOnOff) {
@@ -170523,7 +170873,7 @@ var ClimateDeviceType = (supportsOnOff, supportsHumidity, supportsFanMode, hasBa
170523
170873
  additionalClusters.push(ClimateHumidityMeasurementServer);
170524
170874
  }
170525
170875
  if (hasBattery) {
170526
- additionalClusters.push(ClimatePowerSourceServer);
170876
+ additionalClusters.push(DefaultPowerSourceServer);
170527
170877
  }
170528
170878
  const thermostatServer = ClimateThermostatServer(initialState, features2);
170529
170879
  if (supportsFanMode) {
@@ -170743,6 +171093,22 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
170743
171093
  );
170744
171094
  const currentTilt100ths = currentTilt != null ? currentTilt * 100 : null;
170745
171095
  const isStopped = movementStatus === MovementStatus.Stopped;
171096
+ const inferTarget = (current100ths, existing100ths) => {
171097
+ if (isStopped) return current100ths;
171098
+ if (movementStatus === MovementStatus.Opening) {
171099
+ if (existing100ths != null && current100ths != null && existing100ths < current100ths) {
171100
+ return existing100ths;
171101
+ }
171102
+ return 0;
171103
+ }
171104
+ if (movementStatus === MovementStatus.Closing) {
171105
+ if (existing100ths != null && current100ths != null && existing100ths > current100ths) {
171106
+ return existing100ths;
171107
+ }
171108
+ return 1e4;
171109
+ }
171110
+ return existing100ths ?? current100ths;
171111
+ };
170746
171112
  logger175.debug(
170747
171113
  `Cover update for ${entity.entity_id}: state=${state.state}, lift=${currentLift}%, tilt=${currentTilt}%, movement=${MovementStatus[movementStatus]}`
170748
171114
  );
@@ -170769,14 +171135,18 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
170769
171135
  ...this.features.positionAwareLift ? {
170770
171136
  currentPositionLiftPercentage: currentLift,
170771
171137
  currentPositionLiftPercent100ths: currentLift100ths,
170772
- // When stopped, target MUST equal current for controllers to show correct state
170773
- targetPositionLiftPercent100ths: isStopped ? currentLift100ths : this.state.targetPositionLiftPercent100ths ?? currentLift100ths
171138
+ targetPositionLiftPercent100ths: inferTarget(
171139
+ currentLift100ths,
171140
+ this.state.targetPositionLiftPercent100ths
171141
+ )
170774
171142
  } : {},
170775
171143
  ...this.features.positionAwareTilt ? {
170776
171144
  currentPositionTiltPercentage: currentTilt,
170777
171145
  currentPositionTiltPercent100ths: currentTilt100ths,
170778
- // When stopped, target MUST equal current for controllers to show correct state
170779
- targetPositionTiltPercent100ths: isStopped ? currentTilt100ths : this.state.targetPositionTiltPercent100ths ?? currentTilt100ths
171146
+ targetPositionTiltPercent100ths: inferTarget(
171147
+ currentTilt100ths,
171148
+ this.state.targetPositionTiltPercent100ths
171149
+ )
170780
171150
  } : {}
170781
171151
  }
170782
171152
  );
@@ -170998,6 +171368,9 @@ var adjustPositionForWriting2 = (position, agent) => {
170998
171368
  return adjustPositionForWriting(position, featureFlags, matterSem);
170999
171369
  };
171000
171370
  var shouldSwapOpenClose = (agent) => {
171371
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171372
+ const entitySwap = homeAssistant.state.mapping?.coverSwapOpenClose;
171373
+ if (entitySwap !== void 0) return entitySwap;
171001
171374
  const { featureFlags } = agent.env.get(BridgeDataProvider);
171002
171375
  return featureFlags?.coverSwapOpenClose === true;
171003
171376
  };
@@ -171094,25 +171467,6 @@ var CoverWindowCoveringServer = WindowCoveringServer2(config5);
171094
171467
 
171095
171468
  // src/matter/endpoints/legacy/cover/index.ts
171096
171469
  var logger177 = Logger.get("CoverDevice");
171097
- var CoverPowerSourceServer = PowerSourceServer2({
171098
- getBatteryPercent: (entity, agent) => {
171099
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171100
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171101
- if (batteryEntity) {
171102
- const stateProvider = agent.env.get(EntityStateProvider);
171103
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171104
- if (battery != null) {
171105
- return Math.max(0, Math.min(100, battery));
171106
- }
171107
- }
171108
- const attrs = entity.attributes;
171109
- const level = attrs.battery_level ?? attrs.battery;
171110
- if (level == null || Number.isNaN(Number(level))) {
171111
- return null;
171112
- }
171113
- return Number(level);
171114
- }
171115
- });
171116
171470
  var CoverDeviceType = (supportedFeatures, hasBattery, entityId) => {
171117
171471
  const features2 = /* @__PURE__ */ new Set();
171118
171472
  if (testBit(supportedFeatures, CoverSupportedFeatures.support_open)) {
@@ -171147,7 +171501,10 @@ var CoverDeviceType = (supportedFeatures, hasBattery, entityId) => {
171147
171501
  CoverWindowCoveringServer.with(...features2)
171148
171502
  ];
171149
171503
  if (hasBattery) {
171150
- return WindowCoveringDevice.with(...baseBehaviors, CoverPowerSourceServer);
171504
+ return WindowCoveringDevice.with(
171505
+ ...baseBehaviors,
171506
+ DefaultPowerSourceServer
171507
+ );
171151
171508
  }
171152
171509
  return WindowCoveringDevice.with(...baseBehaviors);
171153
171510
  };
@@ -171268,25 +171625,6 @@ function EventDevice(homeAssistantEntity) {
171268
171625
  // src/matter/endpoints/legacy/fan/index.ts
171269
171626
  init_dist();
171270
171627
  init_home_assistant_entity_behavior();
171271
- var FanPowerSourceServer = PowerSourceServer2({
171272
- getBatteryPercent: (entity, agent) => {
171273
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171274
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171275
- if (batteryEntity) {
171276
- const stateProvider = agent.env.get(EntityStateProvider);
171277
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171278
- if (battery != null) {
171279
- return Math.max(0, Math.min(100, battery));
171280
- }
171281
- }
171282
- const attrs = entity.attributes;
171283
- const level = attrs.battery_level ?? attrs.battery;
171284
- if (level == null || Number.isNaN(Number(level))) {
171285
- return null;
171286
- }
171287
- return Number(level);
171288
- }
171289
- });
171290
171628
  function FanDevice2(homeAssistantEntity) {
171291
171629
  const attributes7 = homeAssistantEntity.entity.state.attributes;
171292
171630
  const supportedFeatures = attributes7.supported_features ?? 0;
@@ -171306,7 +171644,7 @@ function FanDevice2(homeAssistantEntity) {
171306
171644
  BasicInformationServer2,
171307
171645
  HomeAssistantEntityBehavior,
171308
171646
  FanOnOffServer,
171309
- FanPowerSourceServer
171647
+ DefaultPowerSourceServer
171310
171648
  ) : OnOffPlugInUnitDevice.with(
171311
171649
  IdentifyServer2,
171312
171650
  BasicInformationServer2,
@@ -171341,7 +171679,7 @@ function FanDevice2(homeAssistantEntity) {
171341
171679
  HomeAssistantEntityBehavior,
171342
171680
  FanOnOffServer,
171343
171681
  FanFanControlServer.with(...features2),
171344
- FanPowerSourceServer
171682
+ DefaultPowerSourceServer
171345
171683
  ) : FanDevice.with(
171346
171684
  IdentifyServer2,
171347
171685
  BasicInformationServer2,
@@ -171866,25 +172204,6 @@ var ColorTemperatureLightType = ColorTemperatureLightDevice.with(
171866
172204
 
171867
172205
  // src/matter/endpoints/legacy/light/devices/dimmable-light.ts
171868
172206
  init_home_assistant_entity_behavior();
171869
- var LightPowerSourceServer = PowerSourceServer2({
171870
- getBatteryPercent: (entity, agent) => {
171871
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171872
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171873
- if (batteryEntity) {
171874
- const stateProvider = agent.env.get(EntityStateProvider);
171875
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171876
- if (battery != null) {
171877
- return Math.max(0, Math.min(100, battery));
171878
- }
171879
- }
171880
- const attrs = entity.attributes;
171881
- const level = attrs.battery_level ?? attrs.battery;
171882
- if (level == null || Number.isNaN(Number(level))) {
171883
- return null;
171884
- }
171885
- return Number(level);
171886
- }
171887
- });
171888
172207
  var DimmableLightType = DimmableLightDevice.with(
171889
172208
  IdentifyServer2,
171890
172209
  BasicInformationServer2,
@@ -171898,30 +172217,11 @@ var DimmableLightWithBatteryType = DimmableLightDevice.with(
171898
172217
  HomeAssistantEntityBehavior,
171899
172218
  LightOnOffServer,
171900
172219
  LightLevelControlServer,
171901
- LightPowerSourceServer
172220
+ DefaultPowerSourceServer
171902
172221
  );
171903
172222
 
171904
172223
  // src/matter/endpoints/legacy/light/devices/extended-color-light.ts
171905
172224
  init_home_assistant_entity_behavior();
171906
- var LightPowerSourceServer2 = PowerSourceServer2({
171907
- getBatteryPercent: (entity, agent) => {
171908
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171909
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171910
- if (batteryEntity) {
171911
- const stateProvider = agent.env.get(EntityStateProvider);
171912
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171913
- if (battery != null) {
171914
- return Math.max(0, Math.min(100, battery));
171915
- }
171916
- }
171917
- const attrs = entity.attributes;
171918
- const level = attrs.battery_level ?? attrs.battery;
171919
- if (level == null || Number.isNaN(Number(level))) {
171920
- return null;
171921
- }
171922
- return Number(level);
171923
- }
171924
- });
171925
172225
  var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBattery = false) => {
171926
172226
  const features2 = /* @__PURE__ */ new Set();
171927
172227
  if (supportsColorControl) {
@@ -171938,7 +172238,7 @@ var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBatt
171938
172238
  LightOnOffServer,
171939
172239
  LightLevelControlServer,
171940
172240
  LightColorControlServer.with(...features2),
171941
- LightPowerSourceServer2
172241
+ DefaultPowerSourceServer
171942
172242
  );
171943
172243
  }
171944
172244
  return ExtendedColorLightDevice.with(
@@ -171953,25 +172253,6 @@ var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBatt
171953
172253
 
171954
172254
  // src/matter/endpoints/legacy/light/devices/on-off-light-device.ts
171955
172255
  init_home_assistant_entity_behavior();
171956
- var LightPowerSourceServer3 = PowerSourceServer2({
171957
- getBatteryPercent: (entity, agent) => {
171958
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171959
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171960
- if (batteryEntity) {
171961
- const stateProvider = agent.env.get(EntityStateProvider);
171962
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171963
- if (battery != null) {
171964
- return Math.max(0, Math.min(100, battery));
171965
- }
171966
- }
171967
- const attrs = entity.attributes;
171968
- const level = attrs.battery_level ?? attrs.battery;
171969
- if (level == null || Number.isNaN(Number(level))) {
171970
- return null;
171971
- }
171972
- return Number(level);
171973
- }
171974
- });
171975
172256
  var OnOffLightType = OnOffLightDevice.with(
171976
172257
  IdentifyServer2,
171977
172258
  BasicInformationServer2,
@@ -171983,7 +172264,7 @@ var OnOffLightWithBatteryType = OnOffLightDevice.with(
171983
172264
  BasicInformationServer2,
171984
172265
  HomeAssistantEntityBehavior,
171985
172266
  LightOnOffServer,
171986
- LightPowerSourceServer3
172267
+ DefaultPowerSourceServer
171987
172268
  );
171988
172269
 
171989
172270
  // src/matter/endpoints/legacy/light/index.ts
@@ -172674,25 +172955,6 @@ var lockServerConfig = {
172674
172955
  unlock: () => ({ action: "lock.unlock" }),
172675
172956
  unlatch: () => ({ action: "lock.open" })
172676
172957
  };
172677
- var LockPowerSourceServer = PowerSourceServer2({
172678
- getBatteryPercent: (entity, agent) => {
172679
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
172680
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
172681
- if (batteryEntity) {
172682
- const stateProvider = agent.env.get(EntityStateProvider);
172683
- const battery = stateProvider.getBatteryPercent(batteryEntity);
172684
- if (battery != null) {
172685
- return Math.max(0, Math.min(100, battery));
172686
- }
172687
- }
172688
- const attrs = entity.attributes;
172689
- const level = attrs.battery_level ?? attrs.battery;
172690
- if (level == null || Number.isNaN(Number(level))) {
172691
- return null;
172692
- }
172693
- return Number(level);
172694
- }
172695
- });
172696
172958
  var LockDeviceType = DoorLockDevice.with(
172697
172959
  BasicInformationServer2,
172698
172960
  IdentifyServer2,
@@ -172704,7 +172966,7 @@ var LockWithBatteryDeviceType = DoorLockDevice.with(
172704
172966
  IdentifyServer2,
172705
172967
  HomeAssistantEntityBehavior,
172706
172968
  LockServerWithPin(lockServerConfig),
172707
- LockPowerSourceServer
172969
+ DefaultPowerSourceServer
172708
172970
  );
172709
172971
  var LockWithUnlatchDeviceType = DoorLockDevice.with(
172710
172972
  BasicInformationServer2,
@@ -172717,7 +172979,7 @@ var LockWithUnlatchAndBatteryDeviceType = DoorLockDevice.with(
172717
172979
  IdentifyServer2,
172718
172980
  HomeAssistantEntityBehavior,
172719
172981
  LockServerWithPinAndUnbolt(lockServerConfig),
172720
- LockPowerSourceServer
172982
+ DefaultPowerSourceServer
172721
172983
  );
172722
172984
  function LockDevice(homeAssistantEntity) {
172723
172985
  const attrs = homeAssistantEntity.entity.state.attributes;
@@ -173293,8 +173555,20 @@ var PumpType = PumpDevice.with(
173293
173555
  PumpOnOffServer,
173294
173556
  PumpConfigurationAndControlServer2
173295
173557
  );
173558
+ var PumpWithBatteryType = PumpDevice.with(
173559
+ IdentifyServer2,
173560
+ BasicInformationServer2,
173561
+ HomeAssistantEntityBehavior,
173562
+ PumpOnOffServer,
173563
+ PumpConfigurationAndControlServer2,
173564
+ DefaultPowerSourceServer
173565
+ );
173296
173566
  function PumpEndpoint(homeAssistantEntity) {
173297
- return PumpType.set({ homeAssistantEntity });
173567
+ const attrs = homeAssistantEntity.entity.state.attributes;
173568
+ const hasBatteryAttr = attrs.battery_level != null || attrs.battery != null;
173569
+ const hasBatteryEntity = !!homeAssistantEntity.mapping?.batteryEntity;
173570
+ const device = hasBatteryAttr || hasBatteryEntity ? PumpWithBatteryType : PumpType;
173571
+ return device.set({ homeAssistantEntity });
173298
173572
  }
173299
173573
 
173300
173574
  // src/matter/endpoints/legacy/remote/index.ts
@@ -173339,12 +173613,11 @@ function SceneDevice(homeAssistantEntity) {
173339
173613
  // src/matter/endpoints/legacy/script/index.ts
173340
173614
  init_home_assistant_entity_behavior();
173341
173615
  var ScriptOnOffServer = OnOffServer2({
173616
+ isOn: () => false,
173342
173617
  turnOn: () => ({
173343
173618
  action: "script.turn_on"
173344
173619
  }),
173345
- turnOff: () => ({
173346
- action: "script.turn_off"
173347
- })
173620
+ turnOff: null
173348
173621
  });
173349
173622
  var ScriptDeviceType = OnOffPlugInUnitDevice.with(
173350
173623
  BasicInformationServer2,
@@ -174816,7 +175089,7 @@ var Pm25SensorType = AirQualitySensorDevice.with(
174816
175089
 
174817
175090
  // src/matter/endpoints/legacy/sensor/devices/temperature-humidity-sensor.ts
174818
175091
  init_home_assistant_entity_behavior();
174819
- var temperatureConfig2 = {
175092
+ var temperatureConfig3 = {
174820
175093
  getValue(entity, agent) {
174821
175094
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
174822
175095
  const state = entity.state;
@@ -174831,7 +175104,7 @@ var temperatureConfig2 = {
174831
175104
  );
174832
175105
  }
174833
175106
  };
174834
- var humidityConfig3 = {
175107
+ var humidityConfig4 = {
174835
175108
  getValue(_entity, agent) {
174836
175109
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
174837
175110
  const humidityEntity = homeAssistant.state.mapping?.humidityEntity;
@@ -174881,38 +175154,38 @@ var TemperatureHumiditySensorType = TemperatureSensorDevice.with(
174881
175154
  BasicInformationServer2,
174882
175155
  IdentifyServer2,
174883
175156
  HomeAssistantEntityBehavior,
174884
- TemperatureMeasurementServer2(temperatureConfig2),
174885
- HumidityMeasurementServer(humidityConfig3)
175157
+ TemperatureMeasurementServer2(temperatureConfig3),
175158
+ HumidityMeasurementServer(humidityConfig4)
174886
175159
  );
174887
175160
  var TemperatureHumiditySensorWithBatteryType = TemperatureSensorDevice.with(
174888
175161
  BasicInformationServer2,
174889
175162
  IdentifyServer2,
174890
175163
  HomeAssistantEntityBehavior,
174891
- TemperatureMeasurementServer2(temperatureConfig2),
174892
- HumidityMeasurementServer(humidityConfig3),
175164
+ TemperatureMeasurementServer2(temperatureConfig3),
175165
+ HumidityMeasurementServer(humidityConfig4),
174893
175166
  PowerSourceServer2(batteryConfig4)
174894
175167
  );
174895
175168
  var TemperatureHumidityPressureSensorType = TemperatureSensorDevice.with(
174896
175169
  BasicInformationServer2,
174897
175170
  IdentifyServer2,
174898
175171
  HomeAssistantEntityBehavior,
174899
- TemperatureMeasurementServer2(temperatureConfig2),
174900
- HumidityMeasurementServer(humidityConfig3),
175172
+ TemperatureMeasurementServer2(temperatureConfig3),
175173
+ HumidityMeasurementServer(humidityConfig4),
174901
175174
  PressureMeasurementServer2(pressureConfig2)
174902
175175
  );
174903
175176
  var TemperatureHumidityPressureSensorWithBatteryType = TemperatureSensorDevice.with(
174904
175177
  BasicInformationServer2,
174905
175178
  IdentifyServer2,
174906
175179
  HomeAssistantEntityBehavior,
174907
- TemperatureMeasurementServer2(temperatureConfig2),
174908
- HumidityMeasurementServer(humidityConfig3),
175180
+ TemperatureMeasurementServer2(temperatureConfig3),
175181
+ HumidityMeasurementServer(humidityConfig4),
174909
175182
  PressureMeasurementServer2(pressureConfig2),
174910
175183
  PowerSourceServer2(batteryConfig4)
174911
175184
  );
174912
175185
 
174913
175186
  // src/matter/endpoints/legacy/sensor/devices/temperature-pressure-sensor.ts
174914
175187
  init_home_assistant_entity_behavior();
174915
- var temperatureConfig3 = {
175188
+ var temperatureConfig4 = {
174916
175189
  getValue(entity, agent) {
174917
175190
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
174918
175191
  const state = entity.state;
@@ -174963,14 +175236,14 @@ var TemperaturePressureSensorType = TemperatureSensorDevice.with(
174963
175236
  BasicInformationServer2,
174964
175237
  IdentifyServer2,
174965
175238
  HomeAssistantEntityBehavior,
174966
- TemperatureMeasurementServer2(temperatureConfig3),
175239
+ TemperatureMeasurementServer2(temperatureConfig4),
174967
175240
  PressureMeasurementServer2(pressureConfig3)
174968
175241
  );
174969
175242
  var TemperaturePressureSensorWithBatteryType = TemperatureSensorDevice.with(
174970
175243
  BasicInformationServer2,
174971
175244
  IdentifyServer2,
174972
175245
  HomeAssistantEntityBehavior,
174973
- TemperatureMeasurementServer2(temperatureConfig3),
175246
+ TemperatureMeasurementServer2(temperatureConfig4),
174974
175247
  PressureMeasurementServer2(pressureConfig3),
174975
175248
  PowerSourceServer2(batteryConfig5)
174976
175249
  );
@@ -175076,6 +175349,16 @@ function SensorDevice(homeAssistantEntity) {
175076
175349
  return void 0;
175077
175350
  }
175078
175351
 
175352
+ // src/matter/endpoints/legacy/switch/dimmable-plugin-unit.ts
175353
+ init_home_assistant_entity_behavior();
175354
+ var DimmablePlugInUnitType = DimmablePlugInUnitDevice.with(
175355
+ IdentifyServer2,
175356
+ BasicInformationServer2,
175357
+ HomeAssistantEntityBehavior,
175358
+ LightOnOffServer,
175359
+ LightLevelControlServer
175360
+ );
175361
+
175079
175362
  // src/matter/endpoints/legacy/switch/index.ts
175080
175363
  init_home_assistant_entity_behavior();
175081
175364
  var SwitchOnOffServer = OnOffServer2();
@@ -175090,25 +175373,7 @@ var SwitchWithBatteryEndpointType = OnOffPlugInUnitDevice.with(
175090
175373
  IdentifyServer2,
175091
175374
  HomeAssistantEntityBehavior,
175092
175375
  SwitchOnOffServer,
175093
- PowerSourceServer2({
175094
- getBatteryPercent: (entity, agent) => {
175095
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
175096
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
175097
- if (batteryEntity) {
175098
- const stateProvider = agent.env.get(EntityStateProvider);
175099
- const battery = stateProvider.getBatteryPercent(batteryEntity);
175100
- if (battery != null) {
175101
- return Math.max(0, Math.min(100, battery));
175102
- }
175103
- }
175104
- const attrs = entity.attributes;
175105
- const level = attrs.battery_level ?? attrs.battery;
175106
- if (level == null || Number.isNaN(Number(level))) {
175107
- return null;
175108
- }
175109
- return Number(level);
175110
- }
175111
- })
175376
+ DefaultPowerSourceServer
175112
175377
  );
175113
175378
  function SwitchDevice(homeAssistantEntity) {
175114
175379
  const attrs = homeAssistantEntity.entity.state.attributes;
@@ -175703,9 +175968,51 @@ function createCustomServiceAreaServer(customAreas) {
175703
175968
  currentArea: null
175704
175969
  });
175705
175970
  }
175971
+ function createCleanAreaServiceAreaServer(cleanAreaRooms) {
175972
+ const supportedAreas = cleanAreaRooms.map((room) => ({
175973
+ areaId: room.areaId,
175974
+ mapId: null,
175975
+ areaInfo: {
175976
+ locationInfo: {
175977
+ locationName: room.name,
175978
+ floorNumber: null,
175979
+ areaType: null
175980
+ },
175981
+ landmarkInfo: null
175982
+ }
175983
+ }));
175984
+ logger190.info(
175985
+ `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA: ${cleanAreaRooms.map((r) => r.name).join(", ")}`
175986
+ );
175987
+ return ServiceAreaServer2({
175988
+ supportedAreas,
175989
+ selectedAreas: [],
175990
+ currentArea: null
175991
+ });
175992
+ }
175706
175993
 
175707
175994
  // src/matter/endpoints/legacy/vacuum/behaviors/vacuum-rvc-run-mode-server.ts
175708
175995
  var logger191 = Logger.get("VacuumRvcRunModeServer");
175996
+ function buildValetudoSegmentAction(vacuumEntityId, segmentIds, valetudoIdentifier) {
175997
+ const identifier = valetudoIdentifier || vacuumEntityId.replace(/^vacuum\.valetudo_/, "");
175998
+ const topic = `valetudo/${identifier}/MapSegmentationCapability/clean/set`;
175999
+ logger191.info(
176000
+ `Valetudo: mqtt.publish to ${topic}, segments: ${segmentIds.join(", ")}`
176001
+ );
176002
+ return {
176003
+ action: "mqtt.publish",
176004
+ target: false,
176005
+ data: {
176006
+ topic,
176007
+ payload: JSON.stringify({
176008
+ action: "start_segment_action",
176009
+ segment_ids: segmentIds.map(String),
176010
+ iterations: 1,
176011
+ customOrder: true
176012
+ })
176013
+ }
176014
+ };
176015
+ }
175709
176016
  function buildSupportedModes2(attributes7, includeUnnamedRooms = false, customAreas) {
175710
176017
  const modes = [
175711
176018
  {
@@ -175774,6 +176081,16 @@ function handleCustomServiceAreas(selectedAreas, customAreas, homeAssistant, ser
175774
176081
  data: first.data
175775
176082
  };
175776
176083
  }
176084
+ function resolveCleanAreaIds(selectedAreas, cleanAreaRooms) {
176085
+ const haAreaIds = [];
176086
+ for (const areaId of selectedAreas) {
176087
+ const room = cleanAreaRooms.find((r) => r.areaId === areaId);
176088
+ if (room) {
176089
+ haAreaIds.push(room.haAreaId);
176090
+ }
176091
+ }
176092
+ return haAreaIds;
176093
+ }
175777
176094
  var vacuumRvcRunModeConfig = {
175778
176095
  getCurrentMode: (entity) => {
175779
176096
  const state = entity.state;
@@ -175812,6 +176129,20 @@ var vacuumRvcRunModeConfig = {
175812
176129
  serviceArea
175813
176130
  );
175814
176131
  }
176132
+ const cleanAreaRooms = homeAssistant.state.mapping?.cleanAreaRooms;
176133
+ if (cleanAreaRooms && cleanAreaRooms.length > 0) {
176134
+ const haAreaIds = resolveCleanAreaIds(selectedAreas, cleanAreaRooms);
176135
+ serviceArea.state.selectedAreas = [];
176136
+ if (haAreaIds.length > 0) {
176137
+ logger191.info(
176138
+ `CLEAN_AREA: cleaning HA areas: ${haAreaIds.join(", ")}`
176139
+ );
176140
+ return {
176141
+ action: "vacuum.clean_area",
176142
+ data: { cleaning_area_id: haAreaIds }
176143
+ };
176144
+ }
176145
+ }
175815
176146
  const roomEntities = homeAssistant.state.mapping?.roomEntities;
175816
176147
  if (roomEntities && roomEntities.length > 0) {
175817
176148
  const buttonEntityIds = [];
@@ -175840,6 +176171,15 @@ var vacuumRvcRunModeConfig = {
175840
176171
  };
175841
176172
  }
175842
176173
  }
176174
+ const vacuumEntityId = homeAssistant.entityId;
176175
+ if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
176176
+ serviceArea.state.selectedAreas = [];
176177
+ return buildValetudoSegmentAction(
176178
+ vacuumEntityId,
176179
+ selectedAreas,
176180
+ homeAssistant.state.mapping?.valetudoIdentifier
176181
+ );
176182
+ }
175843
176183
  const rooms = parseVacuumRooms(attributes7);
175844
176184
  const roomIds = [];
175845
176185
  let targetMapName;
@@ -175857,25 +176197,6 @@ var vacuumRvcRunModeConfig = {
175857
176197
  `Starting cleaning with selected areas: ${roomIds.join(", ")}`
175858
176198
  );
175859
176199
  serviceArea.state.selectedAreas = [];
175860
- const vacuumEntityId = homeAssistant.entityId;
175861
- if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
175862
- const identifier = vacuumEntityId.replace(/^vacuum\.valetudo_/, "");
175863
- logger191.info(
175864
- `Valetudo vacuum: Using mqtt.publish segment_cleanup for rooms: ${roomIds.join(", ")}`
175865
- );
175866
- return {
175867
- action: "mqtt.publish",
175868
- target: false,
175869
- data: {
175870
- topic: `valetudo/${identifier}/MapSegmentationCapability/clean/set`,
175871
- payload: JSON.stringify({
175872
- segment_ids: roomIds.map(String),
175873
- iterations: 1,
175874
- customOrder: true
175875
- })
175876
- }
175877
- };
175878
- }
175879
176200
  if (isDreameVacuum(attributes7)) {
175880
176201
  if (targetMapName) {
175881
176202
  const vacName = vacuumEntityId.replace("vacuum.", "");
@@ -175945,6 +176266,23 @@ var vacuumRvcRunModeConfig = {
175945
176266
  const entity = homeAssistant.entity;
175946
176267
  const attributes7 = entity.state.attributes;
175947
176268
  logger191.info(`cleanRoom called: roomMode=${roomMode}`);
176269
+ const cleanAreaRooms = homeAssistant.state.mapping?.cleanAreaRooms;
176270
+ if (cleanAreaRooms && cleanAreaRooms.length > 0) {
176271
+ const sorted = [...cleanAreaRooms].sort(
176272
+ (a, b) => a.name.localeCompare(b.name)
176273
+ );
176274
+ const areaIndex = roomMode - ROOM_MODE_BASE2 - 1;
176275
+ if (areaIndex >= 0 && areaIndex < sorted.length) {
176276
+ const area = sorted[areaIndex];
176277
+ logger191.info(
176278
+ `cleanRoom: CLEAN_AREA "${area.name}" \u2192 vacuum.clean_area(${area.haAreaId})`
176279
+ );
176280
+ return {
176281
+ action: "vacuum.clean_area",
176282
+ data: { cleaning_area_id: [area.haAreaId] }
176283
+ };
176284
+ }
176285
+ }
175948
176286
  const customAreas = homeAssistant.state.mapping?.customServiceAreas;
175949
176287
  if (customAreas && customAreas.length > 0) {
175950
176288
  const sorted = [...customAreas].sort(
@@ -175963,6 +176301,15 @@ var vacuumRvcRunModeConfig = {
175963
176301
  };
175964
176302
  }
175965
176303
  }
176304
+ const vacuumEntityId = entity.entity_id;
176305
+ if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
176306
+ const segmentId = getRoomIdFromMode(roomMode);
176307
+ return buildValetudoSegmentAction(
176308
+ vacuumEntityId,
176309
+ [segmentId],
176310
+ homeAssistant.state.mapping?.valetudoIdentifier
176311
+ );
176312
+ }
175966
176313
  const rooms = parseVacuumRooms(attributes7);
175967
176314
  const numericIdFromMode = getRoomIdFromMode(roomMode);
175968
176315
  logger191.info(
@@ -175971,25 +176318,6 @@ var vacuumRvcRunModeConfig = {
175971
176318
  const room = rooms.find((r) => getRoomModeValue(r) === roomMode);
175972
176319
  if (room) {
175973
176320
  const commandId3 = room.originalId ?? room.id;
175974
- const vacuumEntityId = entity.entity_id;
175975
- if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
175976
- const identifier = vacuumEntityId.replace(/^vacuum\.valetudo_/, "");
175977
- logger191.info(
175978
- `Valetudo vacuum: Using mqtt.publish segment_cleanup for room ${room.name} (id: ${commandId3})`
175979
- );
175980
- return {
175981
- action: "mqtt.publish",
175982
- target: false,
175983
- data: {
175984
- topic: `valetudo/${identifier}/MapSegmentationCapability/clean/set`,
175985
- payload: JSON.stringify({
175986
- segment_ids: [String(commandId3)],
175987
- iterations: 1,
175988
- customOrder: true
175989
- })
175990
- }
175991
- };
175992
- }
175993
176321
  if (isDreameVacuum(attributes7)) {
175994
176322
  if (room.mapName) {
175995
176323
  const vacuumName = vacuumEntityId.replace("vacuum.", "");
@@ -176080,6 +176408,39 @@ function createVacuumRvcRunModeServer(attributes7, includeUnnamedRooms = false,
176080
176408
  currentMode: 0 /* Idle */
176081
176409
  });
176082
176410
  }
176411
+ function createCleanAreaRvcRunModeServer(cleanAreaRooms) {
176412
+ const modes = [
176413
+ {
176414
+ label: "Idle",
176415
+ mode: 0 /* Idle */,
176416
+ modeTags: [{ value: RvcRunMode3.ModeTag.Idle }]
176417
+ },
176418
+ {
176419
+ label: "Cleaning",
176420
+ mode: 1 /* Cleaning */,
176421
+ modeTags: [{ value: RvcRunMode3.ModeTag.Cleaning }]
176422
+ }
176423
+ ];
176424
+ const sorted = [...cleanAreaRooms].sort(
176425
+ (a, b) => a.name.localeCompare(b.name)
176426
+ );
176427
+ for (let i = 0; i < sorted.length; i++) {
176428
+ const modeValue = ROOM_MODE_BASE2 + i + 1;
176429
+ if (modeValue > 255) continue;
176430
+ modes.push({
176431
+ label: sorted[i].name,
176432
+ mode: modeValue,
176433
+ modeTags: [{ value: RvcRunMode3.ModeTag.Cleaning }]
176434
+ });
176435
+ }
176436
+ logger191.info(
176437
+ `Creating CLEAN_AREA RvcRunModeServer with ${cleanAreaRooms.length} HA areas, ${modes.length} total modes`
176438
+ );
176439
+ return RvcRunModeServer2(vacuumRvcRunModeConfig, {
176440
+ supportedModes: modes,
176441
+ currentMode: 0 /* Idle */
176442
+ });
176443
+ }
176083
176444
  var VacuumRvcRunModeServer = RvcRunModeServer2(vacuumRvcRunModeConfig);
176084
176445
 
176085
176446
  // src/matter/endpoints/legacy/vacuum/behaviors/vacuum-on-off-server.ts
@@ -176105,10 +176466,14 @@ var VacuumPowerSourceServer = PowerSourceServer2({
176105
176466
  }
176106
176467
  const attributes7 = entity.attributes;
176107
176468
  const batteryLevel = attributes7.battery_level ?? attributes7.battery;
176108
- if (batteryLevel == null || typeof batteryLevel !== "number") {
176469
+ if (batteryLevel == null) {
176109
176470
  return null;
176110
176471
  }
176111
- return batteryLevel;
176472
+ if (typeof batteryLevel === "number") {
176473
+ return batteryLevel;
176474
+ }
176475
+ const parsed = Number.parseFloat(String(batteryLevel));
176476
+ return Number.isNaN(parsed) ? null : parsed;
176112
176477
  },
176113
176478
  isCharging(entity) {
176114
176479
  const state = entity.state;
@@ -177077,8 +177442,9 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
177077
177442
  logger196.info(
177078
177443
  `Creating vacuum endpoint for ${entityId}, mapping: ${JSON.stringify(homeAssistantEntity.mapping ?? "none")}`
177079
177444
  );
177445
+ const cleanAreaRooms = homeAssistantEntity.mapping?.cleanAreaRooms;
177080
177446
  let device = VacuumEndpointType.with(
177081
- createVacuumRvcRunModeServer(
177447
+ cleanAreaRooms && cleanAreaRooms.length > 0 ? createCleanAreaRvcRunModeServer(cleanAreaRooms) : createVacuumRvcRunModeServer(
177082
177448
  attributes7,
177083
177449
  false,
177084
177450
  customAreas && customAreas.length > 0 ? customAreas : void 0
@@ -177092,9 +177458,14 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
177092
177458
  const roomEntities = homeAssistantEntity.mapping?.roomEntities;
177093
177459
  const rooms = parseVacuumRooms(attributes7);
177094
177460
  logger196.info(
177095
- `${entityId}: customAreas=${customAreas?.length ?? 0}, roomEntities=${JSON.stringify(roomEntities ?? [])}, parsedRooms=${rooms.length}`
177461
+ `${entityId}: customAreas=${customAreas?.length ?? 0}, roomEntities=${JSON.stringify(roomEntities ?? [])}, parsedRooms=${rooms.length}, cleanAreaRooms=${cleanAreaRooms?.length ?? 0}`
177096
177462
  );
177097
- if (customAreas && customAreas.length > 0) {
177463
+ if (cleanAreaRooms && cleanAreaRooms.length > 0) {
177464
+ logger196.info(
177465
+ `${entityId}: Adding ServiceArea (${cleanAreaRooms.length} HA areas via CLEAN_AREA)`
177466
+ );
177467
+ device = device.with(createCleanAreaServiceAreaServer(cleanAreaRooms));
177468
+ } else if (customAreas && customAreas.length > 0) {
177098
177469
  logger196.info(
177099
177470
  `${entityId}: Adding ServiceArea (${customAreas.length} custom areas)`
177100
177471
  );
@@ -177199,8 +177570,19 @@ var ValveEndpointType = WaterValveDevice.with(
177199
177570
  HomeAssistantEntityBehavior,
177200
177571
  ValveServer
177201
177572
  );
177573
+ var ValveWithBatteryEndpointType = WaterValveDevice.with(
177574
+ BasicInformationServer2,
177575
+ IdentifyServer2,
177576
+ HomeAssistantEntityBehavior,
177577
+ ValveServer,
177578
+ DefaultPowerSourceServer
177579
+ );
177202
177580
  function ValveDevice(homeAssistantEntity) {
177203
- return ValveEndpointType.set({ homeAssistantEntity });
177581
+ const attrs = homeAssistantEntity.entity.state.attributes;
177582
+ const hasBatteryAttr = attrs.battery_level != null || attrs.battery != null;
177583
+ const hasBatteryEntity = !!homeAssistantEntity.mapping?.batteryEntity;
177584
+ const device = hasBatteryAttr || hasBatteryEntity ? ValveWithBatteryEndpointType : ValveEndpointType;
177585
+ return device.set({ homeAssistantEntity });
177204
177586
  }
177205
177587
 
177206
177588
  // src/matter/endpoints/legacy/water-heater/index.ts
@@ -177321,6 +177703,7 @@ function WaterHeaterDevice(homeAssistantEntity) {
177321
177703
  }
177322
177704
 
177323
177705
  // src/matter/endpoints/legacy/create-legacy-endpoint-type.ts
177706
+ var legacyLogger = Logger.get("LegacyEndpointType");
177324
177707
  function createLegacyEndpointType(entity, mapping, areaName, options) {
177325
177708
  const domain = entity.entity_id.split(".")[0];
177326
177709
  const customName = mapping?.customName;
@@ -177340,10 +177723,20 @@ function createLegacyEndpointType(entity, mapping, areaName, options) {
177340
177723
  );
177341
177724
  } else {
177342
177725
  const factory = deviceCtrs[domain];
177343
- if (!factory) {
177726
+ if (factory) {
177727
+ type = factory({ entity, customName, mapping });
177728
+ } else if (options?.pluginDomainMappings?.has(domain)) {
177729
+ const mappedType = options.pluginDomainMappings.get(domain);
177730
+ const mappedFactory = matterDeviceTypeFactories[mappedType];
177731
+ if (mappedFactory) {
177732
+ legacyLogger.info(
177733
+ `Using plugin domain mapping for "${domain}" \u2192 "${mappedType}"`
177734
+ );
177735
+ type = mappedFactory({ entity, customName, mapping });
177736
+ }
177737
+ } else {
177344
177738
  return void 0;
177345
177739
  }
177346
- type = factory({ entity, customName, mapping });
177347
177740
  }
177348
177741
  }
177349
177742
  if (!type) {
@@ -177415,6 +177808,9 @@ var matterDeviceTypeFactories = {
177415
177808
  }
177416
177809
  return SwitchDevice(ha);
177417
177810
  },
177811
+ dimmable_plugin_unit: (ha) => DimmablePlugInUnitType.set({
177812
+ homeAssistantEntity: { entity: ha.entity, customName: ha.customName }
177813
+ }),
177418
177814
  on_off_switch: SwitchDevice,
177419
177815
  door_lock: LockDevice,
177420
177816
  window_covering: CoverDevice,
@@ -177470,9 +177866,15 @@ var matterDeviceTypeFactories = {
177470
177866
  electrical_sensor: (ha) => ElectricalSensorType.set({
177471
177867
  homeAssistantEntity: { entity: ha.entity, customName: ha.customName }
177472
177868
  }),
177869
+ contact_sensor: (ha) => ContactSensorType.set({
177870
+ homeAssistantEntity: { entity: ha.entity, customName: ha.customName }
177871
+ }),
177473
177872
  motion_sensor: (ha) => MotionSensorType.set({
177474
177873
  homeAssistantEntity: { entity: ha.entity, customName: ha.customName }
177475
177874
  }),
177875
+ occupancy_sensor: (ha) => OccupancySensorType.set({
177876
+ homeAssistantEntity: { entity: ha.entity, customName: ha.customName }
177877
+ }),
177476
177878
  mode_select: SelectDevice,
177477
177879
  water_valve: ValveDevice,
177478
177880
  pump: PumpEndpoint,
@@ -177492,10 +177894,192 @@ var matterDeviceTypeFactories = {
177492
177894
  })
177493
177895
  };
177494
177896
 
177897
+ // src/matter/endpoints/composed/user-composed-endpoint.ts
177898
+ var logger198 = Logger.get("UserComposedEndpoint");
177899
+ function createEndpointId4(entityId, customName) {
177900
+ const baseName = customName || entityId;
177901
+ return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
177902
+ }
177903
+ function buildEntityPayload3(registry2, entityId) {
177904
+ const state = registry2.initialState(entityId);
177905
+ if (!state) return void 0;
177906
+ const entity = registry2.entity(entityId);
177907
+ const deviceRegistry = registry2.deviceOf(entityId);
177908
+ return {
177909
+ entity_id: entityId,
177910
+ state,
177911
+ registry: entity,
177912
+ deviceRegistry
177913
+ };
177914
+ }
177915
+ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
177916
+ entityId;
177917
+ mappedEntityIds;
177918
+ subEndpoints = /* @__PURE__ */ new Map();
177919
+ lastStates = /* @__PURE__ */ new Map();
177920
+ debouncedUpdates = /* @__PURE__ */ new Map();
177921
+ static async create(config10) {
177922
+ const { registry: registry2, primaryEntityId, composedEntities } = config10;
177923
+ const primaryPayload = buildEntityPayload3(registry2, primaryEntityId);
177924
+ if (!primaryPayload) return void 0;
177925
+ let parentType = BridgedNodeEndpoint.with(
177926
+ BasicInformationServer2,
177927
+ IdentifyServer2,
177928
+ HomeAssistantEntityBehavior
177929
+ );
177930
+ if (config10.areaName) {
177931
+ const truncatedName = config10.areaName.length > 16 ? config10.areaName.substring(0, 16) : config10.areaName;
177932
+ parentType = parentType.with(
177933
+ FixedLabelServer.set({
177934
+ labelList: [{ label: "room", value: truncatedName }]
177935
+ })
177936
+ );
177937
+ }
177938
+ const endpointId = createEndpointId4(primaryEntityId, config10.customName);
177939
+ const parts = [];
177940
+ const subEndpointMap = /* @__PURE__ */ new Map();
177941
+ const mappedIds = [];
177942
+ const primaryType = createLegacyEndpointType(
177943
+ primaryPayload,
177944
+ config10.mapping,
177945
+ void 0,
177946
+ { vacuumOnOff: registry2.isVacuumOnOffEnabled() }
177947
+ );
177948
+ if (!primaryType) {
177949
+ logger198.warn(
177950
+ `Cannot create endpoint type for primary entity ${primaryEntityId}`
177951
+ );
177952
+ return void 0;
177953
+ }
177954
+ const primarySub = new Endpoint(primaryType, {
177955
+ id: `${endpointId}_primary`
177956
+ });
177957
+ parts.push(primarySub);
177958
+ subEndpointMap.set(primaryEntityId, primarySub);
177959
+ for (let i = 0; i < composedEntities.length; i++) {
177960
+ const sub = composedEntities[i];
177961
+ if (!sub.entityId) continue;
177962
+ const subPayload = buildEntityPayload3(registry2, sub.entityId);
177963
+ if (!subPayload) {
177964
+ logger198.warn(
177965
+ `Cannot find entity state for composed sub-entity ${sub.entityId}`
177966
+ );
177967
+ continue;
177968
+ }
177969
+ const subMapping = {
177970
+ entityId: sub.entityId,
177971
+ matterDeviceType: sub.matterDeviceType
177972
+ };
177973
+ const subType = createLegacyEndpointType(subPayload, subMapping);
177974
+ if (!subType) {
177975
+ logger198.warn(
177976
+ `Cannot create endpoint type for composed sub-entity ${sub.entityId}`
177977
+ );
177978
+ continue;
177979
+ }
177980
+ const subEndpoint = new Endpoint(subType, {
177981
+ id: `${endpointId}_sub_${i}`
177982
+ });
177983
+ parts.push(subEndpoint);
177984
+ subEndpointMap.set(sub.entityId, subEndpoint);
177985
+ mappedIds.push(sub.entityId);
177986
+ }
177987
+ if (parts.length < 2) {
177988
+ logger198.warn(
177989
+ `User composed device ${primaryEntityId}: only ${parts.length} sub-endpoint(s), need at least 2 (primary + one sub-entity). Falling back to standalone.`
177990
+ );
177991
+ return void 0;
177992
+ }
177993
+ const parentTypeWithState = parentType.set({
177994
+ homeAssistantEntity: {
177995
+ entity: primaryPayload,
177996
+ customName: config10.customName,
177997
+ mapping: config10.mapping
177998
+ }
177999
+ });
178000
+ const endpoint = new _UserComposedEndpoint(
178001
+ parentTypeWithState,
178002
+ primaryEntityId,
178003
+ endpointId,
178004
+ parts,
178005
+ mappedIds
178006
+ );
178007
+ endpoint.subEndpoints = subEndpointMap;
178008
+ const labels = parts.map(
178009
+ (_, i) => i === 0 ? primaryEntityId.split(".")[0] : composedEntities[i - 1]?.entityId?.split(".")[0] ?? "?"
178010
+ ).join("+");
178011
+ logger198.info(
178012
+ `Created user composed device ${primaryEntityId}: ${parts.length} sub-endpoint(s) [${labels}]`
178013
+ );
178014
+ return endpoint;
178015
+ }
178016
+ constructor(type, entityId, id, parts, mappedEntityIds) {
178017
+ super(type, { id, parts });
178018
+ this.entityId = entityId;
178019
+ this.mappedEntityIds = mappedEntityIds;
178020
+ }
178021
+ async updateStates(states) {
178022
+ this.scheduleUpdate(this, this.entityId, states);
178023
+ for (const [entityId, sub] of this.subEndpoints) {
178024
+ this.scheduleUpdate(sub, entityId, states);
178025
+ }
178026
+ }
178027
+ scheduleUpdate(endpoint, entityId, states) {
178028
+ const state = states[entityId];
178029
+ if (!state) return;
178030
+ const key = endpoint === this ? `_parent_:${entityId}` : entityId;
178031
+ const stateJson = JSON.stringify({
178032
+ s: state.state,
178033
+ a: state.attributes
178034
+ });
178035
+ if (this.lastStates.get(key) === stateJson) return;
178036
+ this.lastStates.set(key, stateJson);
178037
+ let debouncedFn = this.debouncedUpdates.get(key);
178038
+ if (!debouncedFn) {
178039
+ debouncedFn = debounce4(
178040
+ (ep, s) => this.flushUpdate(ep, s),
178041
+ 50
178042
+ );
178043
+ this.debouncedUpdates.set(key, debouncedFn);
178044
+ }
178045
+ debouncedFn(endpoint, state);
178046
+ }
178047
+ async flushUpdate(endpoint, state) {
178048
+ try {
178049
+ await endpoint.construction.ready;
178050
+ } catch {
178051
+ return;
178052
+ }
178053
+ try {
178054
+ const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
178055
+ await endpoint.setStateOf(HomeAssistantEntityBehavior, {
178056
+ entity: { ...current, state }
178057
+ });
178058
+ } catch (error) {
178059
+ if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
178060
+ return;
178061
+ }
178062
+ const errorMessage = error instanceof Error ? error.message : String(error);
178063
+ if (errorMessage.includes(
178064
+ "Endpoint storage inaccessible because endpoint is not a node and is not owned by another endpoint"
178065
+ )) {
178066
+ return;
178067
+ }
178068
+ throw error;
178069
+ }
178070
+ }
178071
+ async delete() {
178072
+ for (const fn of this.debouncedUpdates.values()) {
178073
+ fn.clear();
178074
+ }
178075
+ await super.delete();
178076
+ }
178077
+ };
178078
+
177495
178079
  // src/matter/endpoints/legacy/legacy-endpoint.ts
177496
- var logger198 = Logger.get("LegacyEndpoint");
178080
+ var logger199 = Logger.get("LegacyEndpoint");
177497
178081
  var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177498
- static async create(registry2, entityId, mapping) {
178082
+ static async create(registry2, entityId, mapping, pluginDomainMappings) {
177499
178083
  const deviceRegistry = registry2.deviceOf(entityId);
177500
178084
  let state = registry2.initialState(entityId);
177501
178085
  const entity = registry2.entity(entityId);
@@ -177503,25 +178087,25 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177503
178087
  return;
177504
178088
  }
177505
178089
  if (registry2.isAutoBatteryMappingEnabled() && registry2.isBatteryEntityUsed(entityId)) {
177506
- logger198.debug(
178090
+ logger199.debug(
177507
178091
  `Skipping ${entityId} - already auto-assigned as battery to another device`
177508
178092
  );
177509
178093
  return;
177510
178094
  }
177511
178095
  if (registry2.isAutoHumidityMappingEnabled() && registry2.isHumidityEntityUsed(entityId)) {
177512
- logger198.debug(
178096
+ logger199.debug(
177513
178097
  `Skipping ${entityId} - already auto-assigned as humidity to a temperature sensor`
177514
178098
  );
177515
178099
  return;
177516
178100
  }
177517
178101
  if (registry2.isAutoPressureMappingEnabled() && registry2.isPressureEntityUsed(entityId)) {
177518
- logger198.debug(
178102
+ logger199.debug(
177519
178103
  `Skipping ${entityId} - already auto-assigned as pressure to a temperature sensor`
177520
178104
  );
177521
178105
  return;
177522
178106
  }
177523
178107
  if (registry2.isAutoComposedDevicesEnabled() && registry2.isComposedSubEntityUsed(entityId)) {
177524
- logger198.debug(
178108
+ logger199.debug(
177525
178109
  `Skipping ${entityId} - already consumed by a composed device`
177526
178110
  );
177527
178111
  return;
@@ -177541,7 +178125,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177541
178125
  humidityEntity: humidityEntityId
177542
178126
  };
177543
178127
  registry2.markHumidityEntityUsed(humidityEntityId);
177544
- logger198.debug(
178128
+ logger199.debug(
177545
178129
  `Auto-assigned humidity ${humidityEntityId} to ${entityId}`
177546
178130
  );
177547
178131
  }
@@ -177560,7 +178144,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177560
178144
  pressureEntity: pressureEntityId
177561
178145
  };
177562
178146
  registry2.markPressureEntityUsed(pressureEntityId);
177563
- logger198.debug(
178147
+ logger199.debug(
177564
178148
  `Auto-assigned pressure ${pressureEntityId} to ${entityId}`
177565
178149
  );
177566
178150
  }
@@ -177578,7 +178162,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177578
178162
  batteryEntity: batteryEntityId
177579
178163
  };
177580
178164
  registry2.markBatteryEntityUsed(batteryEntityId);
177581
- logger198.debug(
178165
+ logger199.debug(
177582
178166
  `Auto-assigned battery ${batteryEntityId} to ${entityId}`
177583
178167
  );
177584
178168
  }
@@ -177596,7 +178180,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177596
178180
  powerEntity: powerEntityId
177597
178181
  };
177598
178182
  registry2.markPowerEntityUsed(powerEntityId);
177599
- logger198.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
178183
+ logger199.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
177600
178184
  }
177601
178185
  }
177602
178186
  }
@@ -177613,7 +178197,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177613
178197
  energyEntity: energyEntityId
177614
178198
  };
177615
178199
  registry2.markEnergyEntityUsed(energyEntityId);
177616
- logger198.debug(
178200
+ logger199.debug(
177617
178201
  `Auto-assigned energy ${energyEntityId} to ${entityId}`
177618
178202
  );
177619
178203
  }
@@ -177629,7 +178213,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177629
178213
  entityId: effectiveMapping?.entityId ?? entityId,
177630
178214
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
177631
178215
  };
177632
- logger198.debug(
178216
+ logger199.debug(
177633
178217
  `Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity} to ${entityId}`
177634
178218
  );
177635
178219
  }
@@ -177639,7 +178223,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177639
178223
  entityId: effectiveMapping?.entityId ?? entityId,
177640
178224
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
177641
178225
  };
177642
- logger198.debug(
178226
+ logger199.debug(
177643
178227
  `Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity} to ${entityId}`
177644
178228
  );
177645
178229
  }
@@ -177649,12 +178233,27 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177649
178233
  entityId: effectiveMapping?.entityId ?? entityId,
177650
178234
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
177651
178235
  };
177652
- logger198.debug(
178236
+ logger199.debug(
177653
178237
  `Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity} to ${entityId}`
177654
178238
  );
177655
178239
  }
178240
+ const supportedFeatures = state.attributes.supported_features ?? 0;
178241
+ const cleanAreaRooms = await registry2.resolveCleanAreaRooms(
178242
+ entityId,
178243
+ supportedFeatures
178244
+ );
178245
+ if (cleanAreaRooms.length > 0) {
178246
+ effectiveMapping = {
178247
+ ...effectiveMapping,
178248
+ entityId: effectiveMapping?.entityId ?? entityId,
178249
+ cleanAreaRooms
178250
+ };
178251
+ logger199.debug(
178252
+ `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA for ${entityId}`
178253
+ );
178254
+ }
177656
178255
  const vacAttrs = state.attributes;
177657
- if (!vacAttrs.rooms && !vacAttrs.segments && !vacAttrs.room_mapping) {
178256
+ if (cleanAreaRooms.length === 0 && !vacAttrs.rooms && !vacAttrs.segments && !vacAttrs.room_mapping) {
177658
178257
  const valetudoRooms = registry2.findValetudoMapSegments(
177659
178258
  entity.device_id
177660
178259
  );
@@ -177670,7 +178269,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177670
178269
  rooms: roomsObj
177671
178270
  }
177672
178271
  };
177673
- logger198.debug(
178272
+ logger199.debug(
177674
178273
  `Auto-detected ${valetudoRooms.length} Valetudo segments for ${entityId}`
177675
178274
  );
177676
178275
  } else {
@@ -177687,7 +178286,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177687
178286
  rooms: roomsObj
177688
178287
  }
177689
178288
  };
177690
- logger198.debug(
178289
+ logger199.debug(
177691
178290
  `Auto-detected ${roborockRooms.length} Roborock rooms for ${entityId}`
177692
178291
  );
177693
178292
  }
@@ -177695,6 +178294,23 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177695
178294
  }
177696
178295
  }
177697
178296
  }
178297
+ if (registry2.isAutoComposedDevicesEnabled() && effectiveMapping?.composedEntities && effectiveMapping.composedEntities.length > 0) {
178298
+ const composedAreaName = registry2.getAreaName(entityId);
178299
+ const composed = await UserComposedEndpoint.create({
178300
+ registry: registry2,
178301
+ primaryEntityId: entityId,
178302
+ mapping: effectiveMapping,
178303
+ composedEntities: effectiveMapping.composedEntities,
178304
+ customName: effectiveMapping?.customName,
178305
+ areaName: composedAreaName
178306
+ });
178307
+ if (composed) {
178308
+ return composed;
178309
+ }
178310
+ logger199.warn(
178311
+ `User composed device creation failed for ${entityId}, falling back to standalone`
178312
+ );
178313
+ }
177698
178314
  if (registry2.isAutoComposedDevicesEnabled()) {
177699
178315
  const attrs = state.attributes;
177700
178316
  if (entityId.startsWith("sensor.") && attrs.device_class === SensorDeviceClass.temperature && (effectiveMapping?.humidityEntity || effectiveMapping?.pressureEntity)) {
@@ -177711,13 +178327,9 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177711
178327
  return composed;
177712
178328
  }
177713
178329
  const resolvedMatterType = mapping?.matterDeviceType ?? (entityId.startsWith("fan.") ? "fan" : void 0);
177714
- if (resolvedMatterType === "air_purifier" && entity.device_id) {
177715
- const temperatureEntityId = registry2.findTemperatureEntityForDevice(
177716
- entity.device_id
177717
- );
177718
- const humidityEntityId = registry2.findHumidityEntityForDevice(
177719
- entity.device_id
177720
- );
178330
+ if (resolvedMatterType === "air_purifier") {
178331
+ const temperatureEntityId = effectiveMapping?.temperatureEntity || (entity.device_id ? registry2.findTemperatureEntityForDevice(entity.device_id) : void 0);
178332
+ const humidityEntityId = effectiveMapping?.humidityEntity || (entity.device_id ? registry2.findHumidityEntityForDevice(entity.device_id) : void 0);
177721
178333
  if (temperatureEntityId || humidityEntityId) {
177722
178334
  const composedAreaName = registry2.getAreaName(entityId);
177723
178335
  const composed = await ComposedAirPurifierEndpoint.create({
@@ -177762,7 +178374,8 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177762
178374
  const areaName = registry2.getAreaName(entityId);
177763
178375
  const type = createLegacyEndpointType(payload, effectiveMapping, areaName, {
177764
178376
  vacuumOnOff: registry2.isVacuumOnOffEnabled(),
177765
- cleaningModeOptions
178377
+ cleaningModeOptions,
178378
+ pluginDomainMappings
177766
178379
  });
177767
178380
  if (!type) {
177768
178381
  return;
@@ -177773,9 +178386,10 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177773
178386
  }
177774
178387
  constructor(type, entityId, customName, mappedEntityIds) {
177775
178388
  super(type, entityId, customName, mappedEntityIds);
177776
- this.flushUpdate = debounce4(this.flushPendingUpdate.bind(this), 50);
178389
+ this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
177777
178390
  }
177778
178391
  lastState;
178392
+ pendingMappedChange = false;
177779
178393
  flushUpdate;
177780
178394
  async delete() {
177781
178395
  this.flushUpdate.clear();
@@ -177788,11 +178402,12 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177788
178402
  return;
177789
178403
  }
177790
178404
  if (mappedChanged) {
177791
- logger198.debug(
178405
+ this.pendingMappedChange = true;
178406
+ logger199.debug(
177792
178407
  `Mapped entity change detected for ${this.entityId}, forcing update`
177793
178408
  );
177794
178409
  }
177795
- logger198.debug(
178410
+ logger199.debug(
177796
178411
  `State update received for ${this.entityId}: state=${state.state}`
177797
178412
  );
177798
178413
  this.lastState = state;
@@ -177806,8 +178421,13 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177806
178421
  }
177807
178422
  try {
177808
178423
  const current = this.stateOf(HomeAssistantEntityBehavior).entity;
178424
+ let effectiveState = state;
178425
+ if (this.pendingMappedChange) {
178426
+ this.pendingMappedChange = false;
178427
+ effectiveState = { ...state, last_updated: (/* @__PURE__ */ new Date()).toISOString() };
178428
+ }
177809
178429
  await this.setStateOf(HomeAssistantEntityBehavior, {
177810
- entity: { ...current, state }
178430
+ entity: { ...current, state: effectiveState }
177811
178431
  });
177812
178432
  } catch (error) {
177813
178433
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -177826,12 +178446,12 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177826
178446
 
177827
178447
  // src/services/home-assistant/api/subscribe-entities.ts
177828
178448
  init_esm();
177829
- import crypto6 from "node:crypto";
178449
+ import crypto7 from "node:crypto";
177830
178450
  import {
177831
178451
  getCollection
177832
178452
  } from "home-assistant-js-websocket";
177833
178453
  import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js";
177834
- var logger199 = Logger.get("SubscribeEntities");
178454
+ var logger200 = Logger.get("SubscribeEntities");
177835
178455
  function processEvent(store, updates) {
177836
178456
  const state = { ...store.state };
177837
178457
  if (updates.a) {
@@ -177857,7 +178477,7 @@ function processEvent(store, updates) {
177857
178477
  for (const entityId in updates.c) {
177858
178478
  let entityState = state[entityId];
177859
178479
  if (!entityState) {
177860
- logger199.warn("Received state update for unknown entity", entityId);
178480
+ logger200.warn("Received state update for unknown entity", entityId);
177861
178481
  continue;
177862
178482
  }
177863
178483
  entityState = { ...entityState };
@@ -177910,7 +178530,7 @@ var subscribeUpdates = (conn, store, entityIds) => {
177910
178530
  });
177911
178531
  };
177912
178532
  function createEntitiesHash(entityIds) {
177913
- return crypto6.createHash("sha256").update(entityIds.join(",")).digest("hex").substring(0, 16);
178533
+ return crypto7.createHash("sha256").update(entityIds.join(",")).digest("hex").substring(0, 16);
177914
178534
  }
177915
178535
  var entitiesColl = (conn, entityIds) => {
177916
178536
  if (atLeastHaVersion(conn.haVersion, 2022, 4, 0)) {
@@ -177927,7 +178547,7 @@ var subscribeEntities = (conn, onChange, entityIds) => entitiesColl(conn, entity
177927
178547
 
177928
178548
  // src/services/bridges/entity-isolation-service.ts
177929
178549
  init_esm();
177930
- var logger200 = Logger.get("EntityIsolation");
178550
+ var logger201 = Logger.get("EntityIsolation");
177931
178551
  var EntityIsolationServiceImpl = class {
177932
178552
  isolatedEntities = /* @__PURE__ */ new Map();
177933
178553
  isolationCallbacks = /* @__PURE__ */ new Map();
@@ -177992,13 +178612,13 @@ var EntityIsolationServiceImpl = class {
177992
178612
  }
177993
178613
  const parsed = this.parseEndpointPath(msg);
177994
178614
  if (!parsed) {
177995
- logger200.warn("Could not parse entity from error:", msg);
178615
+ logger201.warn("Could not parse entity from error:", msg);
177996
178616
  return false;
177997
178617
  }
177998
178618
  const { bridgeId, entityName } = parsed;
177999
178619
  const callback = this.isolationCallbacks.get(bridgeId);
178000
178620
  if (!callback) {
178001
- logger200.warn(
178621
+ logger201.warn(
178002
178622
  `No isolation callback registered for bridge ${bridgeId}, entity: ${entityName}`
178003
178623
  );
178004
178624
  return false;
@@ -178009,14 +178629,14 @@ var EntityIsolationServiceImpl = class {
178009
178629
  }
178010
178630
  const reason = `${classification}. Entity isolated to protect bridge stability.`;
178011
178631
  this.isolatedEntities.set(key, { entityId: entityName, reason });
178012
- logger200.warn(
178632
+ logger201.warn(
178013
178633
  `Isolating entity "${entityName}" from bridge ${bridgeId} due to: ${reason}`
178014
178634
  );
178015
178635
  try {
178016
178636
  await callback(entityName);
178017
178637
  return true;
178018
178638
  } catch (e) {
178019
- logger200.error(`Failed to isolate entity ${entityName}:`, e);
178639
+ logger201.error(`Failed to isolate entity ${entityName}:`, e);
178020
178640
  return false;
178021
178641
  }
178022
178642
  }
@@ -178259,6 +178879,16 @@ var BridgeEndpointManager = class extends Service {
178259
178879
  }
178260
178880
  }
178261
178881
  }
178882
+ getPluginDomainMappings() {
178883
+ if (!this.pluginManager) return void 0;
178884
+ const mappings = this.pluginManager.getDomainMappings();
178885
+ if (mappings.size === 0) return void 0;
178886
+ const result = /* @__PURE__ */ new Map();
178887
+ for (const [domain, mapping] of mappings) {
178888
+ result.set(domain, mapping.matterDeviceType);
178889
+ }
178890
+ return result;
178891
+ }
178262
178892
  getEntityMapping(entityId) {
178263
178893
  return this.mappingStorage.getMapping(this.bridgeId, entityId);
178264
178894
  }
@@ -178315,16 +178945,20 @@ var BridgeEndpointManager = class extends Service {
178315
178945
  this.entityIds = this.registry.entityIds;
178316
178946
  if (this.registry.isAutoComposedDevicesEnabled()) {
178317
178947
  for (const eid of this.entityIds) {
178318
- if (!eid.startsWith("fan.")) continue;
178319
178948
  const m = this.getEntityMapping(eid);
178949
+ if (m?.composedEntities) {
178950
+ for (const sub of m.composedEntities) {
178951
+ if (sub.entityId) {
178952
+ this.registry.markComposedSubEntityUsed(sub.entityId);
178953
+ }
178954
+ }
178955
+ }
178956
+ if (!eid.startsWith("fan.")) continue;
178320
178957
  const matterType = m?.matterDeviceType ?? "fan";
178321
178958
  if (matterType !== "air_purifier") continue;
178322
178959
  const ent = this.registry.entity(eid);
178323
- if (!ent?.device_id) continue;
178324
- const tempId = this.registry.findTemperatureEntityForDevice(
178325
- ent.device_id
178326
- );
178327
- const humId = this.registry.findHumidityEntityForDevice(ent.device_id);
178960
+ const tempId = m?.temperatureEntity || (ent?.device_id ? this.registry.findTemperatureEntityForDevice(ent.device_id) : void 0);
178961
+ const humId = m?.humidityEntity || (ent?.device_id ? this.registry.findHumidityEntityForDevice(ent.device_id) : void 0);
178328
178962
  if (tempId) this.registry.markComposedSubEntityUsed(tempId);
178329
178963
  if (humId) this.registry.markComposedSubEntityUsed(humId);
178330
178964
  }
@@ -178395,6 +179029,12 @@ var BridgeEndpointManager = class extends Service {
178395
179029
  this.log.debug(`Skipping disabled entity: ${entityId}`);
178396
179030
  continue;
178397
179031
  }
179032
+ if (this.registry.isAutoComposedDevicesEnabled() && this.registry.isComposedSubEntityUsed(entityId)) {
179033
+ this.log.debug(
179034
+ `Skipping ${entityId} \u2014 already part of a composed device`
179035
+ );
179036
+ continue;
179037
+ }
178398
179038
  if (entityId.length > MAX_ENTITY_ID_LENGTH) {
178399
179039
  const reason = `Entity ID too long (${entityId.length} chars, max ${MAX_ENTITY_ID_LENGTH}). This would cause filesystem errors.`;
178400
179040
  this.log.warn(`Skipping entity: ${entityId}. Reason: ${reason}`);
@@ -178404,10 +179044,12 @@ var BridgeEndpointManager = class extends Service {
178404
179044
  let endpoint = existingEndpoints.find((e) => e.entityId === entityId);
178405
179045
  if (!endpoint) {
178406
179046
  try {
179047
+ const domainMappings = this.getPluginDomainMappings();
178407
179048
  endpoint = await LegacyEndpoint.create(
178408
179049
  this.registry,
178409
179050
  entityId,
178410
- mapping
179051
+ mapping,
179052
+ domainMappings
178411
179053
  );
178412
179054
  } catch (e) {
178413
179055
  const reason = this.extractErrorReason(e);
@@ -178843,6 +179485,85 @@ var BridgeRegistry = class _BridgeRegistry {
178843
179485
  return [];
178844
179486
  }
178845
179487
  }
179488
+ static cleanAreaLogger = Logger.get("CleanAreaRooms");
179489
+ /**
179490
+ * Resolve HA areas mapped to vacuum segments via HA 2026.3 CLEAN_AREA.
179491
+ * Fetches the full entity registry entry (including options.vacuum.area_mapping)
179492
+ * and resolves HA area names from the area registry.
179493
+ * Returns CleanAreaRoom[] sorted alphabetically, or empty array if
179494
+ * CLEAN_AREA is not supported or no area_mapping is configured.
179495
+ */
179496
+ async resolveCleanAreaRooms(entityId, supportedFeatures) {
179497
+ if (!this.client) return [];
179498
+ if (!(supportedFeatures & VacuumDeviceFeature.CLEAN_AREA)) return [];
179499
+ try {
179500
+ const entry = await this.client.connection.sendMessagePromise({
179501
+ type: "config/entity_registry/get",
179502
+ entity_id: entityId
179503
+ });
179504
+ const vacuumOptions = entry?.options?.vacuum;
179505
+ const areaMapping = vacuumOptions?.area_mapping;
179506
+ if (!areaMapping || Object.keys(areaMapping).length === 0) {
179507
+ _BridgeRegistry.cleanAreaLogger.debug(
179508
+ `${entityId}: CLEAN_AREA supported but no area_mapping configured`
179509
+ );
179510
+ return [];
179511
+ }
179512
+ let validSegmentIds;
179513
+ try {
179514
+ const segmentsResponse = await this.client.connection.sendMessagePromise({
179515
+ type: "vacuum/get_segments",
179516
+ entity_id: entityId
179517
+ });
179518
+ if (Array.isArray(segmentsResponse)) {
179519
+ validSegmentIds = new Set(segmentsResponse.map((s) => s.id));
179520
+ _BridgeRegistry.cleanAreaLogger.debug(
179521
+ `${entityId}: Current vacuum segments: ${[...validSegmentIds].join(", ")}`
179522
+ );
179523
+ }
179524
+ } catch {
179525
+ _BridgeRegistry.cleanAreaLogger.debug(
179526
+ `${entityId}: vacuum/get_segments not available, skipping stale entry detection`
179527
+ );
179528
+ }
179529
+ const rooms = [];
179530
+ for (const haAreaId of Object.keys(areaMapping)) {
179531
+ const segments = areaMapping[haAreaId];
179532
+ if (!segments || segments.length === 0) {
179533
+ _BridgeRegistry.cleanAreaLogger.debug(
179534
+ `${entityId}: Skipping HA area ${haAreaId} \u2014 no segments mapped`
179535
+ );
179536
+ continue;
179537
+ }
179538
+ if (validSegmentIds && !segments.some((sid) => validSegmentIds.has(sid))) {
179539
+ const areaName2 = this.registry.areas.get(haAreaId) ?? haAreaId;
179540
+ _BridgeRegistry.cleanAreaLogger.info(
179541
+ `${entityId}: Skipping stale HA area "${areaName2}" (${haAreaId}) \u2014 segments [${segments.join(", ")}] no longer exist on vacuum`
179542
+ );
179543
+ continue;
179544
+ }
179545
+ const areaName = this.registry.areas.get(haAreaId) ?? haAreaId;
179546
+ rooms.push({
179547
+ areaId: hashAreaId(haAreaId),
179548
+ haAreaId,
179549
+ name: areaName
179550
+ });
179551
+ }
179552
+ rooms.sort((a, b) => a.name.localeCompare(b.name));
179553
+ if (rooms.length > 0) {
179554
+ _BridgeRegistry.cleanAreaLogger.info(
179555
+ `${entityId}: Resolved ${rooms.length} HA areas via CLEAN_AREA mapping`
179556
+ );
179557
+ }
179558
+ return rooms;
179559
+ } catch (error) {
179560
+ const msg = error instanceof Error ? error.message : typeof error === "object" && error !== null ? JSON.stringify(error) : String(error);
179561
+ _BridgeRegistry.cleanAreaLogger.warn(
179562
+ `${entityId}: Failed to resolve CLEAN_AREA mapping: ${msg}`
179563
+ );
179564
+ return [];
179565
+ }
179566
+ }
178846
179567
  /**
178847
179568
  * Find a pressure sensor entity that belongs to the same HA device.
178848
179569
  * Returns the entity_id of the pressure sensor, or undefined if none found.
@@ -179073,16 +179794,26 @@ var BridgeRegistry = class _BridgeRegistry {
179073
179794
  return true;
179074
179795
  }
179075
179796
  };
179797
+ function hashAreaId(areaId) {
179798
+ let hash2 = 0;
179799
+ for (let i = 0; i < areaId.length; i++) {
179800
+ const char = areaId.charCodeAt(i);
179801
+ hash2 = (hash2 << 5) - hash2 + char;
179802
+ hash2 |= 0;
179803
+ }
179804
+ return Math.abs(hash2);
179805
+ }
179076
179806
 
179077
179807
  // src/services/bridges/server-mode-bridge.ts
179078
179808
  init_dist();
179079
179809
  var AUTO_FORCE_SYNC_INTERVAL_MS2 = 9e4;
179810
+ var DEAD_SESSION_TIMEOUT_MS2 = 6e4;
179080
179811
  var ServerModeBridge = class {
179081
- constructor(logger203, dataProvider, endpointManager, server) {
179812
+ constructor(logger204, dataProvider, endpointManager, server) {
179082
179813
  this.dataProvider = dataProvider;
179083
179814
  this.endpointManager = endpointManager;
179084
179815
  this.server = server;
179085
- this.log = logger203.get(`ServerModeBridge / ${dataProvider.id}`);
179816
+ this.log = logger204.get(`ServerModeBridge / ${dataProvider.id}`);
179086
179817
  }
179087
179818
  log;
179088
179819
  status = {
@@ -179093,6 +179824,8 @@ var ServerModeBridge = class {
179093
179824
  // broadcast updates via WebSocket so the frontend sees every transition.
179094
179825
  onStatusChange;
179095
179826
  autoForceSyncTimer = null;
179827
+ deadSessionTimer = null;
179828
+ staleSessionTimers = /* @__PURE__ */ new Map();
179096
179829
  warmStartTimer = null;
179097
179830
  // Tracks the last synced state JSON per entity to avoid pushing unchanged states.
179098
179831
  lastSyncedState;
@@ -179274,6 +180007,33 @@ ${e?.toString()}`);
179274
180007
  this.log.warn(
179275
180008
  `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
179276
180009
  );
180010
+ if (!this.deadSessionTimer) {
180011
+ this.deadSessionTimer = setTimeout(() => {
180012
+ this.deadSessionTimer = null;
180013
+ this.closeDeadSessions();
180014
+ }, DEAD_SESSION_TIMEOUT_MS2);
180015
+ this.log.info(
180016
+ `Scheduled dead session cleanup in ${DEAD_SESSION_TIMEOUT_MS2 / 1e3}s`
180017
+ );
180018
+ }
180019
+ } else if (totalSubs > 0 && this.deadSessionTimer) {
180020
+ clearTimeout(this.deadSessionTimer);
180021
+ this.deadSessionTimer = null;
180022
+ this.log.info(
180023
+ "Subscriptions recovered, canceled dead session cleanup"
180024
+ );
180025
+ }
180026
+ if (session.subscriptions.size === 0 && !this.staleSessionTimers.has(session.id)) {
180027
+ this.staleSessionTimers.set(
180028
+ session.id,
180029
+ setTimeout(() => {
180030
+ this.staleSessionTimers.delete(session.id);
180031
+ this.closeStaleSession(session.id);
180032
+ }, DEAD_SESSION_TIMEOUT_MS2)
180033
+ );
180034
+ } else if (session.subscriptions.size > 0 && this.staleSessionTimers.has(session.id)) {
180035
+ clearTimeout(this.staleSessionTimers.get(session.id));
180036
+ this.staleSessionTimers.delete(session.id);
179277
180037
  }
179278
180038
  };
179279
180039
  sessionManager.subscriptionsChanged.on(this.sessionDiagHandler);
@@ -179302,6 +180062,62 @@ ${e?.toString()}`);
179302
180062
  } catch {
179303
180063
  }
179304
180064
  }
180065
+ closeStaleSession(sessionId) {
180066
+ try {
180067
+ const sessionManager = this.server.env.get(SessionManager);
180068
+ for (const s of [...sessionManager.sessions]) {
180069
+ if (s.id === sessionId && !s.isClosing && s.subscriptions.size === 0) {
180070
+ this.log.warn(
180071
+ `Closing stale session ${s.id} (peer ${s.peerNodeId}, no subscriptions for ${DEAD_SESSION_TIMEOUT_MS2 / 1e3}s)`
180072
+ );
180073
+ s.initiateClose().catch(() => {
180074
+ return s.initiateForceClose();
180075
+ }).catch(() => {
180076
+ }).finally(() => this.triggerMdnsReAnnounce());
180077
+ break;
180078
+ }
180079
+ }
180080
+ } catch {
180081
+ }
180082
+ }
180083
+ closeDeadSessions() {
180084
+ try {
180085
+ const sessionManager = this.server.env.get(SessionManager);
180086
+ const sessions = [...sessionManager.sessions];
180087
+ const closes = [];
180088
+ for (const s of sessions) {
180089
+ if (!s.isClosing && s.subscriptions.size === 0) {
180090
+ this.log.warn(
180091
+ `Closing dead session ${s.id} (peer ${s.peerNodeId}, no subscriptions for ${DEAD_SESSION_TIMEOUT_MS2 / 1e3}s)`
180092
+ );
180093
+ closes.push(
180094
+ s.initiateClose().catch(() => {
180095
+ return s.initiateForceClose();
180096
+ })
180097
+ );
180098
+ }
180099
+ }
180100
+ if (closes.length > 0) {
180101
+ Promise.allSettled(closes).then(() => this.triggerMdnsReAnnounce());
180102
+ }
180103
+ } catch {
180104
+ }
180105
+ }
180106
+ /**
180107
+ * Force a fresh mDNS operational advertisement after session cleanup.
180108
+ * matter.js DeviceAdvertiser only re-announces when a subscription is
180109
+ * canceled BY THE PEER. When the server cancels after 3 delivery
180110
+ * timeouts, no re-announcement happens and the controller may not
180111
+ * realize it should reconnect (#266).
180112
+ */
180113
+ triggerMdnsReAnnounce() {
180114
+ try {
180115
+ const advertiser = this.server.env.get(DeviceAdvertiser);
180116
+ advertiser.restartAdvertisement();
180117
+ this.log.info("Triggered mDNS re-announcement after session cleanup");
180118
+ } catch {
180119
+ }
180120
+ }
179305
180121
  unwireSessionDiagnostics() {
179306
180122
  try {
179307
180123
  const sessionManager = this.server.env.get(SessionManager);
@@ -179319,6 +180135,14 @@ ${e?.toString()}`);
179319
180135
  this.sessionDiagHandler = void 0;
179320
180136
  this.sessionAddedHandler = void 0;
179321
180137
  this.sessionDeletedHandler = void 0;
180138
+ if (this.deadSessionTimer) {
180139
+ clearTimeout(this.deadSessionTimer);
180140
+ this.deadSessionTimer = null;
180141
+ }
180142
+ for (const timer of this.staleSessionTimers.values()) {
180143
+ clearTimeout(timer);
180144
+ }
180145
+ this.staleSessionTimers.clear();
179322
180146
  }
179323
180147
  stopAutoForceSync() {
179324
180148
  if (this.autoForceSyncTimer) {
@@ -179434,7 +180258,7 @@ init_service();
179434
180258
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
179435
180259
  init_esm();
179436
180260
  init_home_assistant_entity_behavior();
179437
- import debounce5 from "debounce";
180261
+ import debounce6 from "debounce";
179438
180262
 
179439
180263
  // src/matter/endpoints/legacy/vacuum/server-mode-vacuum-device.ts
179440
180264
  init_home_assistant_entity_behavior();
@@ -179448,17 +180272,24 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
179448
180272
  return void 0;
179449
180273
  }
179450
180274
  const attributes7 = homeAssistantEntity.entity.state.attributes;
180275
+ const cleanAreaRooms = homeAssistantEntity.mapping?.cleanAreaRooms;
180276
+ const customAreas = homeAssistantEntity.mapping?.customServiceAreas;
179451
180277
  let device = ServerModeVacuumEndpointType.with(
179452
- createVacuumRvcRunModeServer(attributes7)
180278
+ cleanAreaRooms && cleanAreaRooms.length > 0 ? createCleanAreaRvcRunModeServer(cleanAreaRooms) : createVacuumRvcRunModeServer(
180279
+ attributes7,
180280
+ false,
180281
+ customAreas && customAreas.length > 0 ? customAreas : void 0
180282
+ )
179453
180283
  ).set({ homeAssistantEntity });
179454
180284
  if (includeOnOff) {
179455
180285
  device = device.with(VacuumOnOffServer);
179456
180286
  }
179457
180287
  device = device.with(VacuumPowerSourceServer);
179458
- const customAreas = homeAssistantEntity.mapping?.customServiceAreas;
179459
180288
  const roomEntities = homeAssistantEntity.mapping?.roomEntities;
179460
180289
  const rooms = parseVacuumRooms(attributes7);
179461
- if (customAreas && customAreas.length > 0) {
180290
+ if (cleanAreaRooms && cleanAreaRooms.length > 0) {
180291
+ device = device.with(createCleanAreaServiceAreaServer(cleanAreaRooms));
180292
+ } else if (customAreas && customAreas.length > 0) {
179462
180293
  device = device.with(createCustomServiceAreaServer(customAreas));
179463
180294
  } else if (rooms.length > 0 || roomEntities && roomEntities.length > 0) {
179464
180295
  device = device.with(
@@ -179491,7 +180322,7 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
179491
180322
  }
179492
180323
 
179493
180324
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
179494
- var logger201 = Logger.get("ServerModeVacuumEndpoint");
180325
+ var logger202 = Logger.get("ServerModeVacuumEndpoint");
179495
180326
  var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
179496
180327
  static async create(registry2, entityId, mapping) {
179497
180328
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -179501,7 +180332,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179501
180332
  return void 0;
179502
180333
  }
179503
180334
  let effectiveMapping = mapping;
179504
- logger201.info(
180335
+ logger202.info(
179505
180336
  `${entityId}: device_id=${entity.device_id}, manualBattery=${mapping?.batteryEntity ?? "none"}`
179506
180337
  );
179507
180338
  if (entity.device_id) {
@@ -179516,11 +180347,18 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179516
180347
  batteryEntity: batteryEntityId
179517
180348
  };
179518
180349
  registry2.markBatteryEntityUsed(batteryEntityId);
179519
- logger201.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
180350
+ logger202.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
179520
180351
  } else {
179521
- logger201.warn(
179522
- `${entityId}: No battery entity found for device ${entity.device_id}`
179523
- );
180352
+ const attrs = state.attributes;
180353
+ if (attrs.battery_level != null || attrs.battery != null) {
180354
+ logger202.info(
180355
+ `${entityId}: No battery entity found, using battery attribute from vacuum state`
180356
+ );
180357
+ } else {
180358
+ logger202.warn(
180359
+ `${entityId}: No battery entity found for device ${entity.device_id}`
180360
+ );
180361
+ }
179524
180362
  }
179525
180363
  }
179526
180364
  const vacuumEntities = registry2.findVacuumSelectEntities(
@@ -179532,7 +180370,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179532
180370
  entityId: effectiveMapping?.entityId ?? entityId,
179533
180371
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
179534
180372
  };
179535
- logger201.info(
180373
+ logger202.info(
179536
180374
  `${entityId}: Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity}`
179537
180375
  );
179538
180376
  }
@@ -179542,7 +180380,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179542
180380
  entityId: effectiveMapping?.entityId ?? entityId,
179543
180381
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
179544
180382
  };
179545
- logger201.info(
180383
+ logger202.info(
179546
180384
  `${entityId}: Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity}`
179547
180385
  );
179548
180386
  }
@@ -179552,12 +180390,27 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179552
180390
  entityId: effectiveMapping?.entityId ?? entityId,
179553
180391
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
179554
180392
  };
179555
- logger201.info(
180393
+ logger202.info(
179556
180394
  `${entityId}: Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity}`
179557
180395
  );
179558
180396
  }
180397
+ const supportedFeatures = state.attributes.supported_features ?? 0;
180398
+ const cleanAreaRooms = await registry2.resolveCleanAreaRooms(
180399
+ entityId,
180400
+ supportedFeatures
180401
+ );
180402
+ if (cleanAreaRooms.length > 0) {
180403
+ effectiveMapping = {
180404
+ ...effectiveMapping,
180405
+ entityId: effectiveMapping?.entityId ?? entityId,
180406
+ cleanAreaRooms
180407
+ };
180408
+ logger202.info(
180409
+ `${entityId}: Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA`
180410
+ );
180411
+ }
179559
180412
  const vacAttrs = state.attributes;
179560
- if (!vacAttrs.rooms && !vacAttrs.segments && !vacAttrs.room_mapping) {
180413
+ if (cleanAreaRooms.length === 0 && !vacAttrs.rooms && !vacAttrs.segments && !vacAttrs.room_mapping) {
179561
180414
  const valetudoRooms = registry2.findValetudoMapSegments(
179562
180415
  entity.device_id
179563
180416
  );
@@ -179573,7 +180426,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179573
180426
  rooms: roomsObj
179574
180427
  }
179575
180428
  };
179576
- logger201.info(
180429
+ logger202.info(
179577
180430
  `${entityId}: Auto-detected ${valetudoRooms.length} Valetudo segments`
179578
180431
  );
179579
180432
  } else {
@@ -179590,14 +180443,14 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179590
180443
  rooms: roomsObj
179591
180444
  }
179592
180445
  };
179593
- logger201.info(
180446
+ logger202.info(
179594
180447
  `${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
179595
180448
  );
179596
180449
  }
179597
180450
  }
179598
180451
  }
179599
180452
  } else {
179600
- logger201.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
180453
+ logger202.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
179601
180454
  }
179602
180455
  const payload = {
179603
180456
  entity_id: entityId,
@@ -179643,10 +180496,11 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179643
180496
  );
179644
180497
  }
179645
180498
  lastState;
180499
+ pendingMappedChange = false;
179646
180500
  flushUpdate;
179647
180501
  constructor(type, entityId, customName, mappedEntityIds) {
179648
180502
  super(type, entityId, customName, mappedEntityIds);
179649
- this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
180503
+ this.flushUpdate = debounce6(this.flushPendingUpdate.bind(this), 50);
179650
180504
  }
179651
180505
  async delete() {
179652
180506
  this.flushUpdate.clear();
@@ -179659,11 +180513,12 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179659
180513
  return;
179660
180514
  }
179661
180515
  if (mappedChanged) {
179662
- logger201.debug(
180516
+ this.pendingMappedChange = true;
180517
+ logger202.debug(
179663
180518
  `Mapped entity change detected for ${this.entityId}, forcing update`
179664
180519
  );
179665
180520
  }
179666
- logger201.debug(
180521
+ logger202.debug(
179667
180522
  `State update received for ${this.entityId}: state=${state.state}`
179668
180523
  );
179669
180524
  this.lastState = state;
@@ -179677,8 +180532,13 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179677
180532
  }
179678
180533
  try {
179679
180534
  const current = this.stateOf(HomeAssistantEntityBehavior).entity;
180535
+ let effectiveState = state;
180536
+ if (this.pendingMappedChange) {
180537
+ this.pendingMappedChange = false;
180538
+ effectiveState = { ...state, last_updated: (/* @__PURE__ */ new Date()).toISOString() };
180539
+ }
179680
180540
  await this.setStateOf(HomeAssistantEntityBehavior, {
179681
- entity: { ...current, state }
180541
+ entity: { ...current, state: effectiveState }
179682
180542
  });
179683
180543
  } catch (error) {
179684
180544
  if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
@@ -180072,10 +180932,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
180072
180932
  // src/core/ioc/app-environment.ts
180073
180933
  var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180074
180934
  constructor(rootEnv, options) {
180075
- const logger203 = rootEnv.get(LoggerService);
180935
+ const logger204 = rootEnv.get(LoggerService);
180076
180936
  super({
180077
180937
  id: "App",
180078
- log: logger203.get("AppContainer"),
180938
+ log: logger204.get("AppContainer"),
180079
180939
  parent: rootEnv
180080
180940
  });
180081
180941
  this.options = options;
@@ -180088,8 +180948,8 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180088
180948
  }
180089
180949
  construction;
180090
180950
  async init() {
180091
- const logger203 = this.get(LoggerService);
180092
- this.set(LoggerService, logger203);
180951
+ const logger204 = this.get(LoggerService);
180952
+ this.set(LoggerService, logger204);
180093
180953
  this.set(AppStorage, new AppStorage(await this.load(StorageService)));
180094
180954
  this.set(BridgeStorage, new BridgeStorage(await this.load(AppStorage)));
180095
180955
  this.set(
@@ -180106,7 +180966,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180106
180966
  );
180107
180967
  this.set(
180108
180968
  HomeAssistantClient,
180109
- new HomeAssistantClient(logger203, this.options.homeAssistant)
180969
+ new HomeAssistantClient(logger204, this.options.homeAssistant)
180110
180970
  );
180111
180971
  this.set(
180112
180972
  HomeAssistantConfig,
@@ -180114,7 +180974,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180114
180974
  );
180115
180975
  this.set(
180116
180976
  HomeAssistantActions,
180117
- new HomeAssistantActions(logger203, await this.load(HomeAssistantClient))
180977
+ new HomeAssistantActions(logger204, await this.load(HomeAssistantClient))
180118
180978
  );
180119
180979
  this.set(
180120
180980
  HomeAssistantRegistry,
@@ -180150,7 +181010,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180150
181010
  this.set(
180151
181011
  WebApi,
180152
181012
  new WebApi(
180153
- logger203,
181013
+ logger204,
180154
181014
  await this.load(BridgeService),
180155
181015
  await this.load(HomeAssistantClient),
180156
181016
  await this.load(HomeAssistantRegistry),
@@ -180176,7 +181036,7 @@ init_nodejs();
180176
181036
  init_level_control();
180177
181037
 
180178
181038
  // src/matter/patches/patch-level-control-tlv.ts
180179
- var logger202 = Logger.get("PatchLevelControlTlv");
181039
+ var logger203 = Logger.get("PatchLevelControlTlv");
180180
181040
  function patchLevelControlTlv() {
180181
181041
  let patched = 0;
180182
181042
  const moveToLevelFields = LevelControl3.TlvMoveToLevelRequest.fieldDefinitions;
@@ -180190,11 +181050,11 @@ function patchLevelControlTlv() {
180190
181050
  patched++;
180191
181051
  }
180192
181052
  if (patched > 0) {
180193
- logger202.info(
181053
+ logger203.info(
180194
181054
  `Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
180195
181055
  );
180196
181056
  } else {
180197
- logger202.warn(
181057
+ logger203.warn(
180198
181058
  "Failed to patch LevelControl TLV schemas \u2014 field definitions not found. Google Home brightness adjustment may not work."
180199
181059
  );
180200
181060
  }
@@ -180232,8 +181092,7 @@ process.on("unhandledRejection", (reason) => {
180232
181092
  if (shouldSuppressError(reason)) {
180233
181093
  return;
180234
181094
  }
180235
- console.error("Unhandled rejection:", reason);
180236
- process.exit(1);
181095
+ console.error("Unhandled rejection (process continuing):", reason);
180237
181096
  });
180238
181097
  function registerFinalErrorHandlers() {
180239
181098
  process.removeAllListeners("uncaughtException");
@@ -180259,8 +181118,7 @@ function registerFinalErrorHandlers() {
180259
181118
  console.warn("Suppressed Matter.js internal error:", reason);
180260
181119
  return;
180261
181120
  }
180262
- console.error("Unhandled rejection:", reason);
180263
- process.exit(1);
181121
+ console.error("Unhandled rejection (process continuing):", reason);
180264
181122
  });
180265
181123
  }
180266
181124
  async function startHandler(startOptions, webUiDist) {