@riddix/hamh 2.1.0-alpha.836 → 2.1.0-alpha.838

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.
@@ -1767,8 +1767,8 @@ var init_Cancelable = __esm({
1767
1767
  };
1768
1768
  return result;
1769
1769
  }
1770
- static set logger(logger247) {
1771
- this.#logger = logger247;
1770
+ static set logger(logger248) {
1771
+ this.#logger = logger248;
1772
1772
  }
1773
1773
  static get logger() {
1774
1774
  return this.#logger;
@@ -3475,13 +3475,13 @@ var init_Logger = __esm({
3475
3475
  *
3476
3476
  * @deprecated use {@link destinations}
3477
3477
  */
3478
- static addLogger(identifier, logger247, options) {
3478
+ static addLogger(identifier, logger248, options) {
3479
3479
  if (identifier in this.destinations) {
3480
3480
  throw new ImplementationError(`Logger "${identifier}" already exists`);
3481
3481
  }
3482
3482
  const dest = LogDestination({ name: identifier });
3483
3483
  const legacy = adaptDestinationToLegacy(dest);
3484
- legacy.log = logger247;
3484
+ legacy.log = logger248;
3485
3485
  if (options?.defaultLogLevel !== void 0) {
3486
3486
  legacy.defaultLogLevel = options.defaultLogLevel;
3487
3487
  }
@@ -5687,8 +5687,8 @@ var init_Construction = __esm({
5687
5687
  }
5688
5688
  }
5689
5689
  #unhandledError(...args) {
5690
- const logger247 = Logger.get(this.#subject.constructor.name);
5691
- logger247.error(...args);
5690
+ const logger248 = Logger.get(this.#subject.constructor.name);
5691
+ logger248.error(...args);
5692
5692
  }
5693
5693
  #createErrorHandler(name) {
5694
5694
  return (e) => {
@@ -64070,8 +64070,8 @@ var init_NodeSession = __esm({
64070
64070
  return session?.type === SessionType.Unicast;
64071
64071
  }
64072
64072
  NodeSession2.is = is;
64073
- function logNew(logger247, operation, session, messenger, fabric, peerNodeId) {
64074
- logger247.info(
64073
+ function logNew(logger248, operation, session, messenger, fabric, peerNodeId) {
64074
+ logger248.info(
64075
64075
  session.via,
64076
64076
  `${operation} session with`,
64077
64077
  Diagnostic.strong(PeerAddress({ fabricIndex: fabric.fabricIndex, nodeId: peerNodeId }).toString()),
@@ -117714,11 +117714,11 @@ var init_Api = __esm({
117714
117714
  }
117715
117715
  Api2.resourceFor = resourceFor;
117716
117716
  function log(level, facility, id, ...message) {
117717
- let logger247 = loggers.get(facility);
117718
- if (!logger247) {
117719
- loggers.set(facility, logger247 = Logger.get(facility));
117717
+ let logger248 = loggers.get(facility);
117718
+ if (!logger248) {
117719
+ loggers.set(facility, logger248 = Logger.get(facility));
117720
117720
  }
117721
- logger247[level](Diagnostic.via(id || "(anon)"), message);
117721
+ logger248[level](Diagnostic.via(id || "(anon)"), message);
117722
117722
  }
117723
117723
  Api2.log = log;
117724
117724
  function logRequest(facility, id, method, target) {
@@ -131221,6 +131221,13 @@ var init_controller_compat = __esm({
131221
131221
  aqara: "yes",
131222
131222
  note: "Aqara lists battery storage; others show battery inside a device."
131223
131223
  },
131224
+ 1292: {
131225
+ apple: "no",
131226
+ google: "no",
131227
+ alexa: "no",
131228
+ aqara: "yes",
131229
+ note: "HA and Aqara render EnergyEvse; SmartThings announced support, unconfirmed. Bridged EVSE can break Alexa device recognition, keep it off Alexa bridges."
131230
+ },
131224
131231
  39: {
131225
131232
  apple: "no",
131226
131233
  google: "no",
@@ -132624,10 +132631,10 @@ var init_home_assistant_actions = __esm({
132624
132631
  circuitBreakerResetMs: 3e4
132625
132632
  };
132626
132633
  HomeAssistantActions = class extends Service {
132627
- constructor(logger247, client, config11) {
132634
+ constructor(logger248, client, config11) {
132628
132635
  super("HomeAssistantActions");
132629
132636
  this.client = client;
132630
- this.log = logger247.get(this);
132637
+ this.log = logger248.get(this);
132631
132638
  this.config = { ...defaultConfig, ...config11 };
132632
132639
  this.circuitBreaker = new CircuitBreaker(
132633
132640
  this.config.circuitBreakerThreshold,
@@ -132973,10 +132980,10 @@ var DiagnosticService = class {
132973
132980
  };
132974
132981
 
132975
132982
  // src/api/access-log.ts
132976
- function accessLogger(logger247) {
132983
+ function accessLogger(logger248) {
132977
132984
  return (req, res, next) => {
132978
132985
  res.on("finish", () => {
132979
- logger247.debug(
132986
+ logger248.debug(
132980
132987
  `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}`
132981
132988
  );
132982
132989
  });
@@ -133161,6 +133168,8 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
133161
133168
  currentEntity: config11.currentEntity,
133162
133169
  batteryPowerEntity: config11.batteryPowerEntity,
133163
133170
  batteryEnergyEntity: config11.batteryEnergyEntity,
133171
+ chargingSwitchEntity: config11.chargingSwitchEntity,
133172
+ currentLimitEntity: config11.currentLimitEntity,
133164
133173
  suctionLevelEntity: config11.suctionLevelEntity,
133165
133174
  mopIntensityEntity: config11.mopIntensityEntity,
133166
133175
  valetudoIdentifier: config11.valetudoIdentifier,
@@ -133322,6 +133331,8 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
133322
133331
  currentEntity: config11.currentEntity,
133323
133332
  batteryPowerEntity: config11.batteryPowerEntity,
133324
133333
  batteryEnergyEntity: config11.batteryEnergyEntity,
133334
+ chargingSwitchEntity: config11.chargingSwitchEntity,
133335
+ currentLimitEntity: config11.currentLimitEntity,
133325
133336
  suctionLevelEntity: config11.suctionLevelEntity,
133326
133337
  mopIntensityEntity: config11.mopIntensityEntity,
133327
133338
  temperatureEntity: config11.temperatureEntity,
@@ -134174,6 +134185,8 @@ function entityMappingApi(mappingStorage, identityStorage) {
134174
134185
  currentEntity: body.currentEntity,
134175
134186
  batteryPowerEntity: body.batteryPowerEntity,
134176
134187
  batteryEnergyEntity: body.batteryEnergyEntity,
134188
+ chargingSwitchEntity: body.chargingSwitchEntity,
134189
+ currentLimitEntity: body.currentLimitEntity,
134177
134190
  suctionLevelEntity: body.suctionLevelEntity,
134178
134191
  mopIntensityEntity: body.mopIntensityEntity,
134179
134192
  customServiceAreas: body.customServiceAreas,
@@ -134675,6 +134688,8 @@ function configToProfileEntry(config11) {
134675
134688
  currentEntity: config11.currentEntity,
134676
134689
  batteryPowerEntity: config11.batteryPowerEntity,
134677
134690
  batteryEnergyEntity: config11.batteryEnergyEntity,
134691
+ chargingSwitchEntity: config11.chargingSwitchEntity,
134692
+ currentLimitEntity: config11.currentLimitEntity,
134678
134693
  suctionLevelEntity: config11.suctionLevelEntity,
134679
134694
  mopIntensityEntity: config11.mopIntensityEntity,
134680
134695
  customServiceAreas: config11.customServiceAreas,
@@ -134814,6 +134829,8 @@ function mappingProfileApi(mappingStorage) {
134814
134829
  currentEntity: entry.currentEntity,
134815
134830
  batteryPowerEntity: entry.batteryPowerEntity,
134816
134831
  batteryEnergyEntity: entry.batteryEnergyEntity,
134832
+ chargingSwitchEntity: entry.chargingSwitchEntity,
134833
+ currentLimitEntity: entry.currentLimitEntity,
134817
134834
  suctionLevelEntity: entry.suctionLevelEntity,
134818
134835
  mopIntensityEntity: entry.mopIntensityEntity,
134819
134836
  customServiceAreas: entry.customServiceAreas,
@@ -135040,6 +135057,300 @@ function resolveLabelValue(value, labels) {
135040
135057
  return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
135041
135058
  }
135042
135059
 
135060
+ // src/matter/endpoints/entity-endpoint.ts
135061
+ init_esm7();
135062
+ var EntityEndpoint = class extends Endpoint {
135063
+ constructor(type, entityId, customName, mappedEntityIds, endpointId) {
135064
+ super(type, { id: endpointId ?? createEndpointId(entityId, customName) });
135065
+ this.entityId = entityId;
135066
+ this.mappedEntityIds = mappedEntityIds ?? [];
135067
+ }
135068
+ entityId;
135069
+ mappedEntityIds;
135070
+ lastMappedStates = {};
135071
+ hasMappedEntityChanged(states) {
135072
+ let changed = false;
135073
+ for (const mappedId of this.mappedEntityIds) {
135074
+ const mappedState = states[mappedId];
135075
+ if (!mappedState) continue;
135076
+ const fp = mappedState.state;
135077
+ if (fp !== this.lastMappedStates[mappedId]) {
135078
+ this.lastMappedStates[mappedId] = fp;
135079
+ changed = true;
135080
+ }
135081
+ }
135082
+ return changed;
135083
+ }
135084
+ };
135085
+ function createEndpointId(entityId, customName) {
135086
+ const baseName = customName || entityId;
135087
+ return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
135088
+ }
135089
+ function getMappedEntityIds(mapping) {
135090
+ if (!mapping) return [];
135091
+ const ids = [];
135092
+ if (mapping.batteryEntity && !mapping.disableBatteryMapping) {
135093
+ ids.push(mapping.batteryEntity);
135094
+ }
135095
+ if (mapping.faultEntity) ids.push(mapping.faultEntity);
135096
+ if (mapping.chargingStateEntity) ids.push(mapping.chargingStateEntity);
135097
+ if (mapping.temperatureEntity) ids.push(mapping.temperatureEntity);
135098
+ if (mapping.humidityEntity) ids.push(mapping.humidityEntity);
135099
+ if (mapping.pressureEntity) ids.push(mapping.pressureEntity);
135100
+ if (mapping.cleaningModeEntity) ids.push(mapping.cleaningModeEntity);
135101
+ if (mapping.suctionLevelEntity) ids.push(mapping.suctionLevelEntity);
135102
+ if (mapping.mopIntensityEntity) ids.push(mapping.mopIntensityEntity);
135103
+ if (mapping.filterLifeEntity) ids.push(mapping.filterLifeEntity);
135104
+ if (mapping.powerEntity) ids.push(mapping.powerEntity);
135105
+ if (mapping.energyEntity) ids.push(mapping.energyEntity);
135106
+ if (mapping.voltageEntity) ids.push(mapping.voltageEntity);
135107
+ if (mapping.currentEntity) ids.push(mapping.currentEntity);
135108
+ if (mapping.batteryPowerEntity) ids.push(mapping.batteryPowerEntity);
135109
+ if (mapping.batteryEnergyEntity) ids.push(mapping.batteryEnergyEntity);
135110
+ if (mapping.chargingSwitchEntity) ids.push(mapping.chargingSwitchEntity);
135111
+ if (mapping.currentLimitEntity) ids.push(mapping.currentLimitEntity);
135112
+ if (mapping.currentRoomEntity) ids.push(mapping.currentRoomEntity);
135113
+ if (mapping.cleanedAreaEntity) ids.push(mapping.cleanedAreaEntity);
135114
+ if (mapping.composedEntities) {
135115
+ for (const sub of mapping.composedEntities) {
135116
+ if (sub.entityId) ids.push(sub.entityId);
135117
+ }
135118
+ }
135119
+ return ids;
135120
+ }
135121
+
135122
+ // src/services/bridges/identity-resolver.ts
135123
+ var SEP = "\0";
135124
+ function identityKey(entity) {
135125
+ const uniqueId = entity.registry?.unique_id;
135126
+ const platform = entity.registry?.platform;
135127
+ if (!uniqueId || !platform) {
135128
+ return null;
135129
+ }
135130
+ const domain = entity.entity_id.split(".")[0];
135131
+ return platform + SEP + domain + SEP + uniqueId;
135132
+ }
135133
+ var IdentityResolver = class {
135134
+ constructor(identityStorage, mappingStorage) {
135135
+ this.identityStorage = identityStorage;
135136
+ this.mappingStorage = mappingStorage;
135137
+ }
135138
+ identityStorage;
135139
+ mappingStorage;
135140
+ async resolveIdentity(bridgeId, entity, mapping, opts) {
135141
+ const entityId = entity.entity_id;
135142
+ const key = identityKey(entity);
135143
+ if (key == null) {
135144
+ return {
135145
+ endpointId: createEndpointId(entityId, mapping?.customName),
135146
+ anchorEntityId: entityId,
135147
+ protected: false
135148
+ };
135149
+ }
135150
+ const existing = this.identityStorage.getIdentity(bridgeId, key);
135151
+ if (existing) {
135152
+ const renamedFrom = existing.lastEntityId !== entityId ? existing.lastEntityId ?? existing.anchorEntityId : void 0;
135153
+ if (opts.stableIdentity) {
135154
+ if (renamedFrom) {
135155
+ this.identityStorage.setIdentity(bridgeId, key, {
135156
+ ...existing,
135157
+ lastEntityId: entityId
135158
+ });
135159
+ await this.rekeyMapping(bridgeId, renamedFrom, entityId);
135160
+ }
135161
+ return {
135162
+ endpointId: existing.endpointId,
135163
+ anchorEntityId: existing.anchorEntityId,
135164
+ protected: true,
135165
+ renamedFrom
135166
+ };
135167
+ }
135168
+ const liveEndpointId = createEndpointId(entityId, mapping?.customName);
135169
+ if (existing.endpointId !== liveEndpointId || existing.anchorEntityId !== entityId || existing.lastEntityId !== entityId) {
135170
+ this.identityStorage.setIdentity(bridgeId, key, {
135171
+ ...existing,
135172
+ endpointId: liveEndpointId,
135173
+ anchorEntityId: entityId,
135174
+ lastEntityId: entityId
135175
+ });
135176
+ }
135177
+ return {
135178
+ endpointId: liveEndpointId,
135179
+ anchorEntityId: entityId,
135180
+ protected: false,
135181
+ renamedFrom
135182
+ };
135183
+ }
135184
+ const desired = createEndpointId(entityId, mapping?.customName);
135185
+ let endpointId = desired;
135186
+ if (opts.stableIdentity) {
135187
+ let n = 2;
135188
+ while (opts.isEndpointIdTaken(endpointId, key)) {
135189
+ endpointId = `${desired}_${n++}`;
135190
+ }
135191
+ }
135192
+ const record = {
135193
+ endpointId,
135194
+ anchorEntityId: entityId,
135195
+ lastEntityId: entityId,
135196
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
135197
+ };
135198
+ this.identityStorage.setIdentity(bridgeId, key, record);
135199
+ if (opts.stableIdentity) {
135200
+ return {
135201
+ endpointId: record.endpointId,
135202
+ anchorEntityId: record.anchorEntityId,
135203
+ protected: true
135204
+ };
135205
+ }
135206
+ return {
135207
+ endpointId: desired,
135208
+ anchorEntityId: entityId,
135209
+ protected: false
135210
+ };
135211
+ }
135212
+ // Carry a mapping from the old entity_id to the new one on rename. anchorEntityId
135213
+ // never changes, only the mapping key. Skips when no old mapping exists or a new
135214
+ // one already does, so a manual mapping under the new id is never clobbered.
135215
+ async rekeyMapping(bridgeId, oldEntityId, newEntityId) {
135216
+ if (oldEntityId === newEntityId) {
135217
+ return;
135218
+ }
135219
+ const old = this.mappingStorage.getMapping(bridgeId, oldEntityId);
135220
+ if (!old) {
135221
+ return;
135222
+ }
135223
+ if (this.mappingStorage.getMapping(bridgeId, newEntityId)) {
135224
+ return;
135225
+ }
135226
+ await this.mappingStorage.setMapping({
135227
+ ...old,
135228
+ bridgeId,
135229
+ entityId: newEntityId
135230
+ });
135231
+ await this.mappingStorage.deleteMapping(bridgeId, oldEntityId);
135232
+ }
135233
+ };
135234
+
135235
+ // src/services/storage/orphan-cleanup.ts
135236
+ var ORPHAN_TOMBSTONE_MS = 7 * 24 * 60 * 60 * 1e3;
135237
+ function buildPresentIdentityKeys(entities) {
135238
+ const keys3 = /* @__PURE__ */ new Set();
135239
+ for (const entity of Object.values(entities ?? {})) {
135240
+ if (!entity?.entity_id) continue;
135241
+ const key = identityKey({ entity_id: entity.entity_id, registry: entity });
135242
+ if (key != null) keys3.add(key);
135243
+ }
135244
+ return keys3;
135245
+ }
135246
+ function buildPresentEntityIds(entities) {
135247
+ const ids = /* @__PURE__ */ new Set();
135248
+ for (const entity of Object.values(entities ?? {})) {
135249
+ if (entity?.entity_id) ids.add(entity.entity_id);
135250
+ }
135251
+ return ids;
135252
+ }
135253
+ function stampIdentityPresence(store, bridgeId, presentKeys, now = Date.now()) {
135254
+ const nowIso = new Date(now).toISOString();
135255
+ for (const key of [...store.getBridgeIdentities(bridgeId).keys()]) {
135256
+ if (presentKeys.has(key)) {
135257
+ store.clearIdentityMissing(bridgeId, key);
135258
+ } else {
135259
+ store.markIdentityMissing(bridgeId, key, nowIso);
135260
+ }
135261
+ }
135262
+ }
135263
+ function stampMappingPresence(store, bridgeId, presentEntityIds, now = Date.now()) {
135264
+ const nowIso = new Date(now).toISOString();
135265
+ const entityIds = store.getMappingsForBridge(bridgeId).map((m) => m.entityId);
135266
+ for (const entityId of entityIds) {
135267
+ if (presentEntityIds.has(entityId)) {
135268
+ store.clearMappingMissing(bridgeId, entityId);
135269
+ } else {
135270
+ store.markMappingMissing(bridgeId, entityId, nowIso);
135271
+ }
135272
+ }
135273
+ }
135274
+ function computeOrphanCandidates(input) {
135275
+ const now = input.now ?? Date.now();
135276
+ const candidates = [];
135277
+ for (const [key, record] of input.identities) {
135278
+ if (!record.missingSince) continue;
135279
+ const missingFor = now - Date.parse(record.missingSince);
135280
+ if (!(missingFor >= ORPHAN_TOMBSTONE_MS)) continue;
135281
+ if (input.presentKeys.has(key)) continue;
135282
+ const lastEntityId = record.lastEntityId ?? record.anchorEntityId;
135283
+ candidates.push({
135284
+ identityKey: key,
135285
+ lastEntityId,
135286
+ missingSince: record.missingSince,
135287
+ // Only a true orphan mapping, not one a live entity holds under a reused
135288
+ // entity_id (a different identity key). Guards the sweep from clobbering
135289
+ // that live entity's custom mapping.
135290
+ hasMapping: input.hasMapping(lastEntityId) && !input.presentEntityIds.has(lastEntityId),
135291
+ kind: "identity"
135292
+ });
135293
+ }
135294
+ if (input.mappings) {
135295
+ const linkedByIdentity = /* @__PURE__ */ new Set();
135296
+ for (const candidate of candidates) {
135297
+ if (candidate.hasMapping) linkedByIdentity.add(candidate.lastEntityId);
135298
+ }
135299
+ for (const mapping of input.mappings) {
135300
+ if (!mapping.missingSince) continue;
135301
+ const missingFor = now - Date.parse(mapping.missingSince);
135302
+ if (!(missingFor >= ORPHAN_TOMBSTONE_MS)) continue;
135303
+ if (input.presentEntityIds.has(mapping.entityId)) continue;
135304
+ if (linkedByIdentity.has(mapping.entityId)) continue;
135305
+ candidates.push({
135306
+ identityKey: mapping.entityId,
135307
+ lastEntityId: mapping.entityId,
135308
+ missingSince: mapping.missingSince,
135309
+ hasMapping: true,
135310
+ kind: "mapping"
135311
+ });
135312
+ }
135313
+ }
135314
+ return candidates;
135315
+ }
135316
+ async function executeOrphanCleanup(input) {
135317
+ const candidates = computeOrphanCandidates({
135318
+ bridgeId: input.bridgeId,
135319
+ identities: input.identities,
135320
+ presentKeys: input.presentKeys,
135321
+ presentEntityIds: input.presentEntityIds,
135322
+ hasMapping: (entityId) => input.getMapping(input.bridgeId, entityId) != null,
135323
+ mappings: input.mappings,
135324
+ now: input.now
135325
+ });
135326
+ const byKey = new Map(candidates.map((c) => [c.identityKey, c]));
135327
+ const results = [];
135328
+ for (const key of input.requestedKeys) {
135329
+ const candidate = byKey.get(key);
135330
+ if (!candidate) {
135331
+ results.push({
135332
+ identityKey: key,
135333
+ deleted: false,
135334
+ reason: "not a current orphan candidate"
135335
+ });
135336
+ continue;
135337
+ }
135338
+ if (candidate.kind === "mapping") {
135339
+ if (!input.presentEntityIds.has(candidate.lastEntityId)) {
135340
+ await input.deleteMapping(input.bridgeId, candidate.lastEntityId);
135341
+ }
135342
+ results.push({ identityKey: key, deleted: true });
135343
+ continue;
135344
+ }
135345
+ if (candidate.hasMapping && !input.presentEntityIds.has(candidate.lastEntityId)) {
135346
+ await input.deleteMapping(input.bridgeId, candidate.lastEntityId);
135347
+ }
135348
+ await input.deleteIdentity(input.bridgeId, key);
135349
+ results.push({ identityKey: key, deleted: true });
135350
+ }
135351
+ return results;
135352
+ }
135353
+
135043
135354
  // src/utils/json/endpoint-to-json.ts
135044
135355
  function safeClone(value, depth = 0) {
135045
135356
  if (depth > 20 || value === null || value === void 0) return value;
@@ -135085,7 +135396,7 @@ function endpointToJson(endpoint, parentId) {
135085
135396
 
135086
135397
  // src/api/matter-api.ts
135087
135398
  var ajv = new Ajv();
135088
- function matterApi(bridgeService, haRegistry, identityStorage) {
135399
+ function matterApi(bridgeService, haRegistry, identityStorage, mappingStorage) {
135089
135400
  const router = express11.Router();
135090
135401
  router.get("/", (_, res) => {
135091
135402
  res.status(200).json({});
@@ -135188,6 +135499,64 @@ function matterApi(bridgeService, haRegistry, identityStorage) {
135188
135499
  });
135189
135500
  }
135190
135501
  });
135502
+ router.get("/bridges/:bridgeId/orphans", async (req, res) => {
135503
+ const bridgeId = req.params.bridgeId;
135504
+ const bridge = bridgeService.bridges.find((b) => b.id === bridgeId);
135505
+ if (!bridge) {
135506
+ res.status(404).send("Not Found");
135507
+ return;
135508
+ }
135509
+ if (!haRegistry || !identityStorage) {
135510
+ res.status(503).json({ error: "Orphan cleanup is not available" });
135511
+ return;
135512
+ }
135513
+ const presentKeys = buildPresentIdentityKeys(haRegistry.entities);
135514
+ const presentEntityIds = buildPresentEntityIds(haRegistry.entities);
135515
+ const candidates = computeOrphanCandidates({
135516
+ bridgeId,
135517
+ identities: identityStorage.getBridgeIdentities(bridgeId),
135518
+ presentKeys,
135519
+ presentEntityIds,
135520
+ hasMapping: (entityId) => mappingStorage?.getMapping(bridgeId, entityId) != null,
135521
+ mappings: mappingStorage?.getMappingsForBridge(bridgeId)
135522
+ });
135523
+ res.status(200).json({ candidates });
135524
+ });
135525
+ router.post(
135526
+ "/bridges/:bridgeId/actions/cleanup-orphans",
135527
+ async (req, res) => {
135528
+ const bridgeId = req.params.bridgeId;
135529
+ const bridge = bridgeService.bridges.find((b) => b.id === bridgeId);
135530
+ if (!bridge) {
135531
+ res.status(404).send("Not Found");
135532
+ return;
135533
+ }
135534
+ if (!haRegistry || !identityStorage || !mappingStorage) {
135535
+ res.status(503).json({ error: "Orphan cleanup is not available" });
135536
+ return;
135537
+ }
135538
+ const body = req.body;
135539
+ const requestedKeys = Array.isArray(body?.identityKeys) ? body.identityKeys.filter((k) => typeof k === "string") : [];
135540
+ try {
135541
+ const results = await executeOrphanCleanup({
135542
+ bridgeId,
135543
+ requestedKeys,
135544
+ identities: identityStorage.getBridgeIdentities(bridgeId),
135545
+ presentKeys: buildPresentIdentityKeys(haRegistry.entities),
135546
+ presentEntityIds: buildPresentEntityIds(haRegistry.entities),
135547
+ getMapping: (b, e) => mappingStorage.getMapping(b, e),
135548
+ deleteMapping: (b, e) => mappingStorage.deleteMapping(b, e),
135549
+ deleteIdentity: (b, k) => identityStorage.deleteIdentity(b, k),
135550
+ mappings: mappingStorage.getMappingsForBridge(bridgeId)
135551
+ });
135552
+ res.status(200).json({ results });
135553
+ } catch (e) {
135554
+ res.status(500).json({
135555
+ error: e instanceof Error ? e.message : "Unknown error"
135556
+ });
135557
+ }
135558
+ }
135559
+ );
135191
135560
  router.get("/bridges/:bridgeId/devices", async (req, res) => {
135192
135561
  const bridgeId = req.params.bridgeId;
135193
135562
  const bridge = bridgeService.bridges.find((b) => b.id === bridgeId);
@@ -137161,7 +137530,7 @@ var WebSocketApi = class {
137161
137530
 
137162
137531
  // src/api/web-api.ts
137163
137532
  var WebApi = class extends Service {
137164
- constructor(logger247, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, identityStorage, lockCredentialStorage, settingsStorage, backupService, props) {
137533
+ constructor(logger248, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, identityStorage, lockCredentialStorage, settingsStorage, backupService, props) {
137165
137534
  super("WebApi");
137166
137535
  this.bridgeService = bridgeService;
137167
137536
  this.haClient = haClient;
@@ -137173,8 +137542,8 @@ var WebApi = class extends Service {
137173
137542
  this.settingsStorage = settingsStorage;
137174
137543
  this.backupService = backupService;
137175
137544
  this.props = props;
137176
- this.logger = logger247;
137177
- this.log = logger247.get(this);
137545
+ this.logger = logger248;
137546
+ this.log = logger248.get(this);
137178
137547
  this.accessLogger = accessLogger(this.log.createChild("Access Log"));
137179
137548
  this.startTime = Date.now();
137180
137549
  this.wsApi = new WebSocketApi(
@@ -137207,7 +137576,12 @@ var WebApi = class extends Service {
137207
137576
  const api = express17.Router();
137208
137577
  api.use(express17.json()).use(nocache()).use(
137209
137578
  "/matter",
137210
- matterApi(this.bridgeService, this.haRegistry, this.identityStorage)
137579
+ matterApi(
137580
+ this.bridgeService,
137581
+ this.haRegistry,
137582
+ this.identityStorage,
137583
+ this.mappingStorage
137584
+ )
137211
137585
  ).use(
137212
137586
  "/health",
137213
137587
  healthApi(
@@ -138360,10 +138734,10 @@ function isTransientConnectError(reason) {
138360
138734
  return msg.includes("socket hang up") || msg.includes("tls") || msg.includes("TLS");
138361
138735
  }
138362
138736
  var HomeAssistantClient = class extends Service {
138363
- constructor(logger247, options) {
138737
+ constructor(logger248, options) {
138364
138738
  super("HomeAssistantClient");
138365
138739
  this.options = options;
138366
- this.log = logger247.get(this);
138740
+ this.log = logger248.get(this);
138367
138741
  }
138368
138742
  options;
138369
138743
  static Options = /* @__PURE__ */ Symbol.for("HomeAssistantClientProps");
@@ -139028,11 +139402,37 @@ var EntityIdentityStorage = class extends Service {
139028
139402
  this.identities.delete(bridgeId);
139029
139403
  await this.flush();
139030
139404
  }
139405
+ // Drop a single identity record. Used by the manual orphan cleanup after the
139406
+ // 7-day tombstone; flush immediately since it is a user-initiated deletion.
139407
+ async deleteIdentity(bridgeId, key) {
139408
+ const bridgeMap = this.identities.get(bridgeId);
139409
+ if (bridgeMap?.delete(key)) {
139410
+ await this.flush();
139411
+ }
139412
+ }
139413
+ // Stamp the tombstone the first time a record's entity is absent from HA. Keeps
139414
+ // the first-seen time on later passes and no-ops once stamped, so a steady
139415
+ // absence does not churn the debounced persist.
139416
+ markIdentityMissing(bridgeId, key, nowIso) {
139417
+ const record = this.identities.get(bridgeId)?.get(key);
139418
+ if (!record || record.missingSince != null) return;
139419
+ record.missingSince = nowIso;
139420
+ this.schedulePersist();
139421
+ }
139422
+ // Clear the tombstone when the entity is present again. No-ops when there is
139423
+ // nothing to clear, so an all-present bridge writes nothing on refresh.
139424
+ clearIdentityMissing(bridgeId, key) {
139425
+ const record = this.identities.get(bridgeId)?.get(key);
139426
+ if (!record || record.missingSince == null) return;
139427
+ delete record.missingSince;
139428
+ this.schedulePersist();
139429
+ }
139031
139430
  };
139032
139431
 
139033
139432
  // src/services/storage/entity-mapping-storage.ts
139034
139433
  init_service();
139035
139434
  var CURRENT_VERSION2 = 1;
139435
+ var PERSIST_DEBOUNCE_MS2 = 500;
139036
139436
  var EntityMappingStorage = class extends Service {
139037
139437
  constructor(appStorage) {
139038
139438
  super("EntityMappingStorage");
@@ -139041,10 +139441,14 @@ var EntityMappingStorage = class extends Service {
139041
139441
  appStorage;
139042
139442
  storage;
139043
139443
  mappings = /* @__PURE__ */ new Map();
139444
+ persistTimer = null;
139044
139445
  async initialize() {
139045
139446
  this.storage = this.appStorage.createContext("entity-mappings");
139046
139447
  await this.load();
139047
139448
  }
139449
+ async dispose() {
139450
+ await this.flush();
139451
+ }
139048
139452
  async load() {
139049
139453
  const stored = await this.storage.get("data", {
139050
139454
  version: CURRENT_VERSION2,
@@ -139079,6 +139483,10 @@ var EntityMappingStorage = class extends Service {
139079
139483
  }
139080
139484
  }
139081
139485
  async persist() {
139486
+ if (this.persistTimer) {
139487
+ clearTimeout(this.persistTimer);
139488
+ this.persistTimer = null;
139489
+ }
139082
139490
  const data = {
139083
139491
  version: CURRENT_VERSION2,
139084
139492
  mappings: {}
@@ -139088,6 +139496,17 @@ var EntityMappingStorage = class extends Service {
139088
139496
  }
139089
139497
  await this.storage.set("data", data);
139090
139498
  }
139499
+ schedulePersist() {
139500
+ if (this.persistTimer) return;
139501
+ this.persistTimer = setTimeout(() => {
139502
+ this.persistTimer = null;
139503
+ void this.persist();
139504
+ }, PERSIST_DEBOUNCE_MS2);
139505
+ }
139506
+ // Flush any pending debounced write, used on dispose and by tests.
139507
+ async flush() {
139508
+ await this.persist();
139509
+ }
139091
139510
  getMappingsForBridge(bridgeId) {
139092
139511
  const bridgeMap = this.mappings.get(bridgeId);
139093
139512
  return bridgeMap ? Array.from(bridgeMap.values()) : [];
@@ -139134,6 +139553,8 @@ var EntityMappingStorage = class extends Service {
139134
139553
  currentEntity: request.currentEntity?.trim() || void 0,
139135
139554
  batteryPowerEntity: request.batteryPowerEntity?.trim() || void 0,
139136
139555
  batteryEnergyEntity: request.batteryEnergyEntity?.trim() || void 0,
139556
+ chargingSwitchEntity: request.chargingSwitchEntity?.trim() || void 0,
139557
+ currentLimitEntity: request.currentLimitEntity?.trim() || void 0,
139137
139558
  pressureEntity: request.pressureEntity?.trim() || void 0,
139138
139559
  suctionLevelEntity: request.suctionLevelEntity?.trim() || void 0,
139139
139560
  mopIntensityEntity: request.mopIntensityEntity?.trim() || void 0,
@@ -139162,7 +139583,7 @@ var EntityMappingStorage = class extends Service {
139162
139583
  composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0,
139163
139584
  disableMomentaryFlip: request.disableMomentaryFlip || void 0
139164
139585
  };
139165
- if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.disableBatteryMapping && !config11.chargingStateEntity && !config11.roomEntities && !config11.disableLockPin && !config11.lockUsercodeService && config11.lockUsercodeSlot === void 0 && config11.lockPinMinLength === void 0 && config11.lockPinMaxLength === void 0 && !config11.powerEntity && !config11.energyEntity && !config11.voltageEntity && !config11.currentEntity && !config11.batteryPowerEntity && !config11.batteryEnergyEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && (!config11.fanWindPresets || (config11.fanWindPresets.natural?.length ?? 0) === 0 && (config11.fanWindPresets.sleep?.length ?? 0) === 0) && !config11.fanRestoreSpeedOnPowerOn && !config11.currentRoomEntity && !config11.cleanedAreaEntity && !config11.disableCustomAreaRoomModes && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverExposeAsDimmableLight && !config11.selectExposeAsSwitch && !config11.selectSwitchOnOption && !config11.selectSwitchOffOption && !config11.coverSliderDebounceMs && !config11.updateThrottleMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0) && !config11.disableMomentaryFlip) {
139586
+ if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.disableBatteryMapping && !config11.chargingStateEntity && !config11.roomEntities && !config11.disableLockPin && !config11.lockUsercodeService && config11.lockUsercodeSlot === void 0 && config11.lockPinMinLength === void 0 && config11.lockPinMaxLength === void 0 && !config11.powerEntity && !config11.energyEntity && !config11.voltageEntity && !config11.currentEntity && !config11.batteryPowerEntity && !config11.batteryEnergyEntity && !config11.chargingSwitchEntity && !config11.currentLimitEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && (!config11.fanWindPresets || (config11.fanWindPresets.natural?.length ?? 0) === 0 && (config11.fanWindPresets.sleep?.length ?? 0) === 0) && !config11.fanRestoreSpeedOnPowerOn && !config11.currentRoomEntity && !config11.cleanedAreaEntity && !config11.disableCustomAreaRoomModes && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverExposeAsDimmableLight && !config11.selectExposeAsSwitch && !config11.selectSwitchOnOption && !config11.selectSwitchOffOption && !config11.coverSliderDebounceMs && !config11.updateThrottleMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0) && !config11.disableMomentaryFlip) {
139166
139587
  bridgeMap.delete(request.entityId);
139167
139588
  } else {
139168
139589
  bridgeMap.set(request.entityId, config11);
@@ -139181,6 +139602,23 @@ var EntityMappingStorage = class extends Service {
139181
139602
  this.mappings.delete(bridgeId);
139182
139603
  await this.persist();
139183
139604
  }
139605
+ // Stamp the tombstone the first time a mapping's entity is absent from HA.
139606
+ // Keeps the first-seen time on later passes and no-ops once stamped, so a
139607
+ // steady absence does not churn the debounced persist.
139608
+ markMappingMissing(bridgeId, entityId, nowIso) {
139609
+ const record = this.mappings.get(bridgeId)?.get(entityId);
139610
+ if (!record || record.missingSince != null) return;
139611
+ record.missingSince = nowIso;
139612
+ this.schedulePersist();
139613
+ }
139614
+ // Clear the tombstone when the entity is present again. No-ops when there is
139615
+ // nothing to clear, so an all-present bridge writes nothing on refresh.
139616
+ clearMappingMissing(bridgeId, entityId) {
139617
+ const record = this.mappings.get(bridgeId)?.get(entityId);
139618
+ if (!record || record.missingSince == null) return;
139619
+ delete record.missingSince;
139620
+ this.schedulePersist();
139621
+ }
139184
139622
  };
139185
139623
  function sanitizeVendorId(value) {
139186
139624
  if (value === void 0 || value === null || value === "") {
@@ -149106,6 +149544,7 @@ var EnergyEvseDeviceDefinition = MutableEndpoint({
149106
149544
  )
149107
149545
  });
149108
149546
  Object.freeze(EnergyEvseDeviceDefinition);
149547
+ var EnergyEvseDevice = EnergyEvseDeviceDefinition;
149109
149548
 
149110
149549
  // ../../node_modules/.pnpm/@matter+node@0.17.7_patch_hash=34f8224b2964673272e9f22613c0cb1f8ec12758dd46b4fe303d42373451ad4c/node_modules/@matter/node/dist/esm/devices/extended-color-light.js
149111
149550
  init_IdentifyServer();
@@ -156832,11 +157271,11 @@ var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
156832
157271
  var SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS = 2500;
156833
157272
  var MDNS_ADDRESS_CHECK_INTERVAL_MS = 6e4;
156834
157273
  var Bridge = class {
156835
- constructor(env, logger247, dataProvider, endpointManager, serverOptions) {
157274
+ constructor(env, logger248, dataProvider, endpointManager, serverOptions) {
156836
157275
  this.dataProvider = dataProvider;
156837
157276
  this.endpointManager = endpointManager;
156838
157277
  this.serverOptions = serverOptions;
156839
- this.log = logger247.get(`Bridge / ${dataProvider.id}`);
157278
+ this.log = logger248.get(`Bridge / ${dataProvider.id}`);
156840
157279
  this.server = new BridgeServerNode(
156841
157280
  env,
156842
157281
  this.dataProvider,
@@ -157888,66 +158327,6 @@ var AggregatorEndpoint2 = class extends Endpoint {
157888
158327
  }
157889
158328
  };
157890
158329
 
157891
- // src/matter/endpoints/entity-endpoint.ts
157892
- init_esm7();
157893
- var EntityEndpoint = class extends Endpoint {
157894
- constructor(type, entityId, customName, mappedEntityIds, endpointId) {
157895
- super(type, { id: endpointId ?? createEndpointId(entityId, customName) });
157896
- this.entityId = entityId;
157897
- this.mappedEntityIds = mappedEntityIds ?? [];
157898
- }
157899
- entityId;
157900
- mappedEntityIds;
157901
- lastMappedStates = {};
157902
- hasMappedEntityChanged(states) {
157903
- let changed = false;
157904
- for (const mappedId of this.mappedEntityIds) {
157905
- const mappedState = states[mappedId];
157906
- if (!mappedState) continue;
157907
- const fp = mappedState.state;
157908
- if (fp !== this.lastMappedStates[mappedId]) {
157909
- this.lastMappedStates[mappedId] = fp;
157910
- changed = true;
157911
- }
157912
- }
157913
- return changed;
157914
- }
157915
- };
157916
- function createEndpointId(entityId, customName) {
157917
- const baseName = customName || entityId;
157918
- return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
157919
- }
157920
- function getMappedEntityIds(mapping) {
157921
- if (!mapping) return [];
157922
- const ids = [];
157923
- if (mapping.batteryEntity && !mapping.disableBatteryMapping) {
157924
- ids.push(mapping.batteryEntity);
157925
- }
157926
- if (mapping.faultEntity) ids.push(mapping.faultEntity);
157927
- if (mapping.chargingStateEntity) ids.push(mapping.chargingStateEntity);
157928
- if (mapping.temperatureEntity) ids.push(mapping.temperatureEntity);
157929
- if (mapping.humidityEntity) ids.push(mapping.humidityEntity);
157930
- if (mapping.pressureEntity) ids.push(mapping.pressureEntity);
157931
- if (mapping.cleaningModeEntity) ids.push(mapping.cleaningModeEntity);
157932
- if (mapping.suctionLevelEntity) ids.push(mapping.suctionLevelEntity);
157933
- if (mapping.mopIntensityEntity) ids.push(mapping.mopIntensityEntity);
157934
- if (mapping.filterLifeEntity) ids.push(mapping.filterLifeEntity);
157935
- if (mapping.powerEntity) ids.push(mapping.powerEntity);
157936
- if (mapping.energyEntity) ids.push(mapping.energyEntity);
157937
- if (mapping.voltageEntity) ids.push(mapping.voltageEntity);
157938
- if (mapping.currentEntity) ids.push(mapping.currentEntity);
157939
- if (mapping.batteryPowerEntity) ids.push(mapping.batteryPowerEntity);
157940
- if (mapping.batteryEnergyEntity) ids.push(mapping.batteryEnergyEntity);
157941
- if (mapping.currentRoomEntity) ids.push(mapping.currentRoomEntity);
157942
- if (mapping.cleanedAreaEntity) ids.push(mapping.cleanedAreaEntity);
157943
- if (mapping.composedEntities) {
157944
- for (const sub of mapping.composedEntities) {
157945
- if (sub.entityId) ids.push(sub.entityId);
157946
- }
157947
- }
157948
- return ids;
157949
- }
157950
-
157951
158330
  // src/matter/endpoints/legacy/legacy-endpoint.ts
157952
158331
  init_dist();
157953
158332
  init_esm();
@@ -167520,6 +167899,284 @@ var ElectricalMeterType = ElectricalMeterDevice.with(
167520
167899
  EnergyServer
167521
167900
  );
167522
167901
 
167902
+ // src/matter/endpoints/legacy/sensor/devices/energy-evse.ts
167903
+ init_esm();
167904
+ init_home_assistant_actions();
167905
+ init_home_assistant_entity_behavior();
167906
+
167907
+ // src/matter/endpoints/legacy/sensor/devices/evse-status.ts
167908
+ function mapEvseStatus(rawState, chargingSwitchOn) {
167909
+ const raw = rawState.toLowerCase().trim();
167910
+ const switchSupply = chargingSwitchOn ? EnergyEvse3.SupplyState.ChargingEnabled : EnergyEvse3.SupplyState.Disabled;
167911
+ if (raw === "" || raw === "unknown" || raw === "unavailable") {
167912
+ return {
167913
+ state: null,
167914
+ supplyState: EnergyEvse3.SupplyState.Disabled,
167915
+ faultState: EnergyEvse3.FaultState.NoError
167916
+ };
167917
+ }
167918
+ if (raw.includes("error") || raw.includes("fault")) {
167919
+ return {
167920
+ state: EnergyEvse3.State.Fault,
167921
+ supplyState: EnergyEvse3.SupplyState.DisabledError,
167922
+ faultState: EnergyEvse3.FaultState.Other
167923
+ };
167924
+ }
167925
+ if (raw.includes("not connected") || raw.includes("not_connected") || raw.includes("disconnected") || raw.includes("no vehicle")) {
167926
+ return {
167927
+ state: EnergyEvse3.State.NotPluggedIn,
167928
+ supplyState: switchSupply,
167929
+ faultState: EnergyEvse3.FaultState.NoError
167930
+ };
167931
+ }
167932
+ if (raw.includes("not charging") || raw.includes("not_charging") || raw.includes("not-charging")) {
167933
+ return {
167934
+ state: EnergyEvse3.State.PluggedInNoDemand,
167935
+ supplyState: switchSupply,
167936
+ faultState: EnergyEvse3.FaultState.NoError
167937
+ };
167938
+ }
167939
+ if (raw.includes("charg") && !raw.includes("discharg")) {
167940
+ return {
167941
+ state: EnergyEvse3.State.PluggedInCharging,
167942
+ supplyState: EnergyEvse3.SupplyState.ChargingEnabled,
167943
+ faultState: EnergyEvse3.FaultState.NoError
167944
+ };
167945
+ }
167946
+ if (raw.includes("connected") || raw.includes("ready") || raw.includes("awaiting") || raw.includes("completed") || raw.includes("sleep") || raw.includes("paused")) {
167947
+ return {
167948
+ state: EnergyEvse3.State.PluggedInNoDemand,
167949
+ supplyState: switchSupply,
167950
+ faultState: EnergyEvse3.FaultState.NoError
167951
+ };
167952
+ }
167953
+ return {
167954
+ state: null,
167955
+ supplyState: EnergyEvse3.SupplyState.Disabled,
167956
+ faultState: EnergyEvse3.FaultState.NoError
167957
+ };
167958
+ }
167959
+
167960
+ // src/matter/endpoints/legacy/sensor/devices/energy-evse.ts
167961
+ var logger229 = Logger.get("EnergyEvse");
167962
+ var MIN_CHARGE_CURRENT_MA = 6e3;
167963
+ var MAX_CHARGE_CURRENT_MA = 32e3;
167964
+ var CIRCUIT_CAPACITY_MA = 32e3;
167965
+ var MAX_TIMER_MS = 2147483647;
167966
+ function clampMa(ma) {
167967
+ return Math.min(CIRCUIT_CAPACITY_MA, Math.max(MIN_CHARGE_CURRENT_MA, ma));
167968
+ }
167969
+ var evseChargingTimers = /* @__PURE__ */ new WeakMap();
167970
+ function clearEvseChargingTimer(endpoint) {
167971
+ const timer = evseChargingTimers.get(endpoint);
167972
+ if (timer) {
167973
+ clearTimeout(timer);
167974
+ evseChargingTimers.delete(endpoint);
167975
+ }
167976
+ }
167977
+ var EvseStatusServer = class _EvseStatusServer extends EnergyEvseServer {
167978
+ async initialize() {
167979
+ await super.initialize();
167980
+ const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
167981
+ this.update();
167982
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
167983
+ }
167984
+ async [Symbol.asyncDispose]() {
167985
+ clearEvseChargingTimer(this.endpoint);
167986
+ await super[Symbol.asyncDispose]();
167987
+ }
167988
+ chargingSwitchIsOn() {
167989
+ const mapping = this.agent.get(HomeAssistantEntityBehavior).state.mapping;
167990
+ const switchEntity = mapping?.chargingSwitchEntity;
167991
+ if (!switchEntity) return false;
167992
+ const stateProvider = this.agent.env.get(EntityStateProvider);
167993
+ return stateProvider.getState(switchEntity)?.state === "on";
167994
+ }
167995
+ update() {
167996
+ const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
167997
+ const raw = homeAssistant.entity.state?.state ?? "";
167998
+ const status3 = mapEvseStatus(raw, this.chargingSwitchIsOn());
167999
+ const patch = {
168000
+ state: status3.state,
168001
+ supplyState: status3.supplyState,
168002
+ faultState: status3.faultState
168003
+ };
168004
+ const mapping = homeAssistant.state.mapping;
168005
+ if (mapping?.currentLimitEntity) {
168006
+ const stateProvider = this.agent.env.get(EntityStateProvider);
168007
+ const amps = stateProvider.getNumericState(mapping.currentLimitEntity);
168008
+ if (amps != null) {
168009
+ patch.maximumChargeCurrent = clampMa(Math.round(amps * 1e3));
168010
+ }
168011
+ }
168012
+ applyPatchState(this.state, patch);
168013
+ }
168014
+ enableCharging(request) {
168015
+ const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168016
+ const mapping = homeAssistant.state.mapping;
168017
+ const until = request.chargingEnabledUntil;
168018
+ if (until != null && until * 1e3 <= Date.now()) {
168019
+ this.disable();
168020
+ return;
168021
+ }
168022
+ clearEvseChargingTimer(this.endpoint);
168023
+ let dispatched = false;
168024
+ if (mapping?.chargingSwitchEntity) {
168025
+ homeAssistant.callAction({
168026
+ action: "switch.turn_on",
168027
+ target: mapping.chargingSwitchEntity
168028
+ });
168029
+ dispatched = true;
168030
+ }
168031
+ const maxCurrent = request.maximumChargeCurrent;
168032
+ if (mapping?.currentLimitEntity && maxCurrent != null) {
168033
+ const amps = Math.max(
168034
+ MIN_CHARGE_CURRENT_MA / 1e3,
168035
+ Math.floor(clampMa(Number(maxCurrent)) / 1e3)
168036
+ );
168037
+ homeAssistant.callAction({
168038
+ action: "number.set_value",
168039
+ target: mapping.currentLimitEntity,
168040
+ data: { value: amps }
168041
+ });
168042
+ dispatched = true;
168043
+ }
168044
+ if (!dispatched) {
168045
+ logger229.debug(
168046
+ `enableCharging ignored for ${homeAssistant.entityId}: no charging switch or current limit mapped`
168047
+ );
168048
+ return;
168049
+ }
168050
+ const patch = {
168051
+ supplyState: EnergyEvse3.SupplyState.ChargingEnabled,
168052
+ chargingEnabledUntil: until ?? null
168053
+ };
168054
+ if (request.minimumChargeCurrent != null) {
168055
+ patch.minimumChargeCurrent = clampMa(
168056
+ Number(request.minimumChargeCurrent)
168057
+ );
168058
+ }
168059
+ if (maxCurrent != null) {
168060
+ patch.maximumChargeCurrent = clampMa(Number(maxCurrent));
168061
+ }
168062
+ applyPatchState(this.state, patch);
168063
+ if (until != null) {
168064
+ const delay = until * 1e3 - Date.now();
168065
+ if (delay > 0 && delay <= MAX_TIMER_MS) {
168066
+ const endpoint = this.endpoint;
168067
+ const actions = this.env.get(HomeAssistantActions);
168068
+ const entityId = homeAssistant.entityId;
168069
+ const switchEntity = mapping?.chargingSwitchEntity;
168070
+ const timer = setTimeout(() => {
168071
+ void (async () => {
168072
+ try {
168073
+ if (evseChargingTimers.get(endpoint) !== timer) return;
168074
+ if (switchEntity) {
168075
+ actions.call(
168076
+ { action: "switch.turn_off", target: switchEntity },
168077
+ entityId
168078
+ );
168079
+ }
168080
+ await endpoint.setStateOf(_EvseStatusServer, {
168081
+ supplyState: EnergyEvse3.SupplyState.Disabled,
168082
+ chargingEnabledUntil: null
168083
+ });
168084
+ } catch (error) {
168085
+ logger229.debug(
168086
+ `EVSE charge-window expiry write failed (endpoint may be closing): ${error}`
168087
+ );
168088
+ } finally {
168089
+ if (evseChargingTimers.get(endpoint) === timer) {
168090
+ evseChargingTimers.delete(endpoint);
168091
+ }
168092
+ }
168093
+ })();
168094
+ }, delay);
168095
+ timer.unref?.();
168096
+ evseChargingTimers.set(endpoint, timer);
168097
+ }
168098
+ }
168099
+ }
168100
+ disable() {
168101
+ clearEvseChargingTimer(this.endpoint);
168102
+ const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168103
+ const mapping = homeAssistant.state.mapping;
168104
+ if (mapping?.chargingSwitchEntity) {
168105
+ homeAssistant.callAction({
168106
+ action: "switch.turn_off",
168107
+ target: mapping.chargingSwitchEntity
168108
+ });
168109
+ }
168110
+ if (mapping?.chargingSwitchEntity || mapping?.currentLimitEntity) {
168111
+ applyPatchState(this.state, {
168112
+ supplyState: EnergyEvse3.SupplyState.Disabled,
168113
+ chargingEnabledUntil: null
168114
+ });
168115
+ } else {
168116
+ logger229.debug(
168117
+ `disable ignored for ${homeAssistant.entityId}: no charging switch mapped`
168118
+ );
168119
+ }
168120
+ }
168121
+ };
168122
+ var EvseStatusSeeded = EvseStatusServer.set({
168123
+ supplyState: EnergyEvse3.SupplyState.Disabled,
168124
+ faultState: EnergyEvse3.FaultState.NoError,
168125
+ circuitCapacity: CIRCUIT_CAPACITY_MA,
168126
+ minimumChargeCurrent: MIN_CHARGE_CURRENT_MA,
168127
+ maximumChargeCurrent: MAX_CHARGE_CURRENT_MA
168128
+ });
168129
+ var EvseModeSeeded = EnergyEvseModeServer.set({
168130
+ supportedModes: [
168131
+ {
168132
+ label: "Manual",
168133
+ mode: 1,
168134
+ modeTags: [{ value: EnergyEvseMode3.ModeTag.Manual }]
168135
+ }
168136
+ ],
168137
+ currentMode: 1
168138
+ });
168139
+ var WiredPowerSourceBase = PowerSourceServer.with("Wired");
168140
+ var EvsePowerSourceServer = class extends WiredPowerSourceBase {
168141
+ async initialize() {
168142
+ await super.initialize();
168143
+ const endpointNumber = this.endpoint.number;
168144
+ if (endpointNumber != null) {
168145
+ applyPatchState(this.state, { endpointList: [endpointNumber] });
168146
+ }
168147
+ }
168148
+ };
168149
+ var EvsePowerSource = EvsePowerSourceServer.set({
168150
+ status: PowerSource3.PowerSourceStatus.Active,
168151
+ order: 0,
168152
+ description: "Mains",
168153
+ wiredCurrentType: PowerSource3.WiredCurrentType.Ac
168154
+ });
168155
+ var EnergyEvseBase = EnergyEvseDevice.with(
168156
+ BasicInformationServer2,
168157
+ IdentifyServer2,
168158
+ HomeAssistantEntityBehavior,
168159
+ EvseStatusSeeded,
168160
+ EvseModeSeeded,
168161
+ EvsePowerSource,
168162
+ // Always mount the measurement clusters; both seed safe defaults when no
168163
+ // power/energy entity is mapped, so the endpoint honestly carries the
168164
+ // ElectricalSensor device type.
168165
+ PowerServer,
168166
+ EnergyServer,
168167
+ DescriptorServer
168168
+ ).set({
168169
+ descriptor: {
168170
+ deviceTypeList: [
168171
+ { deviceType: DeviceTypeId(1292), revision: 2 },
168172
+ { deviceType: DeviceTypeId(1296), revision: 1 }
168173
+ ]
168174
+ }
168175
+ });
168176
+ function energyEvseType() {
168177
+ return EnergyEvseBase;
168178
+ }
168179
+
167523
168180
  // src/matter/behaviors/flow-measurement-server.ts
167524
168181
  init_home_assistant_entity_behavior();
167525
168182
  var FlowMeasurementServerBase = class extends FlowMeasurementServer {
@@ -168259,7 +168916,7 @@ var TvocConcentrationMeasurementServer = class extends TvocConcentrationMeasurem
168259
168916
  };
168260
168917
 
168261
168918
  // src/matter/endpoints/legacy/sensor/devices/tvoc-sensor.ts
168262
- var logger229 = Logger.get("TvocSensor");
168919
+ var logger230 = Logger.get("TvocSensor");
168263
168920
  function airQualityFromUgm3(value) {
168264
168921
  if (value <= 300) return AirQuality3.AirQualityEnum.Good;
168265
168922
  if (value <= 1e3) return AirQuality3.AirQualityEnum.Fair;
@@ -168300,17 +168957,17 @@ var TvocAirQualityServer = class extends TvocAirQualityServerBase {
168300
168957
  const attributes9 = entity.state.attributes;
168301
168958
  const deviceClass = attributes9.device_class;
168302
168959
  let airQuality = AirQuality3.AirQualityEnum.Unknown;
168303
- logger229.debug(
168960
+ logger230.debug(
168304
168961
  `[${entity.entity_id}] TVOC update: state="${state}", device_class="${deviceClass}"`
168305
168962
  );
168306
168963
  if (state != null && !Number.isNaN(+state)) {
168307
168964
  const value = +state;
168308
168965
  airQuality = deviceClass === SensorDeviceClass.volatile_organic_compounds ? airQualityFromUgm3(value) : airQualityFromPpb(value);
168309
- logger229.debug(
168966
+ logger230.debug(
168310
168967
  `[${entity.entity_id}] TVOC value=${value} (${deviceClass}) -> airQuality=${AirQuality3.AirQualityEnum[airQuality]}`
168311
168968
  );
168312
168969
  } else {
168313
- logger229.warn(
168970
+ logger230.warn(
168314
168971
  `[${entity.entity_id}] TVOC state not a valid number: "${state}"`
168315
168972
  );
168316
168973
  }
@@ -168524,7 +169181,7 @@ init_home_assistant_entity_behavior();
168524
169181
  // src/matter/behaviors/pm25-concentration-measurement-server.ts
168525
169182
  init_esm();
168526
169183
  init_home_assistant_entity_behavior();
168527
- var logger230 = Logger.get("Pm25ConcentrationMeasurementServer");
169184
+ var logger231 = Logger.get("Pm25ConcentrationMeasurementServer");
168528
169185
  var Pm25ConcentrationMeasurementServerBase = Pm25ConcentrationMeasurementServer.with(
168529
169186
  ConcentrationMeasurement3.Feature.NumericMeasurement
168530
169187
  );
@@ -168548,11 +169205,11 @@ var Pm25ConcentrationMeasurementServer2 = class extends Pm25ConcentrationMeasure
168548
169205
  if (this.state.measurementMedium === void 0) {
168549
169206
  this.state.measurementMedium = ConcentrationMeasurement3.MeasurementMedium.Air;
168550
169207
  }
168551
- logger230.debug(
169208
+ logger231.debug(
168552
169209
  "Pm25ConcentrationMeasurementServer: before super.initialize()"
168553
169210
  );
168554
169211
  await super.initialize();
168555
- logger230.debug(
169212
+ logger231.debug(
168556
169213
  "Pm25ConcentrationMeasurementServer: after super.initialize()"
168557
169214
  );
168558
169215
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
@@ -168575,7 +169232,7 @@ var Pm25ConcentrationMeasurementServer2 = class extends Pm25ConcentrationMeasure
168575
169232
  };
168576
169233
 
168577
169234
  // src/matter/endpoints/legacy/sensor/devices/pm25-sensor.ts
168578
- var logger231 = Logger.get("Pm25AirQualityServer");
169235
+ var logger232 = Logger.get("Pm25AirQualityServer");
168579
169236
  var Pm25AirQualityServerBase = AirQualityServer.with(
168580
169237
  AirQuality3.Feature.Fair,
168581
169238
  AirQuality3.Feature.Moderate,
@@ -168587,9 +169244,9 @@ var Pm25AirQualityServer = class extends Pm25AirQualityServerBase {
168587
169244
  if (this.state.airQuality === void 0) {
168588
169245
  this.state.airQuality = AirQuality3.AirQualityEnum.Unknown;
168589
169246
  }
168590
- logger231.debug("Pm25AirQualityServer: before super.initialize()");
169247
+ logger232.debug("Pm25AirQualityServer: before super.initialize()");
168591
169248
  await super.initialize();
168592
- logger231.debug("Pm25AirQualityServer: after super.initialize()");
169249
+ logger232.debug("Pm25AirQualityServer: after super.initialize()");
168593
169250
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
168594
169251
  this.update(homeAssistant.entity);
168595
169252
  this.reactTo(homeAssistant.onChange, this.update, { offline: true });
@@ -169014,7 +169671,7 @@ init_home_assistant_entity_behavior();
169014
169671
  init_dist();
169015
169672
  init_esm();
169016
169673
  init_home_assistant_entity_behavior();
169017
- var logger232 = Logger.get("VacuumIdentifyServer");
169674
+ var logger233 = Logger.get("VacuumIdentifyServer");
169018
169675
  var IDENTIFY_BUTTON_SUFFIXES = ["_identify", "_locate", "_find_me"];
169019
169676
  var VacuumIdentifyServer = class extends IdentifyServer2 {
169020
169677
  triggerEffect(effect) {
@@ -169031,19 +169688,19 @@ var VacuumIdentifyServer = class extends IdentifyServer2 {
169031
169688
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
169032
169689
  const features3 = homeAssistant.entity.state.attributes.supported_features ?? 0;
169033
169690
  if (testBit(features3, VacuumDeviceFeature.LOCATE)) {
169034
- logger232.info(`${source} \u2192 vacuum.locate for ${homeAssistant.entityId}`);
169691
+ logger233.info(`${source} \u2192 vacuum.locate for ${homeAssistant.entityId}`);
169035
169692
  homeAssistant.callAction({ action: "vacuum.locate" });
169036
169693
  return;
169037
169694
  }
169038
169695
  const sibling = this.#findIdentifyButton(homeAssistant);
169039
169696
  if (sibling) {
169040
- logger232.info(
169697
+ logger233.info(
169041
169698
  `${source} \u2192 button.press ${sibling} for ${homeAssistant.entityId}`
169042
169699
  );
169043
169700
  homeAssistant.callAction({ action: "button.press", target: sibling });
169044
169701
  return;
169045
169702
  }
169046
- logger232.warn(
169703
+ logger233.warn(
169047
169704
  `${source} for ${homeAssistant.entityId}, LOCATE not in supported_features (${features3}), trying vacuum.locate anyway`
169048
169705
  );
169049
169706
  homeAssistant.callAction({ action: "vacuum.locate" });
@@ -169242,14 +169899,14 @@ init_esm();
169242
169899
 
169243
169900
  // src/matter/behaviors/service-area-server.ts
169244
169901
  init_esm();
169245
- var logger233 = Logger.get("ServiceAreaServer");
169902
+ var logger234 = Logger.get("ServiceAreaServer");
169246
169903
  var ServiceAreaWithProgress = ServiceAreaBehavior.with(
169247
169904
  ServiceArea3.Feature.ProgressReporting
169248
169905
  );
169249
169906
  var ServiceAreaServerBase = class extends ServiceAreaWithProgress {
169250
169907
  selectAreas(request) {
169251
169908
  const { newAreas } = request;
169252
- logger233.info(
169909
+ logger234.info(
169253
169910
  `ServiceArea selectAreas called with: ${JSON.stringify(newAreas)}`
169254
169911
  );
169255
169912
  const uniqueAreas = [...new Set(newAreas)];
@@ -169258,7 +169915,7 @@ var ServiceAreaServerBase = class extends ServiceAreaWithProgress {
169258
169915
  (id) => !supportedAreaIds.includes(id)
169259
169916
  );
169260
169917
  if (invalidAreas.length > 0) {
169261
- logger233.warn(`Invalid area IDs requested: ${invalidAreas.join(", ")}`);
169918
+ logger234.warn(`Invalid area IDs requested: ${invalidAreas.join(", ")}`);
169262
169919
  return {
169263
169920
  status: ServiceArea3.SelectAreasStatus.UnsupportedArea,
169264
169921
  statusText: `Invalid area IDs: ${invalidAreas.join(", ")}`
@@ -169270,7 +169927,7 @@ var ServiceAreaServerBase = class extends ServiceAreaWithProgress {
169270
169927
  status: ServiceArea3.OperationalStatus.Pending
169271
169928
  }));
169272
169929
  this.state.currentArea = null;
169273
- logger233.info(
169930
+ logger234.info(
169274
169931
  `ServiceArea: Stored ${uniqueAreas.length} areas for cleaning: ${uniqueAreas.join(", ")}`
169275
169932
  );
169276
169933
  return {
@@ -169291,7 +169948,7 @@ var ServiceAreaServerBase = class extends ServiceAreaWithProgress {
169291
169948
  ServiceAreaServerBase2.State = State;
169292
169949
  })(ServiceAreaServerBase || (ServiceAreaServerBase = {}));
169293
169950
  function ServiceAreaServer2(initialState) {
169294
- logger233.info(
169951
+ logger234.info(
169295
169952
  `Creating ServiceAreaServer with ${initialState.supportedAreas.length} areas`
169296
169953
  );
169297
169954
  return ServiceAreaServerBase.set({
@@ -169308,7 +169965,7 @@ var ServiceAreaWithMapsAndProgress = ServiceAreaBehavior.with(
169308
169965
  var ServiceAreaServerWithMapsBase = class extends ServiceAreaWithMapsAndProgress {
169309
169966
  selectAreas(request) {
169310
169967
  const { newAreas } = request;
169311
- logger233.info(
169968
+ logger234.info(
169312
169969
  `ServiceArea selectAreas called with: ${JSON.stringify(newAreas)}`
169313
169970
  );
169314
169971
  const uniqueAreas = [...new Set(newAreas)];
@@ -169317,7 +169974,7 @@ var ServiceAreaServerWithMapsBase = class extends ServiceAreaWithMapsAndProgress
169317
169974
  (id) => !supportedAreaIds.includes(id)
169318
169975
  );
169319
169976
  if (invalidAreas.length > 0) {
169320
- logger233.warn(`Invalid area IDs requested: ${invalidAreas.join(", ")}`);
169977
+ logger234.warn(`Invalid area IDs requested: ${invalidAreas.join(", ")}`);
169321
169978
  return {
169322
169979
  status: ServiceArea3.SelectAreasStatus.UnsupportedArea,
169323
169980
  statusText: `Invalid area IDs: ${invalidAreas.join(", ")}`
@@ -169329,7 +169986,7 @@ var ServiceAreaServerWithMapsBase = class extends ServiceAreaWithMapsAndProgress
169329
169986
  status: ServiceArea3.OperationalStatus.Pending
169330
169987
  }));
169331
169988
  this.state.currentArea = null;
169332
- logger233.info(
169989
+ logger234.info(
169333
169990
  `ServiceArea: Stored ${uniqueAreas.length} areas for cleaning: ${uniqueAreas.join(", ")}`
169334
169991
  );
169335
169992
  return {
@@ -169350,14 +170007,14 @@ var ServiceAreaServerWithMapsBase = class extends ServiceAreaWithMapsAndProgress
169350
170007
  ServiceAreaServerWithMapsBase2.State = State;
169351
170008
  })(ServiceAreaServerWithMapsBase || (ServiceAreaServerWithMapsBase = {}));
169352
170009
  function ServiceAreaServerWithMaps(initialState) {
169353
- logger233.info(
170010
+ logger234.info(
169354
170011
  `Creating ServiceAreaServer with Maps: ${initialState.supportedAreas.length} areas, ${initialState.supportedMaps.length} maps`
169355
170012
  );
169356
170013
  for (const map of initialState.supportedMaps) {
169357
170014
  const areaCount = initialState.supportedAreas.filter(
169358
170015
  (a) => a.mapId === map.mapId
169359
170016
  ).length;
169360
- logger233.info(` Map ${map.mapId}: "${map.name}" (${areaCount} areas)`);
170017
+ logger234.info(` Map ${map.mapId}: "${map.name}" (${areaCount} areas)`);
169361
170018
  }
169362
170019
  return ServiceAreaServerWithMapsBase.set({
169363
170020
  supportedAreas: initialState.supportedAreas,
@@ -169369,7 +170026,7 @@ function ServiceAreaServerWithMaps(initialState) {
169369
170026
  }
169370
170027
 
169371
170028
  // src/matter/endpoints/legacy/vacuum/behaviors/vacuum-service-area-server.ts
169372
- var logger234 = Logger.get("VacuumServiceAreaServer");
170029
+ var logger235 = Logger.get("VacuumServiceAreaServer");
169373
170030
  function toAreaId(roomId) {
169374
170031
  if (typeof roomId === "number") {
169375
170032
  return roomId;
@@ -169448,13 +170105,13 @@ function createVacuumServiceAreaServer(attributes9, roomEntities, includeUnnamed
169448
170105
  let rooms;
169449
170106
  if (roomEntities && roomEntities.length > 0) {
169450
170107
  rooms = buttonEntitiesToRooms(roomEntities, attributes9);
169451
- logger234.info(
170108
+ logger235.info(
169452
170109
  `Using ${rooms.length} button entities as rooms: ${rooms.map((r) => r.name).join(", ")}`
169453
170110
  );
169454
170111
  } else {
169455
170112
  rooms = parseVacuumRooms(attributes9, includeUnnamedRooms);
169456
170113
  if (rooms.length > 0) {
169457
- logger234.info(
170114
+ logger235.info(
169458
170115
  `Using ${rooms.length} rooms from attributes: ${rooms.map((r) => r.name).join(", ")}`
169459
170116
  );
169460
170117
  }
@@ -169508,7 +170165,7 @@ function createCustomServiceAreaServer(customAreas) {
169508
170165
  landmarkInfo: null
169509
170166
  }
169510
170167
  }));
169511
- logger234.info(
170168
+ logger235.info(
169512
170169
  `Using ${customAreas.length} custom service areas: ${customAreas.map((a) => a.name).join(", ")}`
169513
170170
  );
169514
170171
  return ServiceAreaServer2({
@@ -169530,7 +170187,7 @@ function createCleanAreaServiceAreaServer(cleanAreaRooms) {
169530
170187
  landmarkInfo: null
169531
170188
  }
169532
170189
  }));
169533
- logger234.info(
170190
+ logger235.info(
169534
170191
  `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA: ${cleanAreaRooms.map((r) => r.name).join(", ")}`
169535
170192
  );
169536
170193
  return ServiceAreaServer2({
@@ -169541,11 +170198,11 @@ function createCleanAreaServiceAreaServer(cleanAreaRooms) {
169541
170198
  }
169542
170199
 
169543
170200
  // src/matter/endpoints/legacy/vacuum/behaviors/vacuum-rvc-run-mode-server.ts
169544
- var logger235 = Logger.get("VacuumRvcRunModeServer");
170201
+ var logger236 = Logger.get("VacuumRvcRunModeServer");
169545
170202
  function buildValetudoSegmentAction(vacuumEntityId, segmentIds, valetudoIdentifier) {
169546
170203
  const identifier = valetudoIdentifier || vacuumEntityId.replace(/^vacuum\.valetudo_/, "");
169547
170204
  const topic = `valetudo/${identifier}/MapSegmentationCapability/clean/set`;
169548
- logger235.info(
170205
+ logger236.info(
169549
170206
  `Valetudo: mqtt.publish to ${topic}, segments: ${segmentIds.join(", ")}`
169550
170207
  );
169551
170208
  return {
@@ -169650,14 +170307,14 @@ function mergeBatchData(areas) {
169650
170307
  function handleCustomServiceAreas(selectedAreas, customAreas, session) {
169651
170308
  const matched = selectedAreas.map((areaId) => ({ areaId, area: customAreas[areaId - 1] })).filter((m) => !!m.area);
169652
170309
  if (matched.length === 0) {
169653
- logger235.warn(
170310
+ logger236.warn(
169654
170311
  `Custom service areas: no match for selected IDs ${selectedAreas.join(", ")}`
169655
170312
  );
169656
170313
  return { action: "vacuum.start" };
169657
170314
  }
169658
170315
  const batchArea = matched.find(({ area }) => area.batchDispatch === true);
169659
170316
  if (batchArea) {
169660
- logger235.info(
170317
+ logger236.info(
169661
170318
  `Custom service areas (batch): single call for ${matched.length} room(s): ${matched.map(({ area }) => area.name).join(", ")}`
169662
170319
  );
169663
170320
  session.pendingDispatches = [];
@@ -169678,7 +170335,7 @@ function handleCustomServiceAreas(selectedAreas, customAreas, session) {
169678
170335
  }
169679
170336
  };
169680
170337
  }
169681
- logger235.info(
170338
+ logger236.info(
169682
170339
  `Custom service areas: ${matched.length} room(s) queued: ${matched.map(({ area }) => `${area.service} (${area.name})`).join(", ")}`
169683
170340
  );
169684
170341
  session.pendingDispatches = matched.slice(1).map(({ areaId, area }) => ({
@@ -169716,7 +170373,7 @@ function vacuumIsCleaning(state) {
169716
170373
  var vacuumRvcRunModeConfig = {
169717
170374
  getCurrentMode: (entity) => {
169718
170375
  const isCleaning = vacuumIsCleaning(entity.state);
169719
- logger235.debug(
170376
+ logger236.debug(
169720
170377
  `Vacuum state: "${entity.state}", isCleaning: ${isCleaning}, currentMode: ${isCleaning ? "Cleaning" : "Idle"}`
169721
170378
  );
169722
170379
  return isCleaning ? 1 /* Cleaning */ : 0 /* Idle */;
@@ -169750,7 +170407,7 @@ var vacuumRvcRunModeConfig = {
169750
170407
  if (cleanAreaRooms && cleanAreaRooms.length > 0) {
169751
170408
  const haAreaIds = resolveCleanAreaIds(selectedAreas, cleanAreaRooms);
169752
170409
  if (haAreaIds.length > 0) {
169753
- logger235.info(
170410
+ logger236.info(
169754
170411
  `CLEAN_AREA: cleaning HA areas: ${haAreaIds.join(", ")}`
169755
170412
  );
169756
170413
  return {
@@ -169769,7 +170426,7 @@ var vacuumRvcRunModeConfig = {
169769
170426
  }
169770
170427
  }
169771
170428
  if (matched.length > 0) {
169772
- logger235.info(
170429
+ logger236.info(
169773
170430
  `Roborock: ${matched.length} room button(s) queued: ${matched.map((m) => m.entityId).join(", ")}`
169774
170431
  );
169775
170432
  session.pendingDispatches = matched.slice(1).map(({ areaId, entityId }) => ({
@@ -169803,14 +170460,14 @@ var vacuumRvcRunModeConfig = {
169803
170460
  }
169804
170461
  }
169805
170462
  if (roomIds.length > 0) {
169806
- logger235.info(
170463
+ logger236.info(
169807
170464
  `Starting cleaning with selected areas: ${roomIds.join(", ")}`
169808
170465
  );
169809
170466
  if (isDreameVacuum(attributes9)) {
169810
170467
  if (targetMapName) {
169811
170468
  const vacName = vacuumEntityId.replace("vacuum.", "");
169812
170469
  const selectedMapEntity = `select.${vacName}_selected_map`;
169813
- logger235.info(
170470
+ logger236.info(
169814
170471
  `Dreame multi-floor: switching to map "${targetMapName}" via ${selectedMapEntity}`
169815
170472
  );
169816
170473
  homeAssistant.callAction({
@@ -169837,7 +170494,7 @@ var vacuumRvcRunModeConfig = {
169837
170494
  }
169838
170495
  if (isEcovacsVacuum(attributes9)) {
169839
170496
  const roomIdStr = roomIds.join(",");
169840
- logger235.info(
170497
+ logger236.info(
169841
170498
  `Ecovacs vacuum: Using spot_area for rooms: ${roomIdStr}`
169842
170499
  );
169843
170500
  return {
@@ -169852,14 +170509,14 @@ var vacuumRvcRunModeConfig = {
169852
170509
  }
169853
170510
  };
169854
170511
  }
169855
- logger235.warn(
170512
+ logger236.warn(
169856
170513
  `Room cleaning via send_command not supported for this vacuum type. Rooms: ${roomIds.join(", ")}. Falling back to vacuum.start`
169857
170514
  );
169858
170515
  }
169859
170516
  }
169860
170517
  } catch {
169861
170518
  }
169862
- logger235.info("Starting regular cleaning (no areas selected)");
170519
+ logger236.info("Starting regular cleaning (no areas selected)");
169863
170520
  return { action: "vacuum.start" };
169864
170521
  },
169865
170522
  returnToBase: () => ({ action: "vacuum.return_to_base" }),
@@ -169874,7 +170531,7 @@ var vacuumRvcRunModeConfig = {
169874
170531
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
169875
170532
  const entity = homeAssistant.entity;
169876
170533
  const attributes9 = entity.state.attributes;
169877
- logger235.info(`cleanRoom called: roomMode=${roomMode}`);
170534
+ logger236.info(`cleanRoom called: roomMode=${roomMode}`);
169878
170535
  const cleanAreaRooms = homeAssistant.state.mapping?.cleanAreaRooms;
169879
170536
  if (cleanAreaRooms && cleanAreaRooms.length > 0) {
169880
170537
  const sorted = [...cleanAreaRooms].sort(
@@ -169883,7 +170540,7 @@ var vacuumRvcRunModeConfig = {
169883
170540
  const areaIndex = roomMode - ROOM_MODE_BASE2 - 1;
169884
170541
  if (areaIndex >= 0 && areaIndex < sorted.length) {
169885
170542
  const area = sorted[areaIndex];
169886
- logger235.info(
170543
+ logger236.info(
169887
170544
  `cleanRoom: CLEAN_AREA "${area.name}" \u2192 vacuum.clean_area(${area.haAreaId})`
169888
170545
  );
169889
170546
  return {
@@ -169900,7 +170557,7 @@ var vacuumRvcRunModeConfig = {
169900
170557
  const areaIndex = roomMode - ROOM_MODE_BASE2 - 1;
169901
170558
  if (areaIndex >= 0 && areaIndex < sorted.length) {
169902
170559
  const area = sorted[areaIndex];
169903
- logger235.info(
170560
+ logger236.info(
169904
170561
  `cleanRoom: custom service area "${area.name}" \u2192 ${area.service}`
169905
170562
  );
169906
170563
  return {
@@ -169921,7 +170578,7 @@ var vacuumRvcRunModeConfig = {
169921
170578
  }
169922
170579
  const rooms = parseVacuumRooms(attributes9);
169923
170580
  const numericIdFromMode = getRoomIdFromMode(roomMode);
169924
- logger235.info(
170581
+ logger236.info(
169925
170582
  `cleanRoom: numericIdFromMode=${numericIdFromMode}, available rooms: ${JSON.stringify(rooms.map((r) => ({ id: r.id, name: r.name, modeValue: getRoomModeValue(r) })))}`
169926
170583
  );
169927
170584
  const room = rooms.find((r) => getRoomModeValue(r) === roomMode);
@@ -169931,7 +170588,7 @@ var vacuumRvcRunModeConfig = {
169931
170588
  if (room.mapName) {
169932
170589
  const vacuumName = vacuumEntityId.replace("vacuum.", "");
169933
170590
  const selectedMapEntity = `select.${vacuumName}_selected_map`;
169934
- logger235.info(
170591
+ logger236.info(
169935
170592
  `Dreame multi-floor: switching to map "${room.mapName}" via ${selectedMapEntity}`
169936
170593
  );
169937
170594
  homeAssistant.callAction({
@@ -169940,7 +170597,7 @@ var vacuumRvcRunModeConfig = {
169940
170597
  data: { option: room.mapName }
169941
170598
  });
169942
170599
  }
169943
- logger235.debug(
170600
+ logger236.debug(
169944
170601
  `Dreame vacuum detected, using dreame_vacuum.vacuum_clean_segment for room ${room.name} (commandId: ${commandId3}, id: ${room.id})`
169945
170602
  );
169946
170603
  return {
@@ -169951,7 +170608,7 @@ var vacuumRvcRunModeConfig = {
169951
170608
  };
169952
170609
  }
169953
170610
  if (isRoborockVacuum(attributes9) || isXiaomiMiotVacuum(attributes9)) {
169954
- logger235.debug(
170611
+ logger236.debug(
169955
170612
  `Using vacuum.send_command with app_segment_clean for room ${room.name} (commandId: ${commandId3}, id: ${room.id})`
169956
170613
  );
169957
170614
  return {
@@ -169964,7 +170621,7 @@ var vacuumRvcRunModeConfig = {
169964
170621
  }
169965
170622
  if (isEcovacsVacuum(attributes9)) {
169966
170623
  const roomIdStr = String(commandId3);
169967
- logger235.info(
170624
+ logger236.info(
169968
170625
  `Ecovacs vacuum: Using spot_area for room ${room.name} (id: ${roomIdStr})`
169969
170626
  );
169970
170627
  return {
@@ -169979,7 +170636,7 @@ var vacuumRvcRunModeConfig = {
169979
170636
  }
169980
170637
  };
169981
170638
  }
169982
- logger235.warn(
170639
+ logger236.warn(
169983
170640
  `Room cleaning via send_command not supported for this vacuum type. Room: ${room.name} (id=${commandId3}). Falling back to vacuum.start`
169984
170641
  );
169985
170642
  }
@@ -169996,20 +170653,20 @@ function createVacuumRvcRunModeServer(attributes9, includeUnnamedRooms = false,
169996
170653
  customAreas,
169997
170654
  disableRoomModes
169998
170655
  );
169999
- logger235.info(
170656
+ logger236.info(
170000
170657
  `Creating VacuumRvcRunModeServer with ${rooms.length} rooms, ${supportedModes2.length} total modes`
170001
170658
  );
170002
170659
  if (rooms.length > 0) {
170003
- logger235.info(`Rooms found: ${rooms.map((r) => r.name).join(", ")}`);
170660
+ logger236.info(`Rooms found: ${rooms.map((r) => r.name).join(", ")}`);
170004
170661
  }
170005
170662
  if (filteredCount > 0) {
170006
170663
  const filtered = allRooms.filter((r) => !rooms.some((x) => x.id === r.id));
170007
- logger235.info(
170664
+ logger236.info(
170008
170665
  `Filtered out ${filteredCount} unnamed room(s): ${filtered.map((r) => r.name).join(", ")}`
170009
170666
  );
170010
170667
  }
170011
170668
  if (allRooms.length === 0) {
170012
- logger235.debug(
170669
+ logger236.debug(
170013
170670
  `No rooms found. Attributes: rooms=${JSON.stringify(attributes9.rooms)}, segments=${JSON.stringify(attributes9.segments)}, room_list=${attributes9.room_list}`
170014
170671
  );
170015
170672
  }
@@ -170043,7 +170700,7 @@ function createCleanAreaRvcRunModeServer(cleanAreaRooms) {
170043
170700
  modeTags: [{ value: RvcRunMode3.ModeTag.Cleaning }]
170044
170701
  });
170045
170702
  }
170046
- logger235.info(
170703
+ logger236.info(
170047
170704
  `Creating CLEAN_AREA RvcRunModeServer with ${cleanAreaRooms.length} HA areas, ${modes.length} total modes`
170048
170705
  );
170049
170706
  return RvcRunModeServer2(vacuumRvcRunModeConfig, {
@@ -170133,7 +170790,7 @@ init_nodejs();
170133
170790
 
170134
170791
  // src/matter/behaviors/rvc-clean-mode-server.ts
170135
170792
  init_home_assistant_entity_behavior();
170136
- var logger236 = Logger.get("RvcCleanModeServerBase");
170793
+ var logger237 = Logger.get("RvcCleanModeServerBase");
170137
170794
  var RvcCleanModeServerBase = class _RvcCleanModeServerBase extends RvcCleanModeServer {
170138
170795
  // Pending mode from a recent changeToMode command.
170139
170796
  // Prevents stale HA state (from a different entity like select.xxx)
@@ -170180,14 +170837,14 @@ var RvcCleanModeServerBase = class _RvcCleanModeServerBase extends RvcCleanModeS
170180
170837
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
170181
170838
  const { newMode } = request;
170182
170839
  if (newMode !== this.state.currentMode && !this.state.supportedModes.some((m) => m.mode === newMode)) {
170183
- logger236.warn(`changeToMode(${newMode}) rejected: unsupported mode`);
170840
+ logger237.warn(`changeToMode(${newMode}) rejected: unsupported mode`);
170184
170841
  return {
170185
170842
  status: ModeBase3.ModeChangeStatus.UnsupportedMode,
170186
170843
  statusText: `Unsupported mode: ${newMode}`
170187
170844
  };
170188
170845
  }
170189
170846
  const modeLabel = this.state.supportedModes.find((m) => m.mode === newMode);
170190
- logger236.info(
170847
+ logger237.info(
170191
170848
  `changeToMode(${newMode}) "${modeLabel?.label ?? "unknown"}" for ${homeAssistant.entityId}`
170192
170849
  );
170193
170850
  this.pendingMode = newMode;
@@ -170195,7 +170852,7 @@ var RvcCleanModeServerBase = class _RvcCleanModeServerBase extends RvcCleanModeS
170195
170852
  this.state.currentMode = newMode;
170196
170853
  const action = this.state.config.setCleanMode(newMode, this.agent);
170197
170854
  if (action) {
170198
- logger236.info(
170855
+ logger237.info(
170199
170856
  `changeToMode: dispatching action ${action.action} \u2192 ${action.target ?? homeAssistant.entityId}`
170200
170857
  );
170201
170858
  homeAssistant.callAction(action);
@@ -170228,7 +170885,7 @@ function RvcCleanModeServer2(config11, initialState) {
170228
170885
  }
170229
170886
 
170230
170887
  // src/matter/endpoints/legacy/vacuum/behaviors/vacuum-rvc-clean-mode-server.ts
170231
- var logger237 = Logger.get("VacuumRvcCleanModeServer");
170888
+ var logger238 = Logger.get("VacuumRvcCleanModeServer");
170232
170889
  var MODE_VACUUM = 0;
170233
170890
  var MODE_VACUUM_AND_MOP = 1;
170234
170891
  var MODE_MOP = 2;
@@ -170498,7 +171155,7 @@ function findMatchingCleanOption(ct, availableOptions) {
170498
171155
  const match = availableOptions.find((o) => classifyCleanOption(o) === type);
170499
171156
  if (match) return match;
170500
171157
  }
170501
- logger237.warn(
171158
+ logger238.warn(
170502
171159
  `No match for ${CLEAN_TYPE_LABELS[ct]} in [${availableOptions.join(", ")}]`
170503
171160
  );
170504
171161
  return availableOptions[0];
@@ -170507,7 +171164,7 @@ function buildCleaningModeAction(targetCleanType, agent) {
170507
171164
  const selectEntityId = getCleaningModeSelectEntity(agent);
170508
171165
  const { options } = readSelectEntity(selectEntityId, agent);
170509
171166
  const optionToUse = findMatchingCleanOption(targetCleanType, options);
170510
- logger237.info(
171167
+ logger238.info(
170511
171168
  `Switching cleaning mode to: ${optionToUse} via ${selectEntityId}`
170512
171169
  );
170513
171170
  return {
@@ -170596,7 +171253,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170596
171253
  }
170597
171254
  }
170598
171255
  if (speedMode !== void 0) {
170599
- logger237.debug(
171256
+ logger238.debug(
170600
171257
  `Current mode: Vacuum + fan_speed="${speedState}" -> mode ${speedMode}`
170601
171258
  );
170602
171259
  return speedMode;
@@ -170617,7 +171274,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170617
171274
  }
170618
171275
  }
170619
171276
  if (mopMode !== void 0) {
170620
- logger237.debug(
171277
+ logger238.debug(
170621
171278
  `Current mode: Mop + intensity="${state}" -> mode ${mopMode}`
170622
171279
  );
170623
171280
  return mopMode;
@@ -170635,14 +171292,14 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170635
171292
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
170636
171293
  const vacuumEntityId = homeAssistant.entityId;
170637
171294
  const mapping = homeAssistant.state.mapping;
170638
- logger237.info(
171295
+ logger238.info(
170639
171296
  `setCleanMode(${mode}) for ${vacuumEntityId}, suctionEntity=${mapping?.suctionLevelEntity ?? "none"}, mopEntity=${mapping?.mopIntensityEntity ?? "none"}, fanSpeedList=${JSON.stringify(fanSpeedList ?? [])}, mopIntensityList=${JSON.stringify(mopIntensityList ?? [])}, customTags=${JSON.stringify(customFanSpeedTags ?? {})}`
170640
171297
  );
170641
171298
  if (mopIntensityList && mopIntensityList.length > 0 && isMopIntensityMode(mode)) {
170642
171299
  const mopIndex = mode - MOP_INTENSITY_MODE_BASE;
170643
171300
  const mopName = mopIntensityList[mopIndex];
170644
171301
  if (!mopName) {
170645
- logger237.warn(`Invalid mop intensity mode index: ${mopIndex}`);
171302
+ logger238.warn(`Invalid mop intensity mode index: ${mopIndex}`);
170646
171303
  return void 0;
170647
171304
  }
170648
171305
  if (hasCleanTypes) {
@@ -170655,18 +171312,18 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170655
171312
  mapping.mopIntensityEntity,
170656
171313
  agent
170657
171314
  );
170658
- logger237.info(
171315
+ logger238.info(
170659
171316
  `Mop intensity entity ${mapping.mopIntensityEntity}: current="${state}", options=${JSON.stringify(options ?? [])}`
170660
171317
  );
170661
171318
  let option = matchMopIntensityOption(mopName, options);
170662
171319
  if (!option && options && mopIndex < options.length) {
170663
171320
  option = options[mopIndex];
170664
- logger237.info(
171321
+ logger238.info(
170665
171322
  `Positional match for mop "${mopName}" -> "${option}" (index ${mopIndex})`
170666
171323
  );
170667
171324
  }
170668
171325
  if (option) {
170669
- logger237.info(
171326
+ logger238.info(
170670
171327
  `Setting mop intensity to: ${option} via ${mapping.mopIntensityEntity}`
170671
171328
  );
170672
171329
  return {
@@ -170675,11 +171332,11 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170675
171332
  target: mapping.mopIntensityEntity
170676
171333
  };
170677
171334
  }
170678
- logger237.warn(
171335
+ logger238.warn(
170679
171336
  `No match for mop intensity "${mopName}" in options: [${(options ?? []).join(", ")}]`
170680
171337
  );
170681
171338
  } else {
170682
- logger237.warn(
171339
+ logger238.warn(
170683
171340
  `Mop intensity mode ${mode} requested but no mopIntensityEntity configured`
170684
171341
  );
170685
171342
  }
@@ -170689,7 +171346,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170689
171346
  const fanSpeedIndex = mode - FAN_SPEED_MODE_BASE;
170690
171347
  const fanSpeedName = fanSpeedList[fanSpeedIndex];
170691
171348
  if (!fanSpeedName) {
170692
- logger237.warn(`Invalid fan speed mode index: ${fanSpeedIndex}`);
171349
+ logger238.warn(`Invalid fan speed mode index: ${fanSpeedIndex}`);
170693
171350
  return void 0;
170694
171351
  }
170695
171352
  if (mapping?.suctionLevelEntity) {
@@ -170702,7 +171359,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170702
171359
  mapping.suctionLevelEntity,
170703
171360
  agent
170704
171361
  );
170705
- logger237.info(
171362
+ logger238.info(
170706
171363
  `Suction entity ${mapping.suctionLevelEntity}: current="${state}", options=${JSON.stringify(options ?? [])}`
170707
171364
  );
170708
171365
  let option = matchFanSpeedOption(
@@ -170712,12 +171369,12 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170712
171369
  );
170713
171370
  if (!option && options && fanSpeedIndex < options.length) {
170714
171371
  option = options[fanSpeedIndex];
170715
- logger237.info(
171372
+ logger238.info(
170716
171373
  `Positional match for fan "${fanSpeedName}" -> "${option}" (index ${fanSpeedIndex})`
170717
171374
  );
170718
171375
  }
170719
171376
  if (option) {
170720
- logger237.info(
171377
+ logger238.info(
170721
171378
  `Setting suction to: ${option} via ${mapping.suctionLevelEntity}`
170722
171379
  );
170723
171380
  return {
@@ -170726,7 +171383,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170726
171383
  target: mapping.suctionLevelEntity
170727
171384
  };
170728
171385
  }
170729
- logger237.warn(
171386
+ logger238.warn(
170730
171387
  `No match for fan speed "${fanSpeedName}" in suction options: [${(options ?? []).join(", ")}]`
170731
171388
  );
170732
171389
  return void 0;
@@ -170736,7 +171393,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170736
171393
  buildCleaningModeAction(0 /* Sweeping */, agent)
170737
171394
  );
170738
171395
  }
170739
- logger237.info(
171396
+ logger238.info(
170740
171397
  `Setting fan speed to: ${fanSpeedName} via vacuum.set_fan_speed`
170741
171398
  );
170742
171399
  return {
@@ -170746,7 +171403,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170746
171403
  };
170747
171404
  }
170748
171405
  if (!hasCleanTypes) {
170749
- logger237.debug(
171406
+ logger238.debug(
170750
171407
  `Ignoring cleaning type change (mode=${mode}): no cleaning mode entity`
170751
171408
  );
170752
171409
  return void 0;
@@ -170758,7 +171415,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
170758
171415
  agent
170759
171416
  );
170760
171417
  const optionToUse = findMatchingCleanOption(cleanType, availableOptions);
170761
- logger237.info(
171418
+ logger238.info(
170762
171419
  `Setting cleaning mode to: ${optionToUse} (mode=${mode}) via ${selectEntityId}`
170763
171420
  );
170764
171421
  return {
@@ -170776,10 +171433,10 @@ function createVacuumRvcCleanModeServer(_attributes, fanSpeedList, mopIntensityL
170776
171433
  cleaningModeOptions,
170777
171434
  customFanSpeedTags
170778
171435
  );
170779
- logger237.info(
171436
+ logger238.info(
170780
171437
  `Creating VacuumRvcCleanModeServer with ${supportedModes2.length} modes (fanSpeedList=${JSON.stringify(fanSpeedList ?? [])}, mopIntensityList=${JSON.stringify(mopIntensityList ?? [])}, cleaningModeOptions=${JSON.stringify(cleaningModeOptions ?? [])}, customTags=${JSON.stringify(customFanSpeedTags ?? {})})`
170781
171438
  );
170782
- logger237.info(
171439
+ logger238.info(
170783
171440
  `Modes: ${supportedModes2.map((m) => `${m.mode}:${m.label}[${m.modeTags.map((t) => t.value).join(",")}]`).join(", ")}`
170784
171441
  );
170785
171442
  const initialState = {
@@ -170842,7 +171499,7 @@ function resolveMopIntensityList(mopIntensityEntity) {
170842
171499
  init_dist();
170843
171500
  init_esm();
170844
171501
  init_home_assistant_entity_behavior();
170845
- var logger238 = Logger.get("VacuumRvcOperationalStateServer");
171502
+ var logger239 = Logger.get("VacuumRvcOperationalStateServer");
170846
171503
  function batteryFromAttributes(attrs) {
170847
171504
  const raw = attrs.battery_level ?? attrs.battery;
170848
171505
  if (raw == null) return null;
@@ -170893,16 +171550,16 @@ function mapVacuumOperationalState(entity, batteryPercent = batteryFromAttribute
170893
171550
  operationalState = RvcOperationalState4.OperationalState.Error;
170894
171551
  } else {
170895
171552
  if (state.toLowerCase().includes("clean")) {
170896
- logger238.info(
171553
+ logger239.info(
170897
171554
  `Unknown vacuum state "${state}" contains 'clean', treating as Running`
170898
171555
  );
170899
171556
  operationalState = RvcOperationalState4.OperationalState.Running;
170900
171557
  } else {
170901
- logger238.info(`Unknown vacuum state "${state}", treating as Stopped`);
171558
+ logger239.info(`Unknown vacuum state "${state}", treating as Stopped`);
170902
171559
  operationalState = RvcOperationalState4.OperationalState.Stopped;
170903
171560
  }
170904
171561
  }
170905
- logger238.debug(
171562
+ logger239.debug(
170906
171563
  `Vacuum operationalState: "${state}" -> ${RvcOperationalState4.OperationalState[operationalState]}`
170907
171564
  );
170908
171565
  return operationalState;
@@ -170931,7 +171588,7 @@ var VacuumRvcOperationalStateServer = RvcOperationalStateServer2({
170931
171588
  });
170932
171589
 
170933
171590
  // src/matter/endpoints/legacy/vacuum/index.ts
170934
- var logger239 = Logger.get("VacuumDevice");
171591
+ var logger240 = Logger.get("VacuumDevice");
170935
171592
  var VacuumEndpointType = RoboticVacuumCleanerDevice.with(
170936
171593
  BasicInformationServer2,
170937
171594
  VacuumIdentifyServer,
@@ -170945,7 +171602,7 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
170945
171602
  const entityId = homeAssistantEntity.entity.entity_id;
170946
171603
  const attributes9 = homeAssistantEntity.entity.state.attributes;
170947
171604
  const customAreas = homeAssistantEntity.mapping?.customServiceAreas;
170948
- logger239.info(
171605
+ logger240.info(
170949
171606
  `Creating vacuum endpoint for ${entityId}, mapping: ${JSON.stringify(homeAssistantEntity.mapping ?? "none")}`
170950
171607
  );
170951
171608
  const cleanAreaRooms = homeAssistantEntity.mapping?.cleanAreaRooms;
@@ -170958,32 +171615,32 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
170958
171615
  )
170959
171616
  ).set({ homeAssistantEntity });
170960
171617
  if (includeOnOff) {
170961
- logger239.info(`${entityId}: Adding OnOff cluster (vacuumOnOff flag enabled)`);
171618
+ logger240.info(`${entityId}: Adding OnOff cluster (vacuumOnOff flag enabled)`);
170962
171619
  device = device.with(VacuumOnOffServer);
170963
171620
  }
170964
171621
  device = device.with(VacuumPowerSourceServer);
170965
171622
  const roomEntities = homeAssistantEntity.mapping?.roomEntities;
170966
171623
  const rooms = parseVacuumRooms(attributes9);
170967
- logger239.info(
171624
+ logger240.info(
170968
171625
  `${entityId}: customAreas=${customAreas?.length ?? 0}, roomEntities=${JSON.stringify(roomEntities ?? [])}, parsedRooms=${rooms.length}, cleanAreaRooms=${cleanAreaRooms?.length ?? 0}`
170969
171626
  );
170970
171627
  if (cleanAreaRooms && cleanAreaRooms.length > 0) {
170971
- logger239.info(
171628
+ logger240.info(
170972
171629
  `${entityId}: Adding ServiceArea (${cleanAreaRooms.length} HA areas via CLEAN_AREA)`
170973
171630
  );
170974
171631
  device = device.with(createCleanAreaServiceAreaServer(cleanAreaRooms));
170975
171632
  } else if (customAreas && customAreas.length > 0) {
170976
- logger239.info(
171633
+ logger240.info(
170977
171634
  `${entityId}: Adding ServiceArea (${customAreas.length} custom areas)`
170978
171635
  );
170979
171636
  device = device.with(createCustomServiceAreaServer(customAreas));
170980
171637
  } else if (rooms.length > 0 || roomEntities && roomEntities.length > 0) {
170981
- logger239.info(`${entityId}: Adding ServiceArea (${rooms.length} rooms)`);
171638
+ logger240.info(`${entityId}: Adding ServiceArea (${rooms.length} rooms)`);
170982
171639
  device = device.with(
170983
171640
  createVacuumServiceAreaServer(attributes9, roomEntities)
170984
171641
  );
170985
171642
  } else {
170986
- logger239.info(`${entityId}: Adding ServiceArea (default single-area)`);
171643
+ logger240.info(`${entityId}: Adding ServiceArea (default single-area)`);
170987
171644
  device = device.with(createDefaultServiceAreaServer());
170988
171645
  }
170989
171646
  const fanSpeedList = resolveFanSpeedList(
@@ -170994,7 +171651,7 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
170994
171651
  homeAssistantEntity.mapping?.mopIntensityEntity
170995
171652
  );
170996
171653
  if (cleaningModeOptions || fanSpeedList || mopIntensityList) {
170997
- logger239.info(
171654
+ logger240.info(
170998
171655
  `${entityId}: Adding RvcCleanMode (multi-mode, cleaningModeOptions=${JSON.stringify(cleaningModeOptions ?? [])}, fanSpeedList=${JSON.stringify(fanSpeedList ?? [])}, mopIntensityList=${JSON.stringify(mopIntensityList ?? [])})`
170999
171656
  );
171000
171657
  device = device.with(
@@ -171007,7 +171664,7 @@ function VacuumDevice(homeAssistantEntity, includeOnOff = false, cleaningModeOpt
171007
171664
  )
171008
171665
  );
171009
171666
  } else {
171010
- logger239.info(`${entityId}: Adding RvcCleanMode (default single-mode)`);
171667
+ logger240.info(`${entityId}: Adding RvcCleanMode (default single-mode)`);
171011
171668
  device = device.with(createDefaultRvcCleanModeServer());
171012
171669
  }
171013
171670
  return device;
@@ -171173,7 +171830,7 @@ var WaterHeaterThermostatServer = ThermostatServer2(
171173
171830
  );
171174
171831
 
171175
171832
  // src/matter/endpoints/legacy/water-heater/index.ts
171176
- var logger240 = Logger.get("WaterHeaterDevice");
171833
+ var logger241 = Logger.get("WaterHeaterDevice");
171177
171834
  var WaterHeaterDeviceType = ThermostatDevice.with(
171178
171835
  BasicInformationServer2,
171179
171836
  IdentifyServer2,
@@ -171189,7 +171846,7 @@ function toMatterTemp2(value) {
171189
171846
  }
171190
171847
  function WaterHeaterDevice(homeAssistantEntity) {
171191
171848
  const attributes9 = homeAssistantEntity.entity.state.attributes;
171192
- logger240.debug(
171849
+ logger241.debug(
171193
171850
  `Creating device for ${homeAssistantEntity.entity.entity_id}, min_temp=${attributes9.min_temp}, max_temp=${attributes9.max_temp}`
171194
171851
  );
171195
171852
  const minLimit = toMatterTemp2(attributes9.min_temp) ?? 0;
@@ -171429,6 +172086,7 @@ var matterDeviceTypeFactories = {
171429
172086
  // Legacy SolarPower alias, kept for existing mappings.
171430
172087
  electrical_sensor: (ha) => ElectricalSensorType.set({ homeAssistantEntity: ha }),
171431
172088
  solar_power: (ha) => ElectricalSensorType.set({ homeAssistantEntity: ha }),
172089
+ evse: (ha) => energyEvseType().set({ homeAssistantEntity: ha }),
171432
172090
  contact_sensor: (ha) => ContactSensorType.set({ homeAssistantEntity: ha }),
171433
172091
  motion_sensor: (ha) => MotionSensorType.set({ homeAssistantEntity: ha }),
171434
172092
  occupancy_sensor: (ha) => OccupancySensorType.set({ homeAssistantEntity: ha }),
@@ -171444,7 +172102,7 @@ var matterDeviceTypeFactories = {
171444
172102
  };
171445
172103
 
171446
172104
  // src/matter/endpoints/composed/user-composed-endpoint.ts
171447
- var logger241 = Logger.get("UserComposedEndpoint");
172105
+ var logger242 = Logger.get("UserComposedEndpoint");
171448
172106
  function stripBasicInformation(type) {
171449
172107
  const behaviors = { ...type.behaviors };
171450
172108
  delete behaviors.bridgedDeviceBasicInformation;
@@ -171520,7 +172178,7 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
171520
172178
  { vacuumOnOff: registry2.isVacuumOnOffEnabled() }
171521
172179
  );
171522
172180
  if (!primaryType) {
171523
- logger241.warn(
172181
+ logger242.warn(
171524
172182
  `Cannot create endpoint type for primary entity ${primaryEntityId}`
171525
172183
  );
171526
172184
  return void 0;
@@ -171535,7 +172193,7 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
171535
172193
  if (!sub.entityId) continue;
171536
172194
  const subPayload = buildEntityPayload4(registry2, sub.entityId);
171537
172195
  if (!subPayload) {
171538
- logger241.warn(
172196
+ logger242.warn(
171539
172197
  `Cannot find state for composed sub-entity ${sub.entityId}, it does not exist in Home Assistant (removed or renamed?)`
171540
172198
  );
171541
172199
  continue;
@@ -171550,7 +172208,7 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
171550
172208
  config11.areaName
171551
172209
  );
171552
172210
  if (!subType) {
171553
- logger241.warn(
172211
+ logger242.warn(
171554
172212
  `Cannot create endpoint type for composed sub-entity ${sub.entityId}`
171555
172213
  );
171556
172214
  continue;
@@ -171563,7 +172221,7 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
171563
172221
  mappedIds.push(sub.entityId);
171564
172222
  }
171565
172223
  if (parts.length < 2) {
171566
- logger241.warn(
172224
+ logger242.warn(
171567
172225
  `User composed device ${primaryEntityId}: only ${parts.length} sub-endpoint(s), need at least 2 (primary + one sub-entity). Falling back to standalone.`
171568
172226
  );
171569
172227
  return void 0;
@@ -171587,7 +172245,7 @@ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
171587
172245
  const labels = parts.map(
171588
172246
  (_, i) => i === 0 ? primaryEntityId.split(".")[0] : composedEntities[i - 1]?.entityId?.split(".")[0] ?? "?"
171589
172247
  ).join("+");
171590
- logger241.info(
172248
+ logger242.info(
171591
172249
  `Created user composed device ${primaryEntityId}: ${parts.length} sub-endpoint(s) [${labels}]`
171592
172250
  );
171593
172251
  return endpoint;
@@ -171693,7 +172351,7 @@ function asStandaloneEndpointType(type) {
171693
172351
  }
171694
172352
 
171695
172353
  // src/matter/endpoints/legacy/legacy-endpoint.ts
171696
- var logger242 = Logger.get("LegacyEndpoint");
172354
+ var logger243 = Logger.get("LegacyEndpoint");
171697
172355
  var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171698
172356
  static async create(registry2, entityId, mapping, pluginDomainMappings, standalone = false, endpointId, identityAnchor) {
171699
172357
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -171703,25 +172361,25 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171703
172361
  return;
171704
172362
  }
171705
172363
  if (registry2.isAutoBatteryMappingEnabled() && registry2.isBatteryEntityUsed(entityId)) {
171706
- logger242.debug(
172364
+ logger243.debug(
171707
172365
  `Skipping ${entityId} - already auto-assigned as battery to another device`
171708
172366
  );
171709
172367
  return;
171710
172368
  }
171711
172369
  if (registry2.isAutoHumidityMappingEnabled() && registry2.isHumidityEntityUsed(entityId)) {
171712
- logger242.debug(
172370
+ logger243.debug(
171713
172371
  `Skipping ${entityId} - already auto-assigned as humidity to a temperature sensor`
171714
172372
  );
171715
172373
  return;
171716
172374
  }
171717
172375
  if (registry2.isAutoPressureMappingEnabled() && registry2.isPressureEntityUsed(entityId)) {
171718
- logger242.debug(
172376
+ logger243.debug(
171719
172377
  `Skipping ${entityId} - already auto-assigned as pressure to a temperature sensor`
171720
172378
  );
171721
172379
  return;
171722
172380
  }
171723
172381
  if (registry2.isAutoComposedDevicesEnabled() && registry2.isComposedSubEntityUsed(entityId)) {
171724
- logger242.debug(
172382
+ logger243.debug(
171725
172383
  `Skipping ${entityId} - already consumed by a composed device`
171726
172384
  );
171727
172385
  return;
@@ -171741,7 +172399,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171741
172399
  humidityEntity: humidityEntityId
171742
172400
  };
171743
172401
  registry2.markHumidityEntityUsed(humidityEntityId);
171744
- logger242.debug(
172402
+ logger243.debug(
171745
172403
  `Auto-assigned humidity ${humidityEntityId} to ${entityId}`
171746
172404
  );
171747
172405
  }
@@ -171760,7 +172418,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171760
172418
  pressureEntity: pressureEntityId
171761
172419
  };
171762
172420
  registry2.markPressureEntityUsed(pressureEntityId);
171763
- logger242.debug(
172421
+ logger243.debug(
171764
172422
  `Auto-assigned pressure ${pressureEntityId} to ${entityId}`
171765
172423
  );
171766
172424
  }
@@ -171778,7 +172436,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171778
172436
  batteryEntity: batteryEntityId
171779
172437
  };
171780
172438
  registry2.markBatteryEntityUsed(batteryEntityId);
171781
- logger242.debug(
172439
+ logger243.debug(
171782
172440
  `Auto-assigned battery ${batteryEntityId} to ${entityId}`
171783
172441
  );
171784
172442
  }
@@ -171795,7 +172453,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171795
172453
  entityId: effectiveMapping?.entityId ?? entityId,
171796
172454
  faultEntity: faultEntityId
171797
172455
  };
171798
- logger242.debug(`Auto-assigned fault ${faultEntityId} to ${entityId}`);
172456
+ logger243.debug(`Auto-assigned fault ${faultEntityId} to ${entityId}`);
171799
172457
  }
171800
172458
  }
171801
172459
  if (!mapping?.powerEntity) {
@@ -171811,7 +172469,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171811
172469
  powerEntity: powerEntityId
171812
172470
  };
171813
172471
  registry2.markPowerEntityUsed(powerEntityId);
171814
- logger242.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
172472
+ logger243.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
171815
172473
  }
171816
172474
  }
171817
172475
  }
@@ -171828,7 +172486,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171828
172486
  energyEntity: energyEntityId
171829
172487
  };
171830
172488
  registry2.markEnergyEntityUsed(energyEntityId);
171831
- logger242.debug(
172489
+ logger243.debug(
171832
172490
  `Auto-assigned energy ${energyEntityId} to ${entityId}`
171833
172491
  );
171834
172492
  }
@@ -171844,7 +172502,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171844
172502
  entityId: effectiveMapping?.entityId ?? entityId,
171845
172503
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
171846
172504
  };
171847
- logger242.info(
172505
+ logger243.info(
171848
172506
  `Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity} to ${entityId}`
171849
172507
  );
171850
172508
  }
@@ -171854,7 +172512,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171854
172512
  entityId: effectiveMapping?.entityId ?? entityId,
171855
172513
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
171856
172514
  };
171857
- logger242.info(
172515
+ logger243.info(
171858
172516
  `Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity} to ${entityId}`
171859
172517
  );
171860
172518
  }
@@ -171864,7 +172522,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171864
172522
  entityId: effectiveMapping?.entityId ?? entityId,
171865
172523
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
171866
172524
  };
171867
- logger242.info(
172525
+ logger243.info(
171868
172526
  `Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity} to ${entityId}`
171869
172527
  );
171870
172528
  }
@@ -171874,7 +172532,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171874
172532
  entityId: effectiveMapping?.entityId ?? entityId,
171875
172533
  currentRoomEntity: vacuumEntities.currentRoomEntity
171876
172534
  };
171877
- logger242.info(
172535
+ logger243.info(
171878
172536
  `Auto-assigned currentRoom ${vacuumEntities.currentRoomEntity} to ${entityId}`
171879
172537
  );
171880
172538
  }
@@ -171889,7 +172547,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171889
172547
  entityId: effectiveMapping?.entityId ?? entityId,
171890
172548
  cleanAreaRooms
171891
172549
  };
171892
- logger242.info(
172550
+ logger243.info(
171893
172551
  `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA for ${entityId}`
171894
172552
  );
171895
172553
  }
@@ -171910,7 +172568,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171910
172568
  rooms: roomsObj
171911
172569
  }
171912
172570
  };
171913
- logger242.debug(
172571
+ logger243.debug(
171914
172572
  `Auto-detected ${valetudoRooms.length} Valetudo segments for ${entityId}`
171915
172573
  );
171916
172574
  } else {
@@ -171927,7 +172585,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171927
172585
  rooms: roomsObj
171928
172586
  }
171929
172587
  };
171930
- logger242.debug(
172588
+ logger243.debug(
171931
172589
  `Auto-detected ${roborockRooms.length} Roborock rooms for ${entityId}`
171932
172590
  );
171933
172591
  }
@@ -171936,7 +172594,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171936
172594
  }
171937
172595
  }
171938
172596
  if (standalone && ((effectiveMapping?.composedEntities?.length ?? 0) > 0 || effectiveMapping?.climateExposeFan === true)) {
171939
- logger242.warn(
172597
+ logger243.warn(
171940
172598
  `Composed mappings are not supported in server mode, exposing ${entityId} as a flat standalone endpoint`
171941
172599
  );
171942
172600
  }
@@ -171955,7 +172613,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
171955
172613
  if (composed) {
171956
172614
  return composed;
171957
172615
  }
171958
- logger242.warn(
172616
+ logger243.warn(
171959
172617
  `User composed device creation failed for ${entityId}, falling back to standalone`
171960
172618
  );
171961
172619
  }
@@ -172020,7 +172678,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
172020
172678
  if (composed) {
172021
172679
  return composed;
172022
172680
  }
172023
- logger242.warn(
172681
+ logger243.warn(
172024
172682
  `Companion fan creation failed for ${entityId}, falling back to standalone`
172025
172683
  );
172026
172684
  }
@@ -172100,11 +172758,11 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
172100
172758
  }
172101
172759
  if (mappedChanged) {
172102
172760
  this.pendingMappedChange = true;
172103
- logger242.debug(
172761
+ logger243.debug(
172104
172762
  `Mapped entity change detected for ${this.entityId}, forcing update`
172105
172763
  );
172106
172764
  }
172107
- logger242.debug(
172765
+ logger243.debug(
172108
172766
  `State update received for ${this.entityId}: state=${state.state}`
172109
172767
  );
172110
172768
  this.lastState = state;
@@ -172683,7 +173341,7 @@ import {
172683
173341
  getCollection
172684
173342
  } from "home-assistant-js-websocket";
172685
173343
  import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js";
172686
- var logger243 = Logger.get("SubscribeEntities");
173344
+ var logger244 = Logger.get("SubscribeEntities");
172687
173345
  function processEvent(store, updates) {
172688
173346
  const state = { ...store.state };
172689
173347
  if (updates.a) {
@@ -172709,7 +173367,7 @@ function processEvent(store, updates) {
172709
173367
  for (const entityId in updates.c) {
172710
173368
  let entityState = state[entityId];
172711
173369
  if (!entityState) {
172712
- logger243.warn("Received state update for unknown entity", entityId);
173370
+ logger244.warn("Received state update for unknown entity", entityId);
172713
173371
  continue;
172714
173372
  }
172715
173373
  entityState = { ...entityState };
@@ -172798,7 +173456,7 @@ var subscribeEntities = (conn, onChange, entityIds) => {
172798
173456
  // src/services/bridges/entity-isolation-service.ts
172799
173457
  init_esm();
172800
173458
  init_diagnostic_event_bus();
172801
- var logger244 = Logger.get("EntityIsolation");
173459
+ var logger245 = Logger.get("EntityIsolation");
172802
173460
  var EntityIsolationServiceImpl = class {
172803
173461
  isolatedEntities = /* @__PURE__ */ new Map();
172804
173462
  isolationCallbacks = /* @__PURE__ */ new Map();
@@ -172863,13 +173521,13 @@ var EntityIsolationServiceImpl = class {
172863
173521
  }
172864
173522
  const parsed = this.parseEndpointPath(msg);
172865
173523
  if (!parsed) {
172866
- logger244.warn("Could not parse entity from error:", msg);
173524
+ logger245.warn("Could not parse entity from error:", msg);
172867
173525
  return false;
172868
173526
  }
172869
173527
  const { bridgeId, entityName } = parsed;
172870
173528
  const callback = this.isolationCallbacks.get(bridgeId);
172871
173529
  if (!callback) {
172872
- logger244.warn(
173530
+ logger245.warn(
172873
173531
  `No isolation callback registered for bridge ${bridgeId}, entity: ${entityName}`
172874
173532
  );
172875
173533
  return false;
@@ -172884,7 +173542,7 @@ var EntityIsolationServiceImpl = class {
172884
173542
  reason,
172885
173543
  failedAt: (/* @__PURE__ */ new Date()).toISOString()
172886
173544
  });
172887
- logger244.warn(
173545
+ logger245.warn(
172888
173546
  `Isolating entity "${entityName}" from bridge ${bridgeId} due to: ${reason}`
172889
173547
  );
172890
173548
  diagnosticEventBus.emit("entity_error", `Entity isolated: ${entityName}`, {
@@ -172896,7 +173554,7 @@ var EntityIsolationServiceImpl = class {
172896
173554
  await callback(entityName);
172897
173555
  return true;
172898
173556
  } catch (e) {
172899
- logger244.error(`Failed to isolate entity ${entityName}:`, e);
173557
+ logger245.error(`Failed to isolate entity ${entityName}:`, e);
172900
173558
  return false;
172901
173559
  }
172902
173560
  }
@@ -172925,119 +173583,6 @@ var EntityIsolationServiceImpl = class {
172925
173583
  };
172926
173584
  var EntityIsolationService = new EntityIsolationServiceImpl();
172927
173585
 
172928
- // src/services/bridges/identity-resolver.ts
172929
- var SEP = "\0";
172930
- function identityKey(entity) {
172931
- const uniqueId = entity.registry?.unique_id;
172932
- const platform = entity.registry?.platform;
172933
- if (!uniqueId || !platform) {
172934
- return null;
172935
- }
172936
- const domain = entity.entity_id.split(".")[0];
172937
- return platform + SEP + domain + SEP + uniqueId;
172938
- }
172939
- var IdentityResolver = class {
172940
- constructor(identityStorage, mappingStorage) {
172941
- this.identityStorage = identityStorage;
172942
- this.mappingStorage = mappingStorage;
172943
- }
172944
- identityStorage;
172945
- mappingStorage;
172946
- async resolveIdentity(bridgeId, entity, mapping, opts) {
172947
- const entityId = entity.entity_id;
172948
- const key = identityKey(entity);
172949
- if (key == null) {
172950
- return {
172951
- endpointId: createEndpointId(entityId, mapping?.customName),
172952
- anchorEntityId: entityId,
172953
- protected: false
172954
- };
172955
- }
172956
- const existing = this.identityStorage.getIdentity(bridgeId, key);
172957
- if (existing) {
172958
- const renamedFrom = existing.lastEntityId !== entityId ? existing.lastEntityId ?? existing.anchorEntityId : void 0;
172959
- if (opts.stableIdentity) {
172960
- if (renamedFrom) {
172961
- this.identityStorage.setIdentity(bridgeId, key, {
172962
- ...existing,
172963
- lastEntityId: entityId
172964
- });
172965
- await this.rekeyMapping(bridgeId, renamedFrom, entityId);
172966
- }
172967
- return {
172968
- endpointId: existing.endpointId,
172969
- anchorEntityId: existing.anchorEntityId,
172970
- protected: true,
172971
- renamedFrom
172972
- };
172973
- }
172974
- const liveEndpointId = createEndpointId(entityId, mapping?.customName);
172975
- if (existing.endpointId !== liveEndpointId || existing.anchorEntityId !== entityId || existing.lastEntityId !== entityId) {
172976
- this.identityStorage.setIdentity(bridgeId, key, {
172977
- ...existing,
172978
- endpointId: liveEndpointId,
172979
- anchorEntityId: entityId,
172980
- lastEntityId: entityId
172981
- });
172982
- }
172983
- return {
172984
- endpointId: liveEndpointId,
172985
- anchorEntityId: entityId,
172986
- protected: false,
172987
- renamedFrom
172988
- };
172989
- }
172990
- const desired = createEndpointId(entityId, mapping?.customName);
172991
- let endpointId = desired;
172992
- if (opts.stableIdentity) {
172993
- let n = 2;
172994
- while (opts.isEndpointIdTaken(endpointId, key)) {
172995
- endpointId = `${desired}_${n++}`;
172996
- }
172997
- }
172998
- const record = {
172999
- endpointId,
173000
- anchorEntityId: entityId,
173001
- lastEntityId: entityId,
173002
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
173003
- };
173004
- this.identityStorage.setIdentity(bridgeId, key, record);
173005
- if (opts.stableIdentity) {
173006
- return {
173007
- endpointId: record.endpointId,
173008
- anchorEntityId: record.anchorEntityId,
173009
- protected: true
173010
- };
173011
- }
173012
- return {
173013
- endpointId: desired,
173014
- anchorEntityId: entityId,
173015
- protected: false
173016
- };
173017
- }
173018
- // Carry a mapping from the old entity_id to the new one on rename. anchorEntityId
173019
- // never changes, only the mapping key. Skips when no old mapping exists or a new
173020
- // one already does, so a manual mapping under the new id is never clobbered.
173021
- async rekeyMapping(bridgeId, oldEntityId, newEntityId) {
173022
- if (oldEntityId === newEntityId) {
173023
- return;
173024
- }
173025
- const old = this.mappingStorage.getMapping(bridgeId, oldEntityId);
173026
- if (!old) {
173027
- return;
173028
- }
173029
- if (this.mappingStorage.getMapping(bridgeId, newEntityId)) {
173030
- return;
173031
- }
173032
- await this.mappingStorage.setMapping({
173033
- ...old,
173034
- bridgeId,
173035
- entityId: newEntityId
173036
- });
173037
- await this.mappingStorage.deleteMapping(bridgeId, oldEntityId);
173038
- }
173039
- };
173040
-
173041
173586
  // src/services/bridges/bridge-endpoint-manager.ts
173042
173587
  var MAX_ENTITY_ID_LENGTH = 150;
173043
173588
  var ENDPOINT_REMOVAL_GRACE_MS = 6e4;
@@ -173047,6 +173592,7 @@ var BridgeEndpointManager = class extends Service {
173047
173592
  this.client = client;
173048
173593
  this.registry = registry2;
173049
173594
  this.mappingStorage = mappingStorage;
173595
+ this.identityStorage = identityStorage;
173050
173596
  this.bridgeId = bridgeId;
173051
173597
  this.log = log;
173052
173598
  this.pluginManager = pluginManager;
@@ -173068,6 +173614,7 @@ var BridgeEndpointManager = class extends Service {
173068
173614
  client;
173069
173615
  registry;
173070
173616
  mappingStorage;
173617
+ identityStorage;
173071
173618
  bridgeId;
173072
173619
  log;
173073
173620
  pluginManager;
@@ -173465,6 +174012,17 @@ var BridgeEndpointManager = class extends Service {
173465
174012
  this.mappingFingerprints.set(entityId, fp);
173466
174013
  }
173467
174014
  }
174015
+ const fullEntities = this.registry.fullEntities;
174016
+ stampIdentityPresence(
174017
+ this.identityStorage,
174018
+ this.bridgeId,
174019
+ buildPresentIdentityKeys(fullEntities)
174020
+ );
174021
+ stampMappingPresence(
174022
+ this.mappingStorage,
174023
+ this.bridgeId,
174024
+ buildPresentEntityIds(fullEntities)
174025
+ );
173468
174026
  const existingEndpoints = [];
173469
174027
  const now = Date.now();
173470
174028
  for (const endpoint of endpoints) {
@@ -173784,6 +174342,11 @@ var BridgeRegistry = class _BridgeRegistry {
173784
174342
  initialState(entityId) {
173785
174343
  return this._states[entityId];
173786
174344
  }
174345
+ // The complete HA entity set (unfiltered). Used by orphan tombstone stamping
174346
+ // so a filter change or a scope narrowing never looks like a removal.
174347
+ get fullEntities() {
174348
+ return this.registry.entities;
174349
+ }
173787
174350
  // composed sub-entities may sit outside the bridge filter (#408), so these
173788
174351
  // fall back to the full HA registry. keep them separate from the strict
173789
174352
  // accessors above, every other caller must stay filtered.
@@ -174509,11 +175072,11 @@ function makeWarmStartState(state, now = (/* @__PURE__ */ new Date()).toISOStrin
174509
175072
  return { ...state, last_updated: now };
174510
175073
  }
174511
175074
  var ServerModeBridge = class {
174512
- constructor(logger247, dataProvider, endpointManager, server) {
175075
+ constructor(logger248, dataProvider, endpointManager, server) {
174513
175076
  this.dataProvider = dataProvider;
174514
175077
  this.endpointManager = endpointManager;
174515
175078
  this.server = server;
174516
- this.log = logger247.get(`ServerModeBridge / ${dataProvider.id}`);
175079
+ this.log = logger248.get(`ServerModeBridge / ${dataProvider.id}`);
174517
175080
  }
174518
175081
  dataProvider;
174519
175082
  endpointManager;
@@ -175541,7 +176104,7 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
175541
176104
  }
175542
176105
 
175543
176106
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
175544
- var logger245 = Logger.get("ServerModeVacuumEndpoint");
176107
+ var logger246 = Logger.get("ServerModeVacuumEndpoint");
175545
176108
  var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
175546
176109
  static async create(registry2, entityId, mapping, endpointId) {
175547
176110
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -175554,7 +176117,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175554
176117
  if (mapping?.disableBatteryMapping && mapping.batteryEntity) {
175555
176118
  effectiveMapping = { ...mapping, batteryEntity: void 0 };
175556
176119
  }
175557
- logger245.info(
176120
+ logger246.info(
175558
176121
  `${entityId}: device_id=${entity.device_id}, manualBattery=${mapping?.batteryEntity ?? "none"}`
175559
176122
  );
175560
176123
  if (entity.device_id) {
@@ -175569,15 +176132,15 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175569
176132
  batteryEntity: batteryEntityId
175570
176133
  };
175571
176134
  registry2.markBatteryEntityUsed(batteryEntityId);
175572
- logger245.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
176135
+ logger246.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
175573
176136
  } else {
175574
176137
  const attrs = state.attributes;
175575
176138
  if (attrs.battery_level != null || attrs.battery != null) {
175576
- logger245.info(
176139
+ logger246.info(
175577
176140
  `${entityId}: No battery entity found, using battery attribute from vacuum state`
175578
176141
  );
175579
176142
  } else {
175580
- logger245.warn(
176143
+ logger246.warn(
175581
176144
  `${entityId}: No battery entity found for device ${entity.device_id}`
175582
176145
  );
175583
176146
  }
@@ -175592,7 +176155,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175592
176155
  entityId: effectiveMapping?.entityId ?? entityId,
175593
176156
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
175594
176157
  };
175595
- logger245.info(
176158
+ logger246.info(
175596
176159
  `${entityId}: Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity}`
175597
176160
  );
175598
176161
  }
@@ -175602,7 +176165,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175602
176165
  entityId: effectiveMapping?.entityId ?? entityId,
175603
176166
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
175604
176167
  };
175605
- logger245.info(
176168
+ logger246.info(
175606
176169
  `${entityId}: Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity}`
175607
176170
  );
175608
176171
  }
@@ -175612,7 +176175,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175612
176175
  entityId: effectiveMapping?.entityId ?? entityId,
175613
176176
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
175614
176177
  };
175615
- logger245.info(
176178
+ logger246.info(
175616
176179
  `${entityId}: Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity}`
175617
176180
  );
175618
176181
  }
@@ -175622,7 +176185,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175622
176185
  entityId: effectiveMapping?.entityId ?? entityId,
175623
176186
  currentRoomEntity: vacuumEntities.currentRoomEntity
175624
176187
  };
175625
- logger245.info(
176188
+ logger246.info(
175626
176189
  `${entityId}: Auto-assigned currentRoom ${vacuumEntities.currentRoomEntity}`
175627
176190
  );
175628
176191
  }
@@ -175637,7 +176200,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175637
176200
  entityId: effectiveMapping?.entityId ?? entityId,
175638
176201
  cleanAreaRooms
175639
176202
  };
175640
- logger245.info(
176203
+ logger246.info(
175641
176204
  `${entityId}: Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA`
175642
176205
  );
175643
176206
  }
@@ -175658,7 +176221,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175658
176221
  rooms: roomsObj
175659
176222
  }
175660
176223
  };
175661
- logger245.info(
176224
+ logger246.info(
175662
176225
  `${entityId}: Auto-detected ${valetudoRooms.length} Valetudo segments`
175663
176226
  );
175664
176227
  } else {
@@ -175675,14 +176238,14 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175675
176238
  rooms: roomsObj
175676
176239
  }
175677
176240
  };
175678
- logger245.info(
176241
+ logger246.info(
175679
176242
  `${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
175680
176243
  );
175681
176244
  }
175682
176245
  }
175683
176246
  }
175684
176247
  } else {
175685
- logger245.warn(`${entityId}: No device_id, cannot auto-assign battery`);
176248
+ logger246.warn(`${entityId}: No device_id, cannot auto-assign battery`);
175686
176249
  }
175687
176250
  const payload = {
175688
176251
  entity_id: entityId,
@@ -175755,11 +176318,11 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
175755
176318
  }
175756
176319
  if (mappedChanged) {
175757
176320
  this.pendingMappedChange = true;
175758
- logger245.debug(
176321
+ logger246.debug(
175759
176322
  `Mapped entity change detected for ${this.entityId}, forcing update`
175760
176323
  );
175761
176324
  }
175762
- logger245.debug(
176325
+ logger246.debug(
175763
176326
  `State update received for ${this.entityId}: state=${state.state}`
175764
176327
  );
175765
176328
  this.lastState = state;
@@ -175805,6 +176368,7 @@ var ServerModeEndpointManager = class extends Service {
175805
176368
  this.client = client;
175806
176369
  this.registry = registry2;
175807
176370
  this.mappingStorage = mappingStorage;
176371
+ this.identityStorage = identityStorage;
175808
176372
  this.dataProvider = dataProvider;
175809
176373
  this.log = log;
175810
176374
  this.identityResolver = new IdentityResolver(
@@ -175816,6 +176380,7 @@ var ServerModeEndpointManager = class extends Service {
175816
176380
  client;
175817
176381
  registry;
175818
176382
  mappingStorage;
176383
+ identityStorage;
175819
176384
  dataProvider;
175820
176385
  log;
175821
176386
  entityIds = [];
@@ -175915,6 +176480,17 @@ var ServerModeEndpointManager = class extends Service {
175915
176480
  this.registry.refresh();
175916
176481
  this._failedEntities = [];
175917
176482
  this.entityIds = this.registry.entityIds;
176483
+ const fullEntities = this.registry.fullEntities;
176484
+ stampIdentityPresence(
176485
+ this.identityStorage,
176486
+ this.dataProvider.id,
176487
+ buildPresentIdentityKeys(fullEntities)
176488
+ );
176489
+ stampMappingPresence(
176490
+ this.mappingStorage,
176491
+ this.dataProvider.id,
176492
+ buildPresentEntityIds(fullEntities)
176493
+ );
175918
176494
  try {
175919
176495
  if (this.entityIds.length === 0) {
175920
176496
  this.log.warn("Server mode bridge has no entities configured");
@@ -176324,10 +176900,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
176324
176900
  // src/core/ioc/app-environment.ts
176325
176901
  var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176326
176902
  constructor(rootEnv, options) {
176327
- const logger247 = rootEnv.get(LoggerService);
176903
+ const logger248 = rootEnv.get(LoggerService);
176328
176904
  super({
176329
176905
  id: "App",
176330
- log: logger247.get("AppContainer"),
176906
+ log: logger248.get("AppContainer"),
176331
176907
  parent: rootEnv
176332
176908
  });
176333
176909
  this.options = options;
@@ -176341,8 +176917,8 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176341
176917
  }
176342
176918
  construction;
176343
176919
  async init() {
176344
- const logger247 = this.get(LoggerService);
176345
- this.set(LoggerService, logger247);
176920
+ const logger248 = this.get(LoggerService);
176921
+ this.set(LoggerService, logger248);
176346
176922
  this.set(AppStorage, new AppStorage(await this.load(StorageService)));
176347
176923
  this.set(BridgeStorage, new BridgeStorage(await this.load(AppStorage)));
176348
176924
  this.set(
@@ -176363,7 +176939,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176363
176939
  );
176364
176940
  this.set(
176365
176941
  HomeAssistantClient,
176366
- new HomeAssistantClient(logger247, this.options.homeAssistant)
176942
+ new HomeAssistantClient(logger248, this.options.homeAssistant)
176367
176943
  );
176368
176944
  this.set(
176369
176945
  HomeAssistantConfig,
@@ -176371,7 +176947,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176371
176947
  );
176372
176948
  this.set(
176373
176949
  HomeAssistantActions,
176374
- new HomeAssistantActions(logger247, await this.load(HomeAssistantClient))
176950
+ new HomeAssistantActions(logger248, await this.load(HomeAssistantClient))
176375
176951
  );
176376
176952
  this.set(
176377
176953
  HomeAssistantRegistry,
@@ -176407,7 +176983,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176407
176983
  this.set(
176408
176984
  WebApi,
176409
176985
  new WebApi(
176410
- logger247,
176986
+ logger248,
176411
176987
  await this.load(BridgeService),
176412
176988
  await this.load(HomeAssistantClient),
176413
176989
  await this.load(HomeAssistantRegistry),
@@ -176428,7 +177004,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176428
177004
 
176429
177005
  // src/matter/patches/patch-level-control-tlv.ts
176430
177006
  init_esm();
176431
- var logger246 = Logger.get("PatchLevelControlTlv");
177007
+ var logger247 = Logger.get("PatchLevelControlTlv");
176432
177008
  var optionalFieldModels = /* @__PURE__ */ new WeakSet();
176433
177009
  var fieldModelMandatoryGetterPatched = false;
176434
177010
  function patchLevelControlTlv() {
@@ -176484,11 +177060,11 @@ function patchLevelControlTlv() {
176484
177060
  patched++;
176485
177061
  }
176486
177062
  if (patched > 0) {
176487
- logger246.info(
177063
+ logger247.info(
176488
177064
  `Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
176489
177065
  );
176490
177066
  } else {
176491
- logger246.warn(
177067
+ logger247.warn(
176492
177068
  "Failed to patch LevelControl TLV schemas, field definitions not found. Google Home brightness adjustment may not work.",
176493
177069
  {
176494
177070
  moveToLevel: describeFields(