@riddix/hamh 2.0.35 → 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) => {
@@ -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) {
@@ -147221,10 +147221,10 @@ var init_home_assistant_actions = __esm({
147221
147221
  circuitBreakerResetMs: 3e4
147222
147222
  };
147223
147223
  HomeAssistantActions = class extends Service {
147224
- constructor(logger203, client, config10) {
147224
+ constructor(logger204, client, config10) {
147225
147225
  super("HomeAssistantActions");
147226
147226
  this.client = client;
147227
- this.log = logger203.get(this);
147227
+ this.log = logger204.get(this);
147228
147228
  this.config = { ...defaultConfig, ...config10 };
147229
147229
  this.circuitBreaker = new CircuitBreaker(
147230
147230
  this.config.circuitBreakerThreshold,
@@ -147567,7 +147567,7 @@ var DiagnosticService = class {
147567
147567
  }
147568
147568
  }
147569
147569
  }
147570
- const entities = this.collectEntities(bridge.aggregator);
147570
+ const entities = bridge.aggregator ? this.collectEntities(bridge.aggregator) : [];
147571
147571
  return {
147572
147572
  bridgeId: data.id,
147573
147573
  bridgeName: data.name,
@@ -147596,10 +147596,10 @@ var DiagnosticService = class {
147596
147596
  };
147597
147597
 
147598
147598
  // src/api/access-log.ts
147599
- function accessLogger(logger203) {
147599
+ function accessLogger(logger204) {
147600
147600
  return (req, res, next) => {
147601
147601
  res.on("finish", () => {
147602
- logger203.debug(
147602
+ logger204.debug(
147603
147603
  `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}`
147604
147604
  );
147605
147605
  });
@@ -147766,6 +147766,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147766
147766
  disabled: config10.disabled,
147767
147767
  filterLifeEntity: config10.filterLifeEntity,
147768
147768
  cleaningModeEntity: config10.cleaningModeEntity,
147769
+ temperatureEntity: config10.temperatureEntity,
147769
147770
  humidityEntity: config10.humidityEntity,
147770
147771
  pressureEntity: config10.pressureEntity,
147771
147772
  batteryEntity: config10.batteryEntity,
@@ -147775,7 +147776,8 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147775
147776
  energyEntity: config10.energyEntity,
147776
147777
  suctionLevelEntity: config10.suctionLevelEntity,
147777
147778
  mopIntensityEntity: config10.mopIntensityEntity,
147778
- valetudoIdentifier: config10.valetudoIdentifier
147779
+ valetudoIdentifier: config10.valetudoIdentifier,
147780
+ coverSwapOpenClose: config10.coverSwapOpenClose
147779
147781
  });
147780
147782
  mappingsRestored++;
147781
147783
  }
@@ -148514,6 +148516,15 @@ function logsApi(_logger) {
148514
148516
  }
148515
148517
 
148516
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
+ }
148517
148528
  function detectEnvironment() {
148518
148529
  if (process.env.SUPERVISOR_TOKEN || process.env.HASSIO_TOKEN) {
148519
148530
  return "Home Assistant Add-on";
@@ -148588,7 +148599,14 @@ function diagnosticApi(bridgeService, haClient, haRegistry, version, startTime)
148588
148599
  failedEntities: failedEntities.map((fe) => ({
148589
148600
  entityId: anonymize ? anonymizeEntityId(fe.entityId) : fe.entityId,
148590
148601
  reason: fe.reason
148591
- }))
148602
+ })),
148603
+ endpointTree: (() => {
148604
+ try {
148605
+ return endpointTreeNode(b.server);
148606
+ } catch {
148607
+ return void 0;
148608
+ }
148609
+ })()
148592
148610
  };
148593
148611
  });
148594
148612
  const recentLogs = logBuffer.entries.slice(-logLimit).map((entry) => ({
@@ -148660,6 +148678,7 @@ function entityMappingApi(mappingStorage) {
148660
148678
  disabled: body.disabled,
148661
148679
  filterLifeEntity: body.filterLifeEntity,
148662
148680
  cleaningModeEntity: body.cleaningModeEntity,
148681
+ temperatureEntity: body.temperatureEntity,
148663
148682
  humidityEntity: body.humidityEntity,
148664
148683
  pressureEntity: body.pressureEntity,
148665
148684
  batteryEntity: body.batteryEntity,
@@ -148671,7 +148690,9 @@ function entityMappingApi(mappingStorage) {
148671
148690
  mopIntensityEntity: body.mopIntensityEntity,
148672
148691
  customServiceAreas: body.customServiceAreas,
148673
148692
  customFanSpeedTags: body.customFanSpeedTags,
148674
- valetudoIdentifier: body.valetudoIdentifier
148693
+ valetudoIdentifier: body.valetudoIdentifier,
148694
+ coverSwapOpenClose: body.coverSwapOpenClose,
148695
+ composedEntities: body.composedEntities
148675
148696
  };
148676
148697
  const config10 = await mappingStorage.setMapping(request);
148677
148698
  res.status(200).json(config10);
@@ -149121,6 +149142,7 @@ function configToProfileEntry(config10) {
149121
149142
  disabled: config10.disabled,
149122
149143
  filterLifeEntity: config10.filterLifeEntity,
149123
149144
  cleaningModeEntity: config10.cleaningModeEntity,
149145
+ temperatureEntity: config10.temperatureEntity,
149124
149146
  humidityEntity: config10.humidityEntity,
149125
149147
  pressureEntity: config10.pressureEntity,
149126
149148
  batteryEntity: config10.batteryEntity,
@@ -149132,7 +149154,8 @@ function configToProfileEntry(config10) {
149132
149154
  mopIntensityEntity: config10.mopIntensityEntity,
149133
149155
  customServiceAreas: config10.customServiceAreas,
149134
149156
  customFanSpeedTags: config10.customFanSpeedTags,
149135
- valetudoIdentifier: config10.valetudoIdentifier
149157
+ valetudoIdentifier: config10.valetudoIdentifier,
149158
+ coverSwapOpenClose: config10.coverSwapOpenClose
149136
149159
  };
149137
149160
  }
149138
149161
  function mappingProfileApi(mappingStorage) {
@@ -149240,6 +149263,7 @@ function mappingProfileApi(mappingStorage) {
149240
149263
  disabled: entry.disabled,
149241
149264
  filterLifeEntity: entry.filterLifeEntity,
149242
149265
  cleaningModeEntity: entry.cleaningModeEntity,
149266
+ temperatureEntity: entry.temperatureEntity,
149243
149267
  humidityEntity: entry.humidityEntity,
149244
149268
  pressureEntity: entry.pressureEntity,
149245
149269
  batteryEntity: entry.batteryEntity,
@@ -149251,7 +149275,8 @@ function mappingProfileApi(mappingStorage) {
149251
149275
  mopIntensityEntity: entry.mopIntensityEntity,
149252
149276
  customServiceAreas: entry.customServiceAreas,
149253
149277
  customFanSpeedTags: entry.customFanSpeedTags,
149254
- valetudoIdentifier: entry.valetudoIdentifier
149278
+ valetudoIdentifier: entry.valetudoIdentifier,
149279
+ coverSwapOpenClose: entry.coverSwapOpenClose
149255
149280
  });
149256
149281
  applied++;
149257
149282
  } catch (e) {
@@ -151087,7 +151112,7 @@ var WebSocketApi = class {
151087
151112
 
151088
151113
  // src/api/web-api.ts
151089
151114
  var WebApi = class extends Service {
151090
- constructor(logger203, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151115
+ constructor(logger204, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151091
151116
  super("WebApi");
151092
151117
  this.bridgeService = bridgeService;
151093
151118
  this.haClient = haClient;
@@ -151098,8 +151123,8 @@ var WebApi = class extends Service {
151098
151123
  this.settingsStorage = settingsStorage;
151099
151124
  this.backupService = backupService;
151100
151125
  this.props = props;
151101
- this.logger = logger203;
151102
- this.log = logger203.get(this);
151126
+ this.logger = logger204;
151127
+ this.log = logger204.get(this);
151103
151128
  this.accessLogger = accessLogger(this.log.createChild("Access Log"));
151104
151129
  this.startTime = Date.now();
151105
151130
  this.wsApi = new WebSocketApi(
@@ -151537,12 +151562,12 @@ var CustomStorage = class extends StorageBackendDisk {
151537
151562
 
151538
151563
  // src/core/app/storage.ts
151539
151564
  function storage(environment, options) {
151540
- const logger203 = environment.get(LoggerService).get("CustomStorage");
151565
+ const logger204 = environment.get(LoggerService).get("CustomStorage");
151541
151566
  const location2 = resolveStorageLocation(options.location);
151542
151567
  fs8.mkdirSync(location2, { recursive: true });
151543
151568
  const storageService = environment.get(StorageService);
151544
151569
  storageService.location = location2;
151545
- storageService.factory = (ns) => new CustomStorage(logger203, path8.resolve(location2, ns));
151570
+ storageService.factory = (ns) => new CustomStorage(logger204, path8.resolve(location2, ns));
151546
151571
  }
151547
151572
  function resolveStorageLocation(storageLocation) {
151548
151573
  const homedir = os3.homedir();
@@ -151568,17 +151593,17 @@ import { createRequire } from "node:module";
151568
151593
  import os4 from "node:os";
151569
151594
  import path9 from "node:path";
151570
151595
  function resolveAppVersion() {
151571
- if (process.env.APP_VERSION) {
151572
- return process.env.APP_VERSION;
151573
- }
151574
151596
  try {
151575
151597
  const require2 = createRequire(import.meta.url);
151576
151598
  const pkg = require2("home-assistant-matter-hub/package.json");
151577
- if (pkg.version) {
151599
+ if (pkg.version && pkg.version !== "0.0.0") {
151578
151600
  return pkg.version;
151579
151601
  }
151580
151602
  } catch {
151581
151603
  }
151604
+ if (process.env.APP_VERSION) {
151605
+ return process.env.APP_VERSION;
151606
+ }
151582
151607
  return "0.0.0-dev";
151583
151608
  }
151584
151609
  var Options = class {
@@ -152092,10 +152117,10 @@ import {
152092
152117
  getConfig
152093
152118
  } from "home-assistant-js-websocket";
152094
152119
  var HomeAssistantClient = class extends Service {
152095
- constructor(logger203, options) {
152120
+ constructor(logger204, options) {
152096
152121
  super("HomeAssistantClient");
152097
152122
  this.options = options;
152098
- this.log = logger203.get(this);
152123
+ this.log = logger204.get(this);
152099
152124
  }
152100
152125
  static Options = /* @__PURE__ */ Symbol.for("HomeAssistantClientProps");
152101
152126
  _connection;
@@ -152673,6 +152698,7 @@ var EntityMappingStorage = class extends Service {
152673
152698
  disabled: request.disabled,
152674
152699
  filterLifeEntity: request.filterLifeEntity?.trim() || void 0,
152675
152700
  cleaningModeEntity: request.cleaningModeEntity?.trim() || void 0,
152701
+ temperatureEntity: request.temperatureEntity?.trim() || void 0,
152676
152702
  humidityEntity: request.humidityEntity?.trim() || void 0,
152677
152703
  batteryEntity: request.batteryEntity?.trim() || void 0,
152678
152704
  roomEntities: roomEntities.length > 0 ? roomEntities : void 0,
@@ -152686,9 +152712,11 @@ var EntityMappingStorage = class extends Service {
152686
152712
  (a) => a.name?.trim() && a.service?.trim()
152687
152713
  ) ?? void 0,
152688
152714
  customFanSpeedTags: request.customFanSpeedTags && Object.keys(request.customFanSpeedTags).length > 0 ? request.customFanSpeedTags : void 0,
152689
- valetudoIdentifier: request.valetudoIdentifier?.trim() || 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
152690
152718
  };
152691
- 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) && !config10.valetudoIdentifier) {
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)) {
152692
152720
  bridgeMap.delete(request.entityId);
152693
152721
  } else {
152694
152722
  bridgeMap.set(request.entityId, config10);
@@ -166255,6 +166283,7 @@ function validatePluginDevice(device) {
166255
166283
  }
166256
166284
  var PluginManager = class {
166257
166285
  instances = /* @__PURE__ */ new Map();
166286
+ domainMappings = /* @__PURE__ */ new Map();
166258
166287
  storageDir;
166259
166288
  bridgeId;
166260
166289
  runner = new SafePluginRunner();
@@ -166386,6 +166415,21 @@ var PluginManager = class {
166386
166415
  clusterId3,
166387
166416
  attributes7
166388
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
+ );
166389
166433
  }
166390
166434
  };
166391
166435
  this.instances.set(plugin.name, {
@@ -166511,6 +166555,9 @@ var PluginManager = class {
166511
166555
  if (!instance) return void 0;
166512
166556
  return instance.plugin.getConfigSchema?.();
166513
166557
  }
166558
+ getDomainMappings() {
166559
+ return new Map(this.domainMappings);
166560
+ }
166514
166561
  async updateConfig(pluginName, config10) {
166515
166562
  const instance = this.instances.get(pluginName);
166516
166563
  if (!instance) return false;
@@ -166856,11 +166903,12 @@ function ensureCommissioningConfig(server) {
166856
166903
 
166857
166904
  // src/services/bridges/bridge.ts
166858
166905
  var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
166906
+ var DEAD_SESSION_TIMEOUT_MS = 6e4;
166859
166907
  var Bridge = class {
166860
- constructor(env, logger203, dataProvider, endpointManager) {
166908
+ constructor(env, logger204, dataProvider, endpointManager) {
166861
166909
  this.dataProvider = dataProvider;
166862
166910
  this.endpointManager = endpointManager;
166863
- this.log = logger203.get(`Bridge / ${dataProvider.id}`);
166911
+ this.log = logger204.get(`Bridge / ${dataProvider.id}`);
166864
166912
  this.server = new BridgeServerNode(
166865
166913
  env,
166866
166914
  this.dataProvider,
@@ -166878,6 +166926,8 @@ var Bridge = class {
166878
166926
  // (e.g. Stopped → Starting → Running).
166879
166927
  onStatusChange;
166880
166928
  autoForceSyncTimer = null;
166929
+ deadSessionTimer = null;
166930
+ staleSessionTimers = /* @__PURE__ */ new Map();
166881
166931
  // Tracks the last synced state JSON per entity to avoid pushing unchanged states.
166882
166932
  // Key: entity_id, Value: JSON.stringify of entity.state
166883
166933
  lastSyncedStates = /* @__PURE__ */ new Map();
@@ -167054,6 +167104,33 @@ ${e?.toString()}`);
167054
167104
  this.log.warn(
167055
167105
  `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
167056
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);
167057
167134
  }
167058
167135
  };
167059
167136
  sessionManager.subscriptionsChanged.on(this.sessionDiagHandler);
@@ -167082,6 +167159,62 @@ ${e?.toString()}`);
167082
167159
  } catch {
167083
167160
  }
167084
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
+ }
167085
167218
  unwireSessionDiagnostics() {
167086
167219
  try {
167087
167220
  const sessionManager = this.server.env.get(SessionManager);
@@ -167099,6 +167232,14 @@ ${e?.toString()}`);
167099
167232
  this.sessionDiagHandler = void 0;
167100
167233
  this.sessionAddedHandler = void 0;
167101
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();
167102
167243
  }
167103
167244
  stopAutoForceSync() {
167104
167245
  if (this.autoForceSyncTimer) {
@@ -167232,7 +167373,7 @@ var AggregatorEndpoint2 = class extends Endpoint {
167232
167373
  init_dist();
167233
167374
  init_esm();
167234
167375
  init_home_assistant_entity_behavior();
167235
- import debounce4 from "debounce";
167376
+ import debounce5 from "debounce";
167236
167377
 
167237
167378
  // src/matter/endpoints/entity-endpoint.ts
167238
167379
  init_esm7();
@@ -167266,6 +167407,7 @@ function getMappedEntityIds(mapping) {
167266
167407
  if (!mapping) return [];
167267
167408
  const ids = [];
167268
167409
  if (mapping.batteryEntity) ids.push(mapping.batteryEntity);
167410
+ if (mapping.temperatureEntity) ids.push(mapping.temperatureEntity);
167269
167411
  if (mapping.humidityEntity) ids.push(mapping.humidityEntity);
167270
167412
  if (mapping.pressureEntity) ids.push(mapping.pressureEntity);
167271
167413
  if (mapping.cleaningModeEntity) ids.push(mapping.cleaningModeEntity);
@@ -167274,6 +167416,11 @@ function getMappedEntityIds(mapping) {
167274
167416
  if (mapping.filterLifeEntity) ids.push(mapping.filterLifeEntity);
167275
167417
  if (mapping.powerEntity) ids.push(mapping.powerEntity);
167276
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
+ }
167277
167424
  return ids;
167278
167425
  }
167279
167426
 
@@ -167610,6 +167757,26 @@ function PowerSourceServer2(config10) {
167610
167757
  order: PowerSource3.Cluster.id
167611
167758
  });
167612
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);
167613
167780
 
167614
167781
  // src/matter/behaviors/temperature-measurement-server.ts
167615
167782
  init_home_assistant_entity_behavior();
@@ -168028,12 +168195,13 @@ var OnOffServerBase = class extends OnOffServer {
168028
168195
  return;
168029
168196
  }
168030
168197
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168031
- const action = turnOn?.(void 0, this.agent) ?? {
168032
- action: "homeassistant.turn_on"
168033
- };
168198
+ const action = turnOn ? turnOn(void 0, this.agent) : { action: "homeassistant.turn_on" };
168199
+ applyPatchState(this.state, { onOff: true });
168200
+ if (!action) {
168201
+ return;
168202
+ }
168034
168203
  logger167.info(`[${homeAssistant.entityId}] Turning ON -> ${action.action}`);
168035
168204
  notifyLightTurnedOn(homeAssistant.entityId);
168036
- applyPatchState(this.state, { onOff: true });
168037
168205
  optimisticOnOffState.set(homeAssistant.entityId, {
168038
168206
  expectedOnOff: true,
168039
168207
  timestamp: Date.now()
@@ -168050,11 +168218,12 @@ var OnOffServerBase = class extends OnOffServer {
168050
168218
  return;
168051
168219
  }
168052
168220
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168053
- const action = turnOff?.(void 0, this.agent) ?? {
168054
- action: "homeassistant.turn_off"
168055
- };
168056
- logger167.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
168221
+ const action = turnOff ? turnOff(void 0, this.agent) : { action: "homeassistant.turn_off" };
168057
168222
  applyPatchState(this.state, { onOff: false });
168223
+ if (!action) {
168224
+ return;
168225
+ }
168226
+ logger167.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
168058
168227
  optimisticOnOffState.set(homeAssistant.entityId, {
168059
168228
  expectedOnOff: false,
168060
168229
  timestamp: Date.now()
@@ -168532,35 +168701,25 @@ var FanOnOffServer = OnOffServer2({
168532
168701
 
168533
168702
  // src/matter/endpoints/composed/composed-air-purifier-endpoint.ts
168534
168703
  var logger169 = Logger.get("ComposedAirPurifierEndpoint");
168535
- function createTemperatureConfig(temperatureEntityId) {
168536
- return {
168537
- getValue(_entity, agent) {
168538
- const stateProvider = agent.env.get(EntityStateProvider);
168539
- const tempState = stateProvider.getState(temperatureEntityId);
168540
- if (!tempState) return void 0;
168541
- const temperature3 = Number.parseFloat(tempState.state);
168542
- if (Number.isNaN(temperature3)) return void 0;
168543
- const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
168544
- const attrs = tempState.attributes;
168545
- return Temperature.withUnit(
168546
- temperature3,
168547
- attrs.unit_of_measurement ?? fallbackUnit
168548
- );
168549
- }
168550
- };
168551
- }
168552
- function createHumidityConfig(humidityEntityId) {
168553
- return {
168554
- getValue(_entity, agent) {
168555
- const stateProvider = agent.env.get(EntityStateProvider);
168556
- const humState = stateProvider.getState(humidityEntityId);
168557
- if (!humState) return null;
168558
- const humidity = Number.parseFloat(humState.state);
168559
- if (Number.isNaN(humidity)) return null;
168560
- return humidity;
168561
- }
168562
- };
168563
- }
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
+ };
168564
168723
  var batteryConfig = {
168565
168724
  getBatteryPercent: (_entity, agent) => {
168566
168725
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
@@ -168573,6 +168732,16 @@ var batteryConfig = {
168573
168732
  return null;
168574
168733
  }
168575
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
+ );
168576
168745
  function createEndpointId2(entityId, customName) {
168577
168746
  const baseName = customName || entityId;
168578
168747
  return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
@@ -168592,9 +168761,9 @@ function buildEntityPayload(registry2, entityId) {
168592
168761
  var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends Endpoint {
168593
168762
  entityId;
168594
168763
  mappedEntityIds;
168595
- trackedEntityIds;
168764
+ subEndpoints = /* @__PURE__ */ new Map();
168596
168765
  lastStates = /* @__PURE__ */ new Map();
168597
- debouncedFlush;
168766
+ debouncedUpdates = /* @__PURE__ */ new Map();
168598
168767
  static async create(config10) {
168599
168768
  const { registry: registry2, primaryEntityId } = config10;
168600
168769
  const primaryPayload = buildEntityPayload(registry2, primaryEntityId);
@@ -168622,8 +168791,7 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168622
168791
  if (hasWindModes) {
168623
168792
  features2.add("Wind");
168624
168793
  }
168625
- let parentType = AirPurifierDevice.with(
168626
- BasicInformationServer2,
168794
+ let airPurifierSubType = AirPurifierDevice.with(
168627
168795
  IdentifyServer2,
168628
168796
  HomeAssistantEntityBehavior,
168629
168797
  FanOnOffServer,
@@ -168631,27 +168799,23 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168631
168799
  );
168632
168800
  const hasFilterLife = airPurifierAttributes.filter_life != null || airPurifierAttributes.filter_life_remaining != null || airPurifierAttributes.filter_life_level != null || !!config10.mapping?.filterLifeEntity;
168633
168801
  if (hasFilterLife) {
168634
- parentType = parentType.with(AirPurifierHepaFilterMonitoringServer);
168635
- }
168636
- if (config10.temperatureEntityId) {
168637
- parentType = parentType.with(
168638
- TemperatureMeasurementServer2(
168639
- createTemperatureConfig(config10.temperatureEntityId)
168640
- )
168641
- );
168642
- }
168643
- if (config10.humidityEntityId) {
168644
- parentType = parentType.with(
168645
- HumidityMeasurementServer(
168646
- createHumidityConfig(config10.humidityEntityId)
168647
- )
168802
+ airPurifierSubType = airPurifierSubType.with(
168803
+ AirPurifierHepaFilterMonitoringServer
168648
168804
  );
168649
168805
  }
168650
- const mapping = {
168806
+ const airPurifierMapping = {
168651
168807
  entityId: primaryEntityId,
168652
- ...config10.batteryEntityId ? { batteryEntity: config10.batteryEntityId } : {},
168653
168808
  ...config10.mapping?.filterLifeEntity ? { filterLifeEntity: config10.mapping.filterLifeEntity } : {}
168654
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
+ };
168655
168819
  if (config10.batteryEntityId) {
168656
168820
  parentType = parentType.with(PowerSourceServer2(batteryConfig));
168657
168821
  }
@@ -168664,75 +168828,125 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168664
168828
  );
168665
168829
  }
168666
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
+ }
168667
168871
  const parentTypeWithState = parentType.set({
168668
168872
  homeAssistantEntity: {
168669
168873
  entity: primaryPayload,
168670
168874
  customName: config10.customName,
168671
- mapping
168875
+ mapping: parentMapping
168672
168876
  }
168673
168877
  });
168674
- const trackedEntityIds = [primaryEntityId];
168675
- if (config10.temperatureEntityId)
168676
- trackedEntityIds.push(config10.temperatureEntityId);
168677
- if (config10.humidityEntityId) trackedEntityIds.push(config10.humidityEntityId);
168678
- 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);
168679
168883
  const endpoint = new _ComposedAirPurifierEndpoint(
168680
168884
  parentTypeWithState,
168681
168885
  primaryEntityId,
168682
168886
  endpointId,
168683
- trackedEntityIds,
168887
+ parts,
168684
168888
  mappedIds
168685
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
+ }
168686
168897
  const clusterLabels = [
168687
168898
  "AirPurifier",
168688
168899
  config10.temperatureEntityId ? "+Temp" : "",
168689
168900
  config10.humidityEntityId ? "+Hum" : "",
168690
- config10.batteryEntityId ? "+Bat" : ""
168901
+ config10.batteryEntityId ? "+Bat" : "",
168902
+ hasFilterLife ? "+HEPA" : ""
168691
168903
  ].filter(Boolean).join("");
168692
- logger169.info(`Created air purifier ${primaryEntityId}: ${clusterLabels}`);
168904
+ logger169.info(
168905
+ `Created composed air purifier ${primaryEntityId}: ${clusterLabels}`
168906
+ );
168693
168907
  return endpoint;
168694
168908
  }
168695
- constructor(type, entityId, id, trackedEntityIds, mappedEntityIds) {
168696
- super(type, { id });
168909
+ constructor(type, entityId, id, parts, mappedEntityIds) {
168910
+ super(type, { id, parts });
168697
168911
  this.entityId = entityId;
168698
- this.trackedEntityIds = trackedEntityIds;
168699
168912
  this.mappedEntityIds = mappedEntityIds;
168700
168913
  }
168701
168914
  async updateStates(states) {
168702
- let anyChanged = false;
168703
- for (const entityId of this.trackedEntityIds) {
168704
- const state = states[entityId];
168705
- if (!state) continue;
168706
- const stateJson = JSON.stringify({
168707
- s: state.state,
168708
- a: state.attributes
168709
- });
168710
- if (this.lastStates.get(entityId) !== stateJson) {
168711
- this.lastStates.set(entityId, stateJson);
168712
- anyChanged = true;
168713
- }
168915
+ this.scheduleUpdate(this, this.entityId, states);
168916
+ for (const [entityId, sub] of this.subEndpoints) {
168917
+ this.scheduleUpdate(sub, entityId, states);
168714
168918
  }
168715
- if (!anyChanged) return;
168716
- const primaryState = states[this.entityId];
168717
- if (!primaryState) return;
168718
- if (!this.debouncedFlush) {
168719
- this.debouncedFlush = debounce2(
168720
- (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),
168721
168934
  50
168722
168935
  );
168936
+ this.debouncedUpdates.set(key, debouncedFn);
168723
168937
  }
168724
- this.debouncedFlush(primaryState);
168938
+ debouncedFn(endpoint, state);
168725
168939
  }
168726
- async flushUpdate(state) {
168940
+ async flushUpdate(endpoint, state) {
168727
168941
  try {
168728
- await this.construction.ready;
168942
+ await endpoint.construction.ready;
168729
168943
  } catch {
168730
168944
  return;
168731
168945
  }
168732
168946
  try {
168733
- const current = this.stateOf(HomeAssistantEntityBehavior).entity;
168734
- await this.setStateOf(HomeAssistantEntityBehavior, {
168735
- entity: { ...current, state: { ...state } }
168947
+ const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
168948
+ await endpoint.setStateOf(HomeAssistantEntityBehavior, {
168949
+ entity: { ...current, state }
168736
168950
  });
168737
168951
  } catch (error) {
168738
168952
  if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
@@ -168748,7 +168962,9 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
168748
168962
  }
168749
168963
  }
168750
168964
  async delete() {
168751
- this.debouncedFlush?.clear();
168965
+ for (const fn of this.debouncedUpdates.values()) {
168966
+ fn.clear();
168967
+ }
168752
168968
  await super.delete();
168753
168969
  }
168754
168970
  };
@@ -168833,7 +169049,7 @@ function PressureMeasurementServer2(config10) {
168833
169049
 
168834
169050
  // src/matter/endpoints/composed/composed-sensor-endpoint.ts
168835
169051
  var logger171 = Logger.get("ComposedSensorEndpoint");
168836
- var temperatureConfig = {
169052
+ var temperatureConfig2 = {
168837
169053
  getValue(entity, agent) {
168838
169054
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
168839
169055
  const state = entity.state;
@@ -168846,7 +169062,7 @@ var temperatureConfig = {
168846
169062
  );
168847
169063
  }
168848
169064
  };
168849
- var humidityConfig = {
169065
+ var humidityConfig2 = {
168850
169066
  getValue({ state }) {
168851
169067
  if (state == null || Number.isNaN(+state)) return null;
168852
169068
  return +state;
@@ -168873,15 +169089,15 @@ var batteryConfig2 = {
168873
169089
  return null;
168874
169090
  }
168875
169091
  };
168876
- var TemperatureSubType = TemperatureSensorDevice.with(
169092
+ var TemperatureSubType2 = TemperatureSensorDevice.with(
168877
169093
  IdentifyServer2,
168878
169094
  HomeAssistantEntityBehavior,
168879
- TemperatureMeasurementServer2(temperatureConfig)
169095
+ TemperatureMeasurementServer2(temperatureConfig2)
168880
169096
  );
168881
- var HumiditySubType = HumiditySensorDevice.with(
169097
+ var HumiditySubType2 = HumiditySensorDevice.with(
168882
169098
  IdentifyServer2,
168883
169099
  HomeAssistantEntityBehavior,
168884
- HumidityMeasurementServer(humidityConfig)
169100
+ HumidityMeasurementServer(humidityConfig2)
168885
169101
  );
168886
169102
  var PressureSubType = PressureSensorDevice.with(
168887
169103
  IdentifyServer2,
@@ -168937,7 +169153,7 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168937
169153
  const endpointId = createEndpointId3(primaryEntityId, config10.customName);
168938
169154
  const parts = [];
168939
169155
  const tempSub = new Endpoint(
168940
- TemperatureSubType.set({
169156
+ TemperatureSubType2.set({
168941
169157
  homeAssistantEntity: { entity: primaryPayload }
168942
169158
  }),
168943
169159
  { id: `${endpointId}_temp` }
@@ -168948,7 +169164,7 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
168948
169164
  const humPayload = buildEntityPayload2(registry2, config10.humidityEntityId);
168949
169165
  if (humPayload) {
168950
169166
  humSub = new Endpoint(
168951
- HumiditySubType.set({
169167
+ HumiditySubType2.set({
168952
169168
  homeAssistantEntity: { entity: humPayload }
168953
169169
  }),
168954
169170
  { id: `${endpointId}_humidity` }
@@ -169064,6 +169280,15 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
169064
169280
  }
169065
169281
  };
169066
169282
 
169283
+ // src/matter/endpoints/composed/user-composed-endpoint.ts
169284
+ init_esm();
169285
+ init_esm7();
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();
169291
+
169067
169292
  // src/matter/endpoints/legacy/air-purifier/index.ts
169068
169293
  init_dist();
169069
169294
  init_home_assistant_entity_behavior();
@@ -169857,7 +170082,7 @@ var ClimateFanControlServer = FanControlServer2(config3).with(
169857
170082
  );
169858
170083
 
169859
170084
  // src/matter/endpoints/legacy/climate/behaviors/climate-humidity-measurement-server.ts
169860
- var humidityConfig2 = {
170085
+ var humidityConfig3 = {
169861
170086
  getValue(entity) {
169862
170087
  const attributes7 = entity.attributes;
169863
170088
  const humidity = attributes7.current_humidity;
@@ -169867,11 +170092,18 @@ var humidityConfig2 = {
169867
170092
  return +humidity;
169868
170093
  }
169869
170094
  };
169870
- var ClimateHumidityMeasurementServer = HumidityMeasurementServer(humidityConfig2);
170095
+ var ClimateHumidityMeasurementServer = HumidityMeasurementServer(humidityConfig3);
169871
170096
 
169872
170097
  // src/matter/endpoints/legacy/climate/behaviors/climate-on-off-server.ts
170098
+ init_home_assistant_entity_behavior();
169873
170099
  var ClimateOnOffServer = OnOffServer2({
169874
- 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
+ },
169875
170107
  turnOff: () => ({ action: "climate.turn_off" })
169876
170108
  }).with("Lighting");
169877
170109
 
@@ -170048,7 +170280,8 @@ var ThermostatServerBase = class extends FullFeaturedBase {
170048
170280
  logger174.debug(
170049
170281
  `update: limits heat=[${minHeatLimit}, ${maxHeatLimit}], cool=[${minCoolLimit}, ${maxCoolLimit}], systemMode=${systemMode}, runningMode=${runningMode}`
170050
170282
  );
170051
- const controlSequence = config10.getControlSequence(entity.state, this.agent);
170283
+ let controlSequence = config10.getControlSequence(entity.state, this.agent);
170284
+ controlSequence = this.clampControlSequence(controlSequence);
170052
170285
  this.internal.controlSequenceOfOperation = controlSequence;
170053
170286
  applyPatchState(this.state, {
170054
170287
  ...this.features.heating ? {
@@ -170303,6 +170536,26 @@ var ThermostatServerBase = class extends FullFeaturedBase {
170303
170536
  }
170304
170537
  }
170305
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
+ }
170306
170559
  clampSetpoint(value, min, max, type) {
170307
170560
  const effectiveMin = min ?? 0;
170308
170561
  const effectiveMax = max ?? 5e3;
@@ -170551,7 +170804,7 @@ var config4 = {
170551
170804
  (m) => m === ClimateHvacMode.cool || m === ClimateHvacMode.heat_cool
170552
170805
  );
170553
170806
  const hasHeating = modes.some(
170554
- (m) => m === ClimateHvacMode.heat || m === ClimateHvacMode.heat_cool || m === ClimateHvacMode.auto
170807
+ (m) => m === ClimateHvacMode.heat || m === ClimateHvacMode.heat_cool
170555
170808
  );
170556
170809
  if (hasCooling && hasHeating) {
170557
170810
  const hasAutoMode = modes.includes(ClimateHvacMode.heat_cool) && (modes.includes(ClimateHvacMode.heat) || modes.includes(ClimateHvacMode.cool));
@@ -170611,25 +170864,6 @@ function ClimateThermostatServer(initialState = {}, features2) {
170611
170864
  }
170612
170865
 
170613
170866
  // src/matter/endpoints/legacy/climate/index.ts
170614
- var ClimatePowerSourceServer = PowerSourceServer2({
170615
- getBatteryPercent: (entity, agent) => {
170616
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
170617
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
170618
- if (batteryEntity) {
170619
- const stateProvider = agent.env.get(EntityStateProvider);
170620
- const battery = stateProvider.getBatteryPercent(batteryEntity);
170621
- if (battery != null) {
170622
- return Math.max(0, Math.min(100, battery));
170623
- }
170624
- }
170625
- const attrs = entity.attributes;
170626
- const level = attrs.battery_level ?? attrs.battery;
170627
- if (level == null || Number.isNaN(Number(level))) {
170628
- return null;
170629
- }
170630
- return Number(level);
170631
- }
170632
- });
170633
170867
  var ClimateDeviceType = (supportsOnOff, supportsHumidity, supportsFanMode, hasBattery, features2, initialState = {}) => {
170634
170868
  const additionalClusters = [];
170635
170869
  if (supportsOnOff) {
@@ -170639,7 +170873,7 @@ var ClimateDeviceType = (supportsOnOff, supportsHumidity, supportsFanMode, hasBa
170639
170873
  additionalClusters.push(ClimateHumidityMeasurementServer);
170640
170874
  }
170641
170875
  if (hasBattery) {
170642
- additionalClusters.push(ClimatePowerSourceServer);
170876
+ additionalClusters.push(DefaultPowerSourceServer);
170643
170877
  }
170644
170878
  const thermostatServer = ClimateThermostatServer(initialState, features2);
170645
170879
  if (supportsFanMode) {
@@ -170859,6 +171093,22 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
170859
171093
  );
170860
171094
  const currentTilt100ths = currentTilt != null ? currentTilt * 100 : null;
170861
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
+ };
170862
171112
  logger175.debug(
170863
171113
  `Cover update for ${entity.entity_id}: state=${state.state}, lift=${currentLift}%, tilt=${currentTilt}%, movement=${MovementStatus[movementStatus]}`
170864
171114
  );
@@ -170885,14 +171135,18 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
170885
171135
  ...this.features.positionAwareLift ? {
170886
171136
  currentPositionLiftPercentage: currentLift,
170887
171137
  currentPositionLiftPercent100ths: currentLift100ths,
170888
- // When stopped, target MUST equal current for controllers to show correct state
170889
- targetPositionLiftPercent100ths: isStopped ? currentLift100ths : this.state.targetPositionLiftPercent100ths ?? currentLift100ths
171138
+ targetPositionLiftPercent100ths: inferTarget(
171139
+ currentLift100ths,
171140
+ this.state.targetPositionLiftPercent100ths
171141
+ )
170890
171142
  } : {},
170891
171143
  ...this.features.positionAwareTilt ? {
170892
171144
  currentPositionTiltPercentage: currentTilt,
170893
171145
  currentPositionTiltPercent100ths: currentTilt100ths,
170894
- // When stopped, target MUST equal current for controllers to show correct state
170895
- targetPositionTiltPercent100ths: isStopped ? currentTilt100ths : this.state.targetPositionTiltPercent100ths ?? currentTilt100ths
171146
+ targetPositionTiltPercent100ths: inferTarget(
171147
+ currentTilt100ths,
171148
+ this.state.targetPositionTiltPercent100ths
171149
+ )
170896
171150
  } : {}
170897
171151
  }
170898
171152
  );
@@ -171114,6 +171368,9 @@ var adjustPositionForWriting2 = (position, agent) => {
171114
171368
  return adjustPositionForWriting(position, featureFlags, matterSem);
171115
171369
  };
171116
171370
  var shouldSwapOpenClose = (agent) => {
171371
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171372
+ const entitySwap = homeAssistant.state.mapping?.coverSwapOpenClose;
171373
+ if (entitySwap !== void 0) return entitySwap;
171117
171374
  const { featureFlags } = agent.env.get(BridgeDataProvider);
171118
171375
  return featureFlags?.coverSwapOpenClose === true;
171119
171376
  };
@@ -171210,25 +171467,6 @@ var CoverWindowCoveringServer = WindowCoveringServer2(config5);
171210
171467
 
171211
171468
  // src/matter/endpoints/legacy/cover/index.ts
171212
171469
  var logger177 = Logger.get("CoverDevice");
171213
- var CoverPowerSourceServer = PowerSourceServer2({
171214
- getBatteryPercent: (entity, agent) => {
171215
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171216
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171217
- if (batteryEntity) {
171218
- const stateProvider = agent.env.get(EntityStateProvider);
171219
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171220
- if (battery != null) {
171221
- return Math.max(0, Math.min(100, battery));
171222
- }
171223
- }
171224
- const attrs = entity.attributes;
171225
- const level = attrs.battery_level ?? attrs.battery;
171226
- if (level == null || Number.isNaN(Number(level))) {
171227
- return null;
171228
- }
171229
- return Number(level);
171230
- }
171231
- });
171232
171470
  var CoverDeviceType = (supportedFeatures, hasBattery, entityId) => {
171233
171471
  const features2 = /* @__PURE__ */ new Set();
171234
171472
  if (testBit(supportedFeatures, CoverSupportedFeatures.support_open)) {
@@ -171263,7 +171501,10 @@ var CoverDeviceType = (supportedFeatures, hasBattery, entityId) => {
171263
171501
  CoverWindowCoveringServer.with(...features2)
171264
171502
  ];
171265
171503
  if (hasBattery) {
171266
- return WindowCoveringDevice.with(...baseBehaviors, CoverPowerSourceServer);
171504
+ return WindowCoveringDevice.with(
171505
+ ...baseBehaviors,
171506
+ DefaultPowerSourceServer
171507
+ );
171267
171508
  }
171268
171509
  return WindowCoveringDevice.with(...baseBehaviors);
171269
171510
  };
@@ -171384,25 +171625,6 @@ function EventDevice(homeAssistantEntity) {
171384
171625
  // src/matter/endpoints/legacy/fan/index.ts
171385
171626
  init_dist();
171386
171627
  init_home_assistant_entity_behavior();
171387
- var FanPowerSourceServer = PowerSourceServer2({
171388
- getBatteryPercent: (entity, agent) => {
171389
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171390
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171391
- if (batteryEntity) {
171392
- const stateProvider = agent.env.get(EntityStateProvider);
171393
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171394
- if (battery != null) {
171395
- return Math.max(0, Math.min(100, battery));
171396
- }
171397
- }
171398
- const attrs = entity.attributes;
171399
- const level = attrs.battery_level ?? attrs.battery;
171400
- if (level == null || Number.isNaN(Number(level))) {
171401
- return null;
171402
- }
171403
- return Number(level);
171404
- }
171405
- });
171406
171628
  function FanDevice2(homeAssistantEntity) {
171407
171629
  const attributes7 = homeAssistantEntity.entity.state.attributes;
171408
171630
  const supportedFeatures = attributes7.supported_features ?? 0;
@@ -171422,7 +171644,7 @@ function FanDevice2(homeAssistantEntity) {
171422
171644
  BasicInformationServer2,
171423
171645
  HomeAssistantEntityBehavior,
171424
171646
  FanOnOffServer,
171425
- FanPowerSourceServer
171647
+ DefaultPowerSourceServer
171426
171648
  ) : OnOffPlugInUnitDevice.with(
171427
171649
  IdentifyServer2,
171428
171650
  BasicInformationServer2,
@@ -171457,7 +171679,7 @@ function FanDevice2(homeAssistantEntity) {
171457
171679
  HomeAssistantEntityBehavior,
171458
171680
  FanOnOffServer,
171459
171681
  FanFanControlServer.with(...features2),
171460
- FanPowerSourceServer
171682
+ DefaultPowerSourceServer
171461
171683
  ) : FanDevice.with(
171462
171684
  IdentifyServer2,
171463
171685
  BasicInformationServer2,
@@ -171982,25 +172204,6 @@ var ColorTemperatureLightType = ColorTemperatureLightDevice.with(
171982
172204
 
171983
172205
  // src/matter/endpoints/legacy/light/devices/dimmable-light.ts
171984
172206
  init_home_assistant_entity_behavior();
171985
- var LightPowerSourceServer = PowerSourceServer2({
171986
- getBatteryPercent: (entity, agent) => {
171987
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171988
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
171989
- if (batteryEntity) {
171990
- const stateProvider = agent.env.get(EntityStateProvider);
171991
- const battery = stateProvider.getBatteryPercent(batteryEntity);
171992
- if (battery != null) {
171993
- return Math.max(0, Math.min(100, battery));
171994
- }
171995
- }
171996
- const attrs = entity.attributes;
171997
- const level = attrs.battery_level ?? attrs.battery;
171998
- if (level == null || Number.isNaN(Number(level))) {
171999
- return null;
172000
- }
172001
- return Number(level);
172002
- }
172003
- });
172004
172207
  var DimmableLightType = DimmableLightDevice.with(
172005
172208
  IdentifyServer2,
172006
172209
  BasicInformationServer2,
@@ -172014,30 +172217,11 @@ var DimmableLightWithBatteryType = DimmableLightDevice.with(
172014
172217
  HomeAssistantEntityBehavior,
172015
172218
  LightOnOffServer,
172016
172219
  LightLevelControlServer,
172017
- LightPowerSourceServer
172220
+ DefaultPowerSourceServer
172018
172221
  );
172019
172222
 
172020
172223
  // src/matter/endpoints/legacy/light/devices/extended-color-light.ts
172021
172224
  init_home_assistant_entity_behavior();
172022
- var LightPowerSourceServer2 = PowerSourceServer2({
172023
- getBatteryPercent: (entity, agent) => {
172024
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
172025
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
172026
- if (batteryEntity) {
172027
- const stateProvider = agent.env.get(EntityStateProvider);
172028
- const battery = stateProvider.getBatteryPercent(batteryEntity);
172029
- if (battery != null) {
172030
- return Math.max(0, Math.min(100, battery));
172031
- }
172032
- }
172033
- const attrs = entity.attributes;
172034
- const level = attrs.battery_level ?? attrs.battery;
172035
- if (level == null || Number.isNaN(Number(level))) {
172036
- return null;
172037
- }
172038
- return Number(level);
172039
- }
172040
- });
172041
172225
  var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBattery = false) => {
172042
172226
  const features2 = /* @__PURE__ */ new Set();
172043
172227
  if (supportsColorControl) {
@@ -172054,7 +172238,7 @@ var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBatt
172054
172238
  LightOnOffServer,
172055
172239
  LightLevelControlServer,
172056
172240
  LightColorControlServer.with(...features2),
172057
- LightPowerSourceServer2
172241
+ DefaultPowerSourceServer
172058
172242
  );
172059
172243
  }
172060
172244
  return ExtendedColorLightDevice.with(
@@ -172069,25 +172253,6 @@ var ExtendedColorLightType = (supportsColorControl, supportsTemperature, hasBatt
172069
172253
 
172070
172254
  // src/matter/endpoints/legacy/light/devices/on-off-light-device.ts
172071
172255
  init_home_assistant_entity_behavior();
172072
- var LightPowerSourceServer3 = PowerSourceServer2({
172073
- getBatteryPercent: (entity, agent) => {
172074
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
172075
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
172076
- if (batteryEntity) {
172077
- const stateProvider = agent.env.get(EntityStateProvider);
172078
- const battery = stateProvider.getBatteryPercent(batteryEntity);
172079
- if (battery != null) {
172080
- return Math.max(0, Math.min(100, battery));
172081
- }
172082
- }
172083
- const attrs = entity.attributes;
172084
- const level = attrs.battery_level ?? attrs.battery;
172085
- if (level == null || Number.isNaN(Number(level))) {
172086
- return null;
172087
- }
172088
- return Number(level);
172089
- }
172090
- });
172091
172256
  var OnOffLightType = OnOffLightDevice.with(
172092
172257
  IdentifyServer2,
172093
172258
  BasicInformationServer2,
@@ -172099,7 +172264,7 @@ var OnOffLightWithBatteryType = OnOffLightDevice.with(
172099
172264
  BasicInformationServer2,
172100
172265
  HomeAssistantEntityBehavior,
172101
172266
  LightOnOffServer,
172102
- LightPowerSourceServer3
172267
+ DefaultPowerSourceServer
172103
172268
  );
172104
172269
 
172105
172270
  // src/matter/endpoints/legacy/light/index.ts
@@ -172790,25 +172955,6 @@ var lockServerConfig = {
172790
172955
  unlock: () => ({ action: "lock.unlock" }),
172791
172956
  unlatch: () => ({ action: "lock.open" })
172792
172957
  };
172793
- var LockPowerSourceServer = PowerSourceServer2({
172794
- getBatteryPercent: (entity, agent) => {
172795
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
172796
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
172797
- if (batteryEntity) {
172798
- const stateProvider = agent.env.get(EntityStateProvider);
172799
- const battery = stateProvider.getBatteryPercent(batteryEntity);
172800
- if (battery != null) {
172801
- return Math.max(0, Math.min(100, battery));
172802
- }
172803
- }
172804
- const attrs = entity.attributes;
172805
- const level = attrs.battery_level ?? attrs.battery;
172806
- if (level == null || Number.isNaN(Number(level))) {
172807
- return null;
172808
- }
172809
- return Number(level);
172810
- }
172811
- });
172812
172958
  var LockDeviceType = DoorLockDevice.with(
172813
172959
  BasicInformationServer2,
172814
172960
  IdentifyServer2,
@@ -172820,7 +172966,7 @@ var LockWithBatteryDeviceType = DoorLockDevice.with(
172820
172966
  IdentifyServer2,
172821
172967
  HomeAssistantEntityBehavior,
172822
172968
  LockServerWithPin(lockServerConfig),
172823
- LockPowerSourceServer
172969
+ DefaultPowerSourceServer
172824
172970
  );
172825
172971
  var LockWithUnlatchDeviceType = DoorLockDevice.with(
172826
172972
  BasicInformationServer2,
@@ -172833,7 +172979,7 @@ var LockWithUnlatchAndBatteryDeviceType = DoorLockDevice.with(
172833
172979
  IdentifyServer2,
172834
172980
  HomeAssistantEntityBehavior,
172835
172981
  LockServerWithPinAndUnbolt(lockServerConfig),
172836
- LockPowerSourceServer
172982
+ DefaultPowerSourceServer
172837
172983
  );
172838
172984
  function LockDevice(homeAssistantEntity) {
172839
172985
  const attrs = homeAssistantEntity.entity.state.attributes;
@@ -173409,8 +173555,20 @@ var PumpType = PumpDevice.with(
173409
173555
  PumpOnOffServer,
173410
173556
  PumpConfigurationAndControlServer2
173411
173557
  );
173558
+ var PumpWithBatteryType = PumpDevice.with(
173559
+ IdentifyServer2,
173560
+ BasicInformationServer2,
173561
+ HomeAssistantEntityBehavior,
173562
+ PumpOnOffServer,
173563
+ PumpConfigurationAndControlServer2,
173564
+ DefaultPowerSourceServer
173565
+ );
173412
173566
  function PumpEndpoint(homeAssistantEntity) {
173413
- 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 });
173414
173572
  }
173415
173573
 
173416
173574
  // src/matter/endpoints/legacy/remote/index.ts
@@ -174931,7 +175089,7 @@ var Pm25SensorType = AirQualitySensorDevice.with(
174931
175089
 
174932
175090
  // src/matter/endpoints/legacy/sensor/devices/temperature-humidity-sensor.ts
174933
175091
  init_home_assistant_entity_behavior();
174934
- var temperatureConfig2 = {
175092
+ var temperatureConfig3 = {
174935
175093
  getValue(entity, agent) {
174936
175094
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
174937
175095
  const state = entity.state;
@@ -174946,7 +175104,7 @@ var temperatureConfig2 = {
174946
175104
  );
174947
175105
  }
174948
175106
  };
174949
- var humidityConfig3 = {
175107
+ var humidityConfig4 = {
174950
175108
  getValue(_entity, agent) {
174951
175109
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
174952
175110
  const humidityEntity = homeAssistant.state.mapping?.humidityEntity;
@@ -174996,38 +175154,38 @@ var TemperatureHumiditySensorType = TemperatureSensorDevice.with(
174996
175154
  BasicInformationServer2,
174997
175155
  IdentifyServer2,
174998
175156
  HomeAssistantEntityBehavior,
174999
- TemperatureMeasurementServer2(temperatureConfig2),
175000
- HumidityMeasurementServer(humidityConfig3)
175157
+ TemperatureMeasurementServer2(temperatureConfig3),
175158
+ HumidityMeasurementServer(humidityConfig4)
175001
175159
  );
175002
175160
  var TemperatureHumiditySensorWithBatteryType = TemperatureSensorDevice.with(
175003
175161
  BasicInformationServer2,
175004
175162
  IdentifyServer2,
175005
175163
  HomeAssistantEntityBehavior,
175006
- TemperatureMeasurementServer2(temperatureConfig2),
175007
- HumidityMeasurementServer(humidityConfig3),
175164
+ TemperatureMeasurementServer2(temperatureConfig3),
175165
+ HumidityMeasurementServer(humidityConfig4),
175008
175166
  PowerSourceServer2(batteryConfig4)
175009
175167
  );
175010
175168
  var TemperatureHumidityPressureSensorType = TemperatureSensorDevice.with(
175011
175169
  BasicInformationServer2,
175012
175170
  IdentifyServer2,
175013
175171
  HomeAssistantEntityBehavior,
175014
- TemperatureMeasurementServer2(temperatureConfig2),
175015
- HumidityMeasurementServer(humidityConfig3),
175172
+ TemperatureMeasurementServer2(temperatureConfig3),
175173
+ HumidityMeasurementServer(humidityConfig4),
175016
175174
  PressureMeasurementServer2(pressureConfig2)
175017
175175
  );
175018
175176
  var TemperatureHumidityPressureSensorWithBatteryType = TemperatureSensorDevice.with(
175019
175177
  BasicInformationServer2,
175020
175178
  IdentifyServer2,
175021
175179
  HomeAssistantEntityBehavior,
175022
- TemperatureMeasurementServer2(temperatureConfig2),
175023
- HumidityMeasurementServer(humidityConfig3),
175180
+ TemperatureMeasurementServer2(temperatureConfig3),
175181
+ HumidityMeasurementServer(humidityConfig4),
175024
175182
  PressureMeasurementServer2(pressureConfig2),
175025
175183
  PowerSourceServer2(batteryConfig4)
175026
175184
  );
175027
175185
 
175028
175186
  // src/matter/endpoints/legacy/sensor/devices/temperature-pressure-sensor.ts
175029
175187
  init_home_assistant_entity_behavior();
175030
- var temperatureConfig3 = {
175188
+ var temperatureConfig4 = {
175031
175189
  getValue(entity, agent) {
175032
175190
  const fallbackUnit = agent.env.get(HomeAssistantConfig).unitSystem.temperature;
175033
175191
  const state = entity.state;
@@ -175078,14 +175236,14 @@ var TemperaturePressureSensorType = TemperatureSensorDevice.with(
175078
175236
  BasicInformationServer2,
175079
175237
  IdentifyServer2,
175080
175238
  HomeAssistantEntityBehavior,
175081
- TemperatureMeasurementServer2(temperatureConfig3),
175239
+ TemperatureMeasurementServer2(temperatureConfig4),
175082
175240
  PressureMeasurementServer2(pressureConfig3)
175083
175241
  );
175084
175242
  var TemperaturePressureSensorWithBatteryType = TemperatureSensorDevice.with(
175085
175243
  BasicInformationServer2,
175086
175244
  IdentifyServer2,
175087
175245
  HomeAssistantEntityBehavior,
175088
- TemperatureMeasurementServer2(temperatureConfig3),
175246
+ TemperatureMeasurementServer2(temperatureConfig4),
175089
175247
  PressureMeasurementServer2(pressureConfig3),
175090
175248
  PowerSourceServer2(batteryConfig5)
175091
175249
  );
@@ -175215,25 +175373,7 @@ var SwitchWithBatteryEndpointType = OnOffPlugInUnitDevice.with(
175215
175373
  IdentifyServer2,
175216
175374
  HomeAssistantEntityBehavior,
175217
175375
  SwitchOnOffServer,
175218
- PowerSourceServer2({
175219
- getBatteryPercent: (entity, agent) => {
175220
- const homeAssistant = agent.get(HomeAssistantEntityBehavior);
175221
- const batteryEntity = homeAssistant.state.mapping?.batteryEntity;
175222
- if (batteryEntity) {
175223
- const stateProvider = agent.env.get(EntityStateProvider);
175224
- const battery = stateProvider.getBatteryPercent(batteryEntity);
175225
- if (battery != null) {
175226
- return Math.max(0, Math.min(100, battery));
175227
- }
175228
- }
175229
- const attrs = entity.attributes;
175230
- const level = attrs.battery_level ?? attrs.battery;
175231
- if (level == null || Number.isNaN(Number(level))) {
175232
- return null;
175233
- }
175234
- return Number(level);
175235
- }
175236
- })
175376
+ DefaultPowerSourceServer
175237
175377
  );
175238
175378
  function SwitchDevice(homeAssistantEntity) {
175239
175379
  const attrs = homeAssistantEntity.entity.state.attributes;
@@ -177430,8 +177570,19 @@ var ValveEndpointType = WaterValveDevice.with(
177430
177570
  HomeAssistantEntityBehavior,
177431
177571
  ValveServer
177432
177572
  );
177573
+ var ValveWithBatteryEndpointType = WaterValveDevice.with(
177574
+ BasicInformationServer2,
177575
+ IdentifyServer2,
177576
+ HomeAssistantEntityBehavior,
177577
+ ValveServer,
177578
+ DefaultPowerSourceServer
177579
+ );
177433
177580
  function ValveDevice(homeAssistantEntity) {
177434
- 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 });
177435
177586
  }
177436
177587
 
177437
177588
  // src/matter/endpoints/legacy/water-heater/index.ts
@@ -177552,6 +177703,7 @@ function WaterHeaterDevice(homeAssistantEntity) {
177552
177703
  }
177553
177704
 
177554
177705
  // src/matter/endpoints/legacy/create-legacy-endpoint-type.ts
177706
+ var legacyLogger = Logger.get("LegacyEndpointType");
177555
177707
  function createLegacyEndpointType(entity, mapping, areaName, options) {
177556
177708
  const domain = entity.entity_id.split(".")[0];
177557
177709
  const customName = mapping?.customName;
@@ -177571,10 +177723,20 @@ function createLegacyEndpointType(entity, mapping, areaName, options) {
177571
177723
  );
177572
177724
  } else {
177573
177725
  const factory = deviceCtrs[domain];
177574
- 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 {
177575
177738
  return void 0;
177576
177739
  }
177577
- type = factory({ entity, customName, mapping });
177578
177740
  }
177579
177741
  }
177580
177742
  if (!type) {
@@ -177732,10 +177894,192 @@ var matterDeviceTypeFactories = {
177732
177894
  })
177733
177895
  };
177734
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
+
177735
178079
  // src/matter/endpoints/legacy/legacy-endpoint.ts
177736
- var logger198 = Logger.get("LegacyEndpoint");
178080
+ var logger199 = Logger.get("LegacyEndpoint");
177737
178081
  var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177738
- static async create(registry2, entityId, mapping) {
178082
+ static async create(registry2, entityId, mapping, pluginDomainMappings) {
177739
178083
  const deviceRegistry = registry2.deviceOf(entityId);
177740
178084
  let state = registry2.initialState(entityId);
177741
178085
  const entity = registry2.entity(entityId);
@@ -177743,25 +178087,25 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177743
178087
  return;
177744
178088
  }
177745
178089
  if (registry2.isAutoBatteryMappingEnabled() && registry2.isBatteryEntityUsed(entityId)) {
177746
- logger198.debug(
178090
+ logger199.debug(
177747
178091
  `Skipping ${entityId} - already auto-assigned as battery to another device`
177748
178092
  );
177749
178093
  return;
177750
178094
  }
177751
178095
  if (registry2.isAutoHumidityMappingEnabled() && registry2.isHumidityEntityUsed(entityId)) {
177752
- logger198.debug(
178096
+ logger199.debug(
177753
178097
  `Skipping ${entityId} - already auto-assigned as humidity to a temperature sensor`
177754
178098
  );
177755
178099
  return;
177756
178100
  }
177757
178101
  if (registry2.isAutoPressureMappingEnabled() && registry2.isPressureEntityUsed(entityId)) {
177758
- logger198.debug(
178102
+ logger199.debug(
177759
178103
  `Skipping ${entityId} - already auto-assigned as pressure to a temperature sensor`
177760
178104
  );
177761
178105
  return;
177762
178106
  }
177763
178107
  if (registry2.isAutoComposedDevicesEnabled() && registry2.isComposedSubEntityUsed(entityId)) {
177764
- logger198.debug(
178108
+ logger199.debug(
177765
178109
  `Skipping ${entityId} - already consumed by a composed device`
177766
178110
  );
177767
178111
  return;
@@ -177781,7 +178125,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177781
178125
  humidityEntity: humidityEntityId
177782
178126
  };
177783
178127
  registry2.markHumidityEntityUsed(humidityEntityId);
177784
- logger198.debug(
178128
+ logger199.debug(
177785
178129
  `Auto-assigned humidity ${humidityEntityId} to ${entityId}`
177786
178130
  );
177787
178131
  }
@@ -177800,7 +178144,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177800
178144
  pressureEntity: pressureEntityId
177801
178145
  };
177802
178146
  registry2.markPressureEntityUsed(pressureEntityId);
177803
- logger198.debug(
178147
+ logger199.debug(
177804
178148
  `Auto-assigned pressure ${pressureEntityId} to ${entityId}`
177805
178149
  );
177806
178150
  }
@@ -177818,7 +178162,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177818
178162
  batteryEntity: batteryEntityId
177819
178163
  };
177820
178164
  registry2.markBatteryEntityUsed(batteryEntityId);
177821
- logger198.debug(
178165
+ logger199.debug(
177822
178166
  `Auto-assigned battery ${batteryEntityId} to ${entityId}`
177823
178167
  );
177824
178168
  }
@@ -177836,7 +178180,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177836
178180
  powerEntity: powerEntityId
177837
178181
  };
177838
178182
  registry2.markPowerEntityUsed(powerEntityId);
177839
- logger198.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
178183
+ logger199.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
177840
178184
  }
177841
178185
  }
177842
178186
  }
@@ -177853,7 +178197,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177853
178197
  energyEntity: energyEntityId
177854
178198
  };
177855
178199
  registry2.markEnergyEntityUsed(energyEntityId);
177856
- logger198.debug(
178200
+ logger199.debug(
177857
178201
  `Auto-assigned energy ${energyEntityId} to ${entityId}`
177858
178202
  );
177859
178203
  }
@@ -177869,7 +178213,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177869
178213
  entityId: effectiveMapping?.entityId ?? entityId,
177870
178214
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
177871
178215
  };
177872
- logger198.debug(
178216
+ logger199.debug(
177873
178217
  `Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity} to ${entityId}`
177874
178218
  );
177875
178219
  }
@@ -177879,7 +178223,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177879
178223
  entityId: effectiveMapping?.entityId ?? entityId,
177880
178224
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
177881
178225
  };
177882
- logger198.debug(
178226
+ logger199.debug(
177883
178227
  `Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity} to ${entityId}`
177884
178228
  );
177885
178229
  }
@@ -177889,7 +178233,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177889
178233
  entityId: effectiveMapping?.entityId ?? entityId,
177890
178234
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
177891
178235
  };
177892
- logger198.debug(
178236
+ logger199.debug(
177893
178237
  `Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity} to ${entityId}`
177894
178238
  );
177895
178239
  }
@@ -177904,7 +178248,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177904
178248
  entityId: effectiveMapping?.entityId ?? entityId,
177905
178249
  cleanAreaRooms
177906
178250
  };
177907
- logger198.debug(
178251
+ logger199.debug(
177908
178252
  `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA for ${entityId}`
177909
178253
  );
177910
178254
  }
@@ -177925,7 +178269,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177925
178269
  rooms: roomsObj
177926
178270
  }
177927
178271
  };
177928
- logger198.debug(
178272
+ logger199.debug(
177929
178273
  `Auto-detected ${valetudoRooms.length} Valetudo segments for ${entityId}`
177930
178274
  );
177931
178275
  } else {
@@ -177942,7 +178286,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177942
178286
  rooms: roomsObj
177943
178287
  }
177944
178288
  };
177945
- logger198.debug(
178289
+ logger199.debug(
177946
178290
  `Auto-detected ${roborockRooms.length} Roborock rooms for ${entityId}`
177947
178291
  );
177948
178292
  }
@@ -177950,6 +178294,23 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177950
178294
  }
177951
178295
  }
177952
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
+ }
177953
178314
  if (registry2.isAutoComposedDevicesEnabled()) {
177954
178315
  const attrs = state.attributes;
177955
178316
  if (entityId.startsWith("sensor.") && attrs.device_class === SensorDeviceClass.temperature && (effectiveMapping?.humidityEntity || effectiveMapping?.pressureEntity)) {
@@ -177966,13 +178327,9 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177966
178327
  return composed;
177967
178328
  }
177968
178329
  const resolvedMatterType = mapping?.matterDeviceType ?? (entityId.startsWith("fan.") ? "fan" : void 0);
177969
- if (resolvedMatterType === "air_purifier" && entity.device_id) {
177970
- const temperatureEntityId = registry2.findTemperatureEntityForDevice(
177971
- entity.device_id
177972
- );
177973
- const humidityEntityId = registry2.findHumidityEntityForDevice(
177974
- entity.device_id
177975
- );
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);
177976
178333
  if (temperatureEntityId || humidityEntityId) {
177977
178334
  const composedAreaName = registry2.getAreaName(entityId);
177978
178335
  const composed = await ComposedAirPurifierEndpoint.create({
@@ -178017,7 +178374,8 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178017
178374
  const areaName = registry2.getAreaName(entityId);
178018
178375
  const type = createLegacyEndpointType(payload, effectiveMapping, areaName, {
178019
178376
  vacuumOnOff: registry2.isVacuumOnOffEnabled(),
178020
- cleaningModeOptions
178377
+ cleaningModeOptions,
178378
+ pluginDomainMappings
178021
178379
  });
178022
178380
  if (!type) {
178023
178381
  return;
@@ -178028,7 +178386,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178028
178386
  }
178029
178387
  constructor(type, entityId, customName, mappedEntityIds) {
178030
178388
  super(type, entityId, customName, mappedEntityIds);
178031
- this.flushUpdate = debounce4(this.flushPendingUpdate.bind(this), 50);
178389
+ this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
178032
178390
  }
178033
178391
  lastState;
178034
178392
  pendingMappedChange = false;
@@ -178045,11 +178403,11 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178045
178403
  }
178046
178404
  if (mappedChanged) {
178047
178405
  this.pendingMappedChange = true;
178048
- logger198.debug(
178406
+ logger199.debug(
178049
178407
  `Mapped entity change detected for ${this.entityId}, forcing update`
178050
178408
  );
178051
178409
  }
178052
- logger198.debug(
178410
+ logger199.debug(
178053
178411
  `State update received for ${this.entityId}: state=${state.state}`
178054
178412
  );
178055
178413
  this.lastState = state;
@@ -178093,7 +178451,7 @@ import {
178093
178451
  getCollection
178094
178452
  } from "home-assistant-js-websocket";
178095
178453
  import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js";
178096
- var logger199 = Logger.get("SubscribeEntities");
178454
+ var logger200 = Logger.get("SubscribeEntities");
178097
178455
  function processEvent(store, updates) {
178098
178456
  const state = { ...store.state };
178099
178457
  if (updates.a) {
@@ -178119,7 +178477,7 @@ function processEvent(store, updates) {
178119
178477
  for (const entityId in updates.c) {
178120
178478
  let entityState = state[entityId];
178121
178479
  if (!entityState) {
178122
- logger199.warn("Received state update for unknown entity", entityId);
178480
+ logger200.warn("Received state update for unknown entity", entityId);
178123
178481
  continue;
178124
178482
  }
178125
178483
  entityState = { ...entityState };
@@ -178189,7 +178547,7 @@ var subscribeEntities = (conn, onChange, entityIds) => entitiesColl(conn, entity
178189
178547
 
178190
178548
  // src/services/bridges/entity-isolation-service.ts
178191
178549
  init_esm();
178192
- var logger200 = Logger.get("EntityIsolation");
178550
+ var logger201 = Logger.get("EntityIsolation");
178193
178551
  var EntityIsolationServiceImpl = class {
178194
178552
  isolatedEntities = /* @__PURE__ */ new Map();
178195
178553
  isolationCallbacks = /* @__PURE__ */ new Map();
@@ -178254,13 +178612,13 @@ var EntityIsolationServiceImpl = class {
178254
178612
  }
178255
178613
  const parsed = this.parseEndpointPath(msg);
178256
178614
  if (!parsed) {
178257
- logger200.warn("Could not parse entity from error:", msg);
178615
+ logger201.warn("Could not parse entity from error:", msg);
178258
178616
  return false;
178259
178617
  }
178260
178618
  const { bridgeId, entityName } = parsed;
178261
178619
  const callback = this.isolationCallbacks.get(bridgeId);
178262
178620
  if (!callback) {
178263
- logger200.warn(
178621
+ logger201.warn(
178264
178622
  `No isolation callback registered for bridge ${bridgeId}, entity: ${entityName}`
178265
178623
  );
178266
178624
  return false;
@@ -178271,14 +178629,14 @@ var EntityIsolationServiceImpl = class {
178271
178629
  }
178272
178630
  const reason = `${classification}. Entity isolated to protect bridge stability.`;
178273
178631
  this.isolatedEntities.set(key, { entityId: entityName, reason });
178274
- logger200.warn(
178632
+ logger201.warn(
178275
178633
  `Isolating entity "${entityName}" from bridge ${bridgeId} due to: ${reason}`
178276
178634
  );
178277
178635
  try {
178278
178636
  await callback(entityName);
178279
178637
  return true;
178280
178638
  } catch (e) {
178281
- logger200.error(`Failed to isolate entity ${entityName}:`, e);
178639
+ logger201.error(`Failed to isolate entity ${entityName}:`, e);
178282
178640
  return false;
178283
178641
  }
178284
178642
  }
@@ -178521,6 +178879,16 @@ var BridgeEndpointManager = class extends Service {
178521
178879
  }
178522
178880
  }
178523
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
+ }
178524
178892
  getEntityMapping(entityId) {
178525
178893
  return this.mappingStorage.getMapping(this.bridgeId, entityId);
178526
178894
  }
@@ -178577,16 +178945,20 @@ var BridgeEndpointManager = class extends Service {
178577
178945
  this.entityIds = this.registry.entityIds;
178578
178946
  if (this.registry.isAutoComposedDevicesEnabled()) {
178579
178947
  for (const eid of this.entityIds) {
178580
- if (!eid.startsWith("fan.")) continue;
178581
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;
178582
178957
  const matterType = m?.matterDeviceType ?? "fan";
178583
178958
  if (matterType !== "air_purifier") continue;
178584
178959
  const ent = this.registry.entity(eid);
178585
- if (!ent?.device_id) continue;
178586
- const tempId = this.registry.findTemperatureEntityForDevice(
178587
- ent.device_id
178588
- );
178589
- 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);
178590
178962
  if (tempId) this.registry.markComposedSubEntityUsed(tempId);
178591
178963
  if (humId) this.registry.markComposedSubEntityUsed(humId);
178592
178964
  }
@@ -178657,6 +179029,12 @@ var BridgeEndpointManager = class extends Service {
178657
179029
  this.log.debug(`Skipping disabled entity: ${entityId}`);
178658
179030
  continue;
178659
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
+ }
178660
179038
  if (entityId.length > MAX_ENTITY_ID_LENGTH) {
178661
179039
  const reason = `Entity ID too long (${entityId.length} chars, max ${MAX_ENTITY_ID_LENGTH}). This would cause filesystem errors.`;
178662
179040
  this.log.warn(`Skipping entity: ${entityId}. Reason: ${reason}`);
@@ -178666,10 +179044,12 @@ var BridgeEndpointManager = class extends Service {
178666
179044
  let endpoint = existingEndpoints.find((e) => e.entityId === entityId);
178667
179045
  if (!endpoint) {
178668
179046
  try {
179047
+ const domainMappings = this.getPluginDomainMappings();
178669
179048
  endpoint = await LegacyEndpoint.create(
178670
179049
  this.registry,
178671
179050
  entityId,
178672
- mapping
179051
+ mapping,
179052
+ domainMappings
178673
179053
  );
178674
179054
  } catch (e) {
178675
179055
  const reason = this.extractErrorReason(e);
@@ -179129,8 +179509,39 @@ var BridgeRegistry = class _BridgeRegistry {
179129
179509
  );
179130
179510
  return [];
179131
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
+ }
179132
179529
  const rooms = [];
179133
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
+ }
179134
179545
  const areaName = this.registry.areas.get(haAreaId) ?? haAreaId;
179135
179546
  rooms.push({
179136
179547
  areaId: hashAreaId(haAreaId),
@@ -179396,12 +179807,13 @@ function hashAreaId(areaId) {
179396
179807
  // src/services/bridges/server-mode-bridge.ts
179397
179808
  init_dist();
179398
179809
  var AUTO_FORCE_SYNC_INTERVAL_MS2 = 9e4;
179810
+ var DEAD_SESSION_TIMEOUT_MS2 = 6e4;
179399
179811
  var ServerModeBridge = class {
179400
- constructor(logger203, dataProvider, endpointManager, server) {
179812
+ constructor(logger204, dataProvider, endpointManager, server) {
179401
179813
  this.dataProvider = dataProvider;
179402
179814
  this.endpointManager = endpointManager;
179403
179815
  this.server = server;
179404
- this.log = logger203.get(`ServerModeBridge / ${dataProvider.id}`);
179816
+ this.log = logger204.get(`ServerModeBridge / ${dataProvider.id}`);
179405
179817
  }
179406
179818
  log;
179407
179819
  status = {
@@ -179412,6 +179824,8 @@ var ServerModeBridge = class {
179412
179824
  // broadcast updates via WebSocket so the frontend sees every transition.
179413
179825
  onStatusChange;
179414
179826
  autoForceSyncTimer = null;
179827
+ deadSessionTimer = null;
179828
+ staleSessionTimers = /* @__PURE__ */ new Map();
179415
179829
  warmStartTimer = null;
179416
179830
  // Tracks the last synced state JSON per entity to avoid pushing unchanged states.
179417
179831
  lastSyncedState;
@@ -179593,6 +180007,33 @@ ${e?.toString()}`);
179593
180007
  this.log.warn(
179594
180008
  `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
179595
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);
179596
180037
  }
179597
180038
  };
179598
180039
  sessionManager.subscriptionsChanged.on(this.sessionDiagHandler);
@@ -179621,6 +180062,62 @@ ${e?.toString()}`);
179621
180062
  } catch {
179622
180063
  }
179623
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
+ }
179624
180121
  unwireSessionDiagnostics() {
179625
180122
  try {
179626
180123
  const sessionManager = this.server.env.get(SessionManager);
@@ -179638,6 +180135,14 @@ ${e?.toString()}`);
179638
180135
  this.sessionDiagHandler = void 0;
179639
180136
  this.sessionAddedHandler = void 0;
179640
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();
179641
180146
  }
179642
180147
  stopAutoForceSync() {
179643
180148
  if (this.autoForceSyncTimer) {
@@ -179753,7 +180258,7 @@ init_service();
179753
180258
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
179754
180259
  init_esm();
179755
180260
  init_home_assistant_entity_behavior();
179756
- import debounce5 from "debounce";
180261
+ import debounce6 from "debounce";
179757
180262
 
179758
180263
  // src/matter/endpoints/legacy/vacuum/server-mode-vacuum-device.ts
179759
180264
  init_home_assistant_entity_behavior();
@@ -179817,7 +180322,7 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
179817
180322
  }
179818
180323
 
179819
180324
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
179820
- var logger201 = Logger.get("ServerModeVacuumEndpoint");
180325
+ var logger202 = Logger.get("ServerModeVacuumEndpoint");
179821
180326
  var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
179822
180327
  static async create(registry2, entityId, mapping) {
179823
180328
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -179827,7 +180332,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179827
180332
  return void 0;
179828
180333
  }
179829
180334
  let effectiveMapping = mapping;
179830
- logger201.info(
180335
+ logger202.info(
179831
180336
  `${entityId}: device_id=${entity.device_id}, manualBattery=${mapping?.batteryEntity ?? "none"}`
179832
180337
  );
179833
180338
  if (entity.device_id) {
@@ -179842,15 +180347,15 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179842
180347
  batteryEntity: batteryEntityId
179843
180348
  };
179844
180349
  registry2.markBatteryEntityUsed(batteryEntityId);
179845
- logger201.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
180350
+ logger202.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
179846
180351
  } else {
179847
180352
  const attrs = state.attributes;
179848
180353
  if (attrs.battery_level != null || attrs.battery != null) {
179849
- logger201.info(
180354
+ logger202.info(
179850
180355
  `${entityId}: No battery entity found, using battery attribute from vacuum state`
179851
180356
  );
179852
180357
  } else {
179853
- logger201.warn(
180358
+ logger202.warn(
179854
180359
  `${entityId}: No battery entity found for device ${entity.device_id}`
179855
180360
  );
179856
180361
  }
@@ -179865,7 +180370,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179865
180370
  entityId: effectiveMapping?.entityId ?? entityId,
179866
180371
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
179867
180372
  };
179868
- logger201.info(
180373
+ logger202.info(
179869
180374
  `${entityId}: Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity}`
179870
180375
  );
179871
180376
  }
@@ -179875,7 +180380,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179875
180380
  entityId: effectiveMapping?.entityId ?? entityId,
179876
180381
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
179877
180382
  };
179878
- logger201.info(
180383
+ logger202.info(
179879
180384
  `${entityId}: Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity}`
179880
180385
  );
179881
180386
  }
@@ -179885,7 +180390,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179885
180390
  entityId: effectiveMapping?.entityId ?? entityId,
179886
180391
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
179887
180392
  };
179888
- logger201.info(
180393
+ logger202.info(
179889
180394
  `${entityId}: Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity}`
179890
180395
  );
179891
180396
  }
@@ -179900,7 +180405,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179900
180405
  entityId: effectiveMapping?.entityId ?? entityId,
179901
180406
  cleanAreaRooms
179902
180407
  };
179903
- logger201.info(
180408
+ logger202.info(
179904
180409
  `${entityId}: Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA`
179905
180410
  );
179906
180411
  }
@@ -179921,7 +180426,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179921
180426
  rooms: roomsObj
179922
180427
  }
179923
180428
  };
179924
- logger201.info(
180429
+ logger202.info(
179925
180430
  `${entityId}: Auto-detected ${valetudoRooms.length} Valetudo segments`
179926
180431
  );
179927
180432
  } else {
@@ -179938,14 +180443,14 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179938
180443
  rooms: roomsObj
179939
180444
  }
179940
180445
  };
179941
- logger201.info(
180446
+ logger202.info(
179942
180447
  `${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
179943
180448
  );
179944
180449
  }
179945
180450
  }
179946
180451
  }
179947
180452
  } else {
179948
- logger201.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
180453
+ logger202.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
179949
180454
  }
179950
180455
  const payload = {
179951
180456
  entity_id: entityId,
@@ -179995,7 +180500,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
179995
180500
  flushUpdate;
179996
180501
  constructor(type, entityId, customName, mappedEntityIds) {
179997
180502
  super(type, entityId, customName, mappedEntityIds);
179998
- this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
180503
+ this.flushUpdate = debounce6(this.flushPendingUpdate.bind(this), 50);
179999
180504
  }
180000
180505
  async delete() {
180001
180506
  this.flushUpdate.clear();
@@ -180009,11 +180514,11 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180009
180514
  }
180010
180515
  if (mappedChanged) {
180011
180516
  this.pendingMappedChange = true;
180012
- logger201.debug(
180517
+ logger202.debug(
180013
180518
  `Mapped entity change detected for ${this.entityId}, forcing update`
180014
180519
  );
180015
180520
  }
180016
- logger201.debug(
180521
+ logger202.debug(
180017
180522
  `State update received for ${this.entityId}: state=${state.state}`
180018
180523
  );
180019
180524
  this.lastState = state;
@@ -180427,10 +180932,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
180427
180932
  // src/core/ioc/app-environment.ts
180428
180933
  var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180429
180934
  constructor(rootEnv, options) {
180430
- const logger203 = rootEnv.get(LoggerService);
180935
+ const logger204 = rootEnv.get(LoggerService);
180431
180936
  super({
180432
180937
  id: "App",
180433
- log: logger203.get("AppContainer"),
180938
+ log: logger204.get("AppContainer"),
180434
180939
  parent: rootEnv
180435
180940
  });
180436
180941
  this.options = options;
@@ -180443,8 +180948,8 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180443
180948
  }
180444
180949
  construction;
180445
180950
  async init() {
180446
- const logger203 = this.get(LoggerService);
180447
- this.set(LoggerService, logger203);
180951
+ const logger204 = this.get(LoggerService);
180952
+ this.set(LoggerService, logger204);
180448
180953
  this.set(AppStorage, new AppStorage(await this.load(StorageService)));
180449
180954
  this.set(BridgeStorage, new BridgeStorage(await this.load(AppStorage)));
180450
180955
  this.set(
@@ -180461,7 +180966,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180461
180966
  );
180462
180967
  this.set(
180463
180968
  HomeAssistantClient,
180464
- new HomeAssistantClient(logger203, this.options.homeAssistant)
180969
+ new HomeAssistantClient(logger204, this.options.homeAssistant)
180465
180970
  );
180466
180971
  this.set(
180467
180972
  HomeAssistantConfig,
@@ -180469,7 +180974,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180469
180974
  );
180470
180975
  this.set(
180471
180976
  HomeAssistantActions,
180472
- new HomeAssistantActions(logger203, await this.load(HomeAssistantClient))
180977
+ new HomeAssistantActions(logger204, await this.load(HomeAssistantClient))
180473
180978
  );
180474
180979
  this.set(
180475
180980
  HomeAssistantRegistry,
@@ -180505,7 +181010,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180505
181010
  this.set(
180506
181011
  WebApi,
180507
181012
  new WebApi(
180508
- logger203,
181013
+ logger204,
180509
181014
  await this.load(BridgeService),
180510
181015
  await this.load(HomeAssistantClient),
180511
181016
  await this.load(HomeAssistantRegistry),
@@ -180531,7 +181036,7 @@ init_nodejs();
180531
181036
  init_level_control();
180532
181037
 
180533
181038
  // src/matter/patches/patch-level-control-tlv.ts
180534
- var logger202 = Logger.get("PatchLevelControlTlv");
181039
+ var logger203 = Logger.get("PatchLevelControlTlv");
180535
181040
  function patchLevelControlTlv() {
180536
181041
  let patched = 0;
180537
181042
  const moveToLevelFields = LevelControl3.TlvMoveToLevelRequest.fieldDefinitions;
@@ -180545,11 +181050,11 @@ function patchLevelControlTlv() {
180545
181050
  patched++;
180546
181051
  }
180547
181052
  if (patched > 0) {
180548
- logger202.info(
181053
+ logger203.info(
180549
181054
  `Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
180550
181055
  );
180551
181056
  } else {
180552
- logger202.warn(
181057
+ logger203.warn(
180553
181058
  "Failed to patch LevelControl TLV schemas \u2014 field definitions not found. Google Home brightness adjustment may not work."
180554
181059
  );
180555
181060
  }
@@ -180587,8 +181092,7 @@ process.on("unhandledRejection", (reason) => {
180587
181092
  if (shouldSuppressError(reason)) {
180588
181093
  return;
180589
181094
  }
180590
- console.error("Unhandled rejection:", reason);
180591
- process.exit(1);
181095
+ console.error("Unhandled rejection (process continuing):", reason);
180592
181096
  });
180593
181097
  function registerFinalErrorHandlers() {
180594
181098
  process.removeAllListeners("uncaughtException");
@@ -180614,8 +181118,7 @@ function registerFinalErrorHandlers() {
180614
181118
  console.warn("Suppressed Matter.js internal error:", reason);
180615
181119
  return;
180616
181120
  }
180617
- console.error("Unhandled rejection:", reason);
180618
- process.exit(1);
181121
+ console.error("Unhandled rejection (process continuing):", reason);
180619
181122
  });
180620
181123
  }
180621
181124
  async function startHandler(startOptions, webUiDist) {