@riddix/hamh 2.1.0-alpha.521 → 2.1.0-alpha.523

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1743,8 +1743,8 @@ var init_Cancelable = __esm({
1743
1743
  };
1744
1744
  return result;
1745
1745
  }
1746
- static set logger(logger203) {
1747
- this.#logger = logger203;
1746
+ static set logger(logger204) {
1747
+ this.#logger = logger204;
1748
1748
  }
1749
1749
  static get logger() {
1750
1750
  return this.#logger;
@@ -3422,13 +3422,13 @@ var init_Logger = __esm({
3422
3422
  *
3423
3423
  * @deprecated use {@link destinations}
3424
3424
  */
3425
- static addLogger(identifier, logger203, options) {
3425
+ static addLogger(identifier, logger204, options) {
3426
3426
  if (identifier in this.destinations) {
3427
3427
  throw new ImplementationError(`Logger "${identifier}" already exists`);
3428
3428
  }
3429
3429
  const dest = LogDestination({ name: identifier });
3430
3430
  const legacy = adaptDestinationToLegacy(dest);
3431
- legacy.log = logger203;
3431
+ legacy.log = logger204;
3432
3432
  if (options?.defaultLogLevel !== void 0) {
3433
3433
  legacy.defaultLogLevel = options.defaultLogLevel;
3434
3434
  }
@@ -5538,8 +5538,8 @@ function Construction(subject, initializer) {
5538
5538
  }
5539
5539
  }
5540
5540
  function unhandledError(...args) {
5541
- const logger203 = Logger.get(subject.constructor.name);
5542
- logger203.error(...args);
5541
+ const logger204 = Logger.get(subject.constructor.name);
5542
+ logger204.error(...args);
5543
5543
  }
5544
5544
  function createErrorHandler(name) {
5545
5545
  return (e) => {
@@ -135998,11 +135998,11 @@ var init_Api = __esm({
135998
135998
  }
135999
135999
  Api2.resourceFor = resourceFor;
136000
136000
  function log(level, facility, id, ...message) {
136001
- let logger203 = loggers.get(facility);
136002
- if (!logger203) {
136003
- loggers.set(facility, logger203 = Logger.get(facility));
136001
+ let logger204 = loggers.get(facility);
136002
+ if (!logger204) {
136003
+ loggers.set(facility, logger204 = Logger.get(facility));
136004
136004
  }
136005
- logger203[level](Diagnostic.via(id || "(anon)"), message);
136005
+ logger204[level](Diagnostic.via(id || "(anon)"), message);
136006
136006
  }
136007
136007
  Api2.log = log;
136008
136008
  function logRequest(facility, id, method, target) {
@@ -147221,10 +147221,10 @@ var init_home_assistant_actions = __esm({
147221
147221
  circuitBreakerResetMs: 3e4
147222
147222
  };
147223
147223
  HomeAssistantActions = class extends Service {
147224
- constructor(logger203, client, config10) {
147224
+ constructor(logger204, client, config10) {
147225
147225
  super("HomeAssistantActions");
147226
147226
  this.client = client;
147227
- this.log = logger203.get(this);
147227
+ this.log = logger204.get(this);
147228
147228
  this.config = { ...defaultConfig, ...config10 };
147229
147229
  this.circuitBreaker = new CircuitBreaker(
147230
147230
  this.config.circuitBreakerThreshold,
@@ -147596,10 +147596,10 @@ var DiagnosticService = class {
147596
147596
  };
147597
147597
 
147598
147598
  // src/api/access-log.ts
147599
- function accessLogger(logger203) {
147599
+ function accessLogger(logger204) {
147600
147600
  return (req, res, next) => {
147601
147601
  res.on("finish", () => {
147602
- logger203.debug(
147602
+ logger204.debug(
147603
147603
  `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}`
147604
147604
  );
147605
147605
  });
@@ -151095,7 +151095,7 @@ var WebSocketApi = class {
151095
151095
 
151096
151096
  // src/api/web-api.ts
151097
151097
  var WebApi = class extends Service {
151098
- constructor(logger203, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151098
+ constructor(logger204, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, lockCredentialStorage, settingsStorage, backupService, props) {
151099
151099
  super("WebApi");
151100
151100
  this.bridgeService = bridgeService;
151101
151101
  this.haClient = haClient;
@@ -151106,8 +151106,8 @@ var WebApi = class extends Service {
151106
151106
  this.settingsStorage = settingsStorage;
151107
151107
  this.backupService = backupService;
151108
151108
  this.props = props;
151109
- this.logger = logger203;
151110
- this.log = logger203.get(this);
151109
+ this.logger = logger204;
151110
+ this.log = logger204.get(this);
151111
151111
  this.accessLogger = accessLogger(this.log.createChild("Access Log"));
151112
151112
  this.startTime = Date.now();
151113
151113
  this.wsApi = new WebSocketApi(
@@ -151545,12 +151545,12 @@ var CustomStorage = class extends StorageBackendDisk {
151545
151545
 
151546
151546
  // src/core/app/storage.ts
151547
151547
  function storage(environment, options) {
151548
- const logger203 = environment.get(LoggerService).get("CustomStorage");
151548
+ const logger204 = environment.get(LoggerService).get("CustomStorage");
151549
151549
  const location2 = resolveStorageLocation(options.location);
151550
151550
  fs8.mkdirSync(location2, { recursive: true });
151551
151551
  const storageService = environment.get(StorageService);
151552
151552
  storageService.location = location2;
151553
- storageService.factory = (ns) => new CustomStorage(logger203, path8.resolve(location2, ns));
151553
+ storageService.factory = (ns) => new CustomStorage(logger204, path8.resolve(location2, ns));
151554
151554
  }
151555
151555
  function resolveStorageLocation(storageLocation) {
151556
151556
  const homedir = os3.homedir();
@@ -152100,10 +152100,10 @@ import {
152100
152100
  getConfig
152101
152101
  } from "home-assistant-js-websocket";
152102
152102
  var HomeAssistantClient = class extends Service {
152103
- constructor(logger203, options) {
152103
+ constructor(logger204, options) {
152104
152104
  super("HomeAssistantClient");
152105
152105
  this.options = options;
152106
- this.log = logger203.get(this);
152106
+ this.log = logger204.get(this);
152107
152107
  }
152108
152108
  static Options = /* @__PURE__ */ Symbol.for("HomeAssistantClientProps");
152109
152109
  _connection;
@@ -152696,9 +152696,10 @@ var EntityMappingStorage = class extends Service {
152696
152696
  ) ?? void 0,
152697
152697
  customFanSpeedTags: request.customFanSpeedTags && Object.keys(request.customFanSpeedTags).length > 0 ? request.customFanSpeedTags : void 0,
152698
152698
  valetudoIdentifier: request.valetudoIdentifier?.trim() || void 0,
152699
- coverSwapOpenClose: request.coverSwapOpenClose || void 0
152699
+ coverSwapOpenClose: request.coverSwapOpenClose || void 0,
152700
+ composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
152700
152701
  };
152701
- if (!config10.matterDeviceType && !config10.customName && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.temperatureEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0) && !config10.valetudoIdentifier && !config10.coverSwapOpenClose) {
152702
+ if (!config10.matterDeviceType && !config10.customName && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.temperatureEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0) && !config10.valetudoIdentifier && !config10.coverSwapOpenClose && (!config10.composedEntities || config10.composedEntities.length === 0)) {
152702
152703
  bridgeMap.delete(request.entityId);
152703
152704
  } else {
152704
152705
  bridgeMap.set(request.entityId, config10);
@@ -166868,10 +166869,10 @@ function ensureCommissioningConfig(server) {
166868
166869
  var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
166869
166870
  var DEAD_SESSION_TIMEOUT_MS = 6e4;
166870
166871
  var Bridge = class {
166871
- constructor(env, logger203, dataProvider, endpointManager) {
166872
+ constructor(env, logger204, dataProvider, endpointManager) {
166872
166873
  this.dataProvider = dataProvider;
166873
166874
  this.endpointManager = endpointManager;
166874
- this.log = logger203.get(`Bridge / ${dataProvider.id}`);
166875
+ this.log = logger204.get(`Bridge / ${dataProvider.id}`);
166875
166876
  this.server = new BridgeServerNode(
166876
166877
  env,
166877
166878
  this.dataProvider,
@@ -167316,7 +167317,7 @@ var AggregatorEndpoint2 = class extends Endpoint {
167316
167317
  init_dist();
167317
167318
  init_esm();
167318
167319
  init_home_assistant_entity_behavior();
167319
- import debounce4 from "debounce";
167320
+ import debounce5 from "debounce";
167320
167321
 
167321
167322
  // src/matter/endpoints/entity-endpoint.ts
167322
167323
  init_esm7();
@@ -167359,6 +167360,11 @@ function getMappedEntityIds(mapping) {
167359
167360
  if (mapping.filterLifeEntity) ids.push(mapping.filterLifeEntity);
167360
167361
  if (mapping.powerEntity) ids.push(mapping.powerEntity);
167361
167362
  if (mapping.energyEntity) ids.push(mapping.energyEntity);
167363
+ if (mapping.composedEntities) {
167364
+ for (const sub of mapping.composedEntities) {
167365
+ if (sub.entityId) ids.push(sub.entityId);
167366
+ }
167367
+ }
167362
167368
  return ids;
167363
167369
  }
167364
167370
 
@@ -168113,12 +168119,13 @@ var OnOffServerBase = class extends OnOffServer {
168113
168119
  return;
168114
168120
  }
168115
168121
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168116
- const action = turnOn?.(void 0, this.agent) ?? {
168117
- action: "homeassistant.turn_on"
168118
- };
168122
+ const action = turnOn ? turnOn(void 0, this.agent) : { action: "homeassistant.turn_on" };
168123
+ applyPatchState(this.state, { onOff: true });
168124
+ if (!action) {
168125
+ return;
168126
+ }
168119
168127
  logger167.info(`[${homeAssistant.entityId}] Turning ON -> ${action.action}`);
168120
168128
  notifyLightTurnedOn(homeAssistant.entityId);
168121
- applyPatchState(this.state, { onOff: true });
168122
168129
  optimisticOnOffState.set(homeAssistant.entityId, {
168123
168130
  expectedOnOff: true,
168124
168131
  timestamp: Date.now()
@@ -168135,11 +168142,12 @@ var OnOffServerBase = class extends OnOffServer {
168135
168142
  return;
168136
168143
  }
168137
168144
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
168138
- const action = turnOff?.(void 0, this.agent) ?? {
168139
- action: "homeassistant.turn_off"
168140
- };
168141
- logger167.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
168145
+ const action = turnOff ? turnOff(void 0, this.agent) : { action: "homeassistant.turn_off" };
168142
168146
  applyPatchState(this.state, { onOff: false });
168147
+ if (!action) {
168148
+ return;
168149
+ }
168150
+ logger167.info(`[${homeAssistant.entityId}] Turning OFF -> ${action.action}`);
168143
168151
  optimisticOnOffState.set(homeAssistant.entityId, {
168144
168152
  expectedOnOff: false,
168145
168153
  timestamp: Date.now()
@@ -169196,6 +169204,12 @@ var ComposedSensorEndpoint = class _ComposedSensorEndpoint extends Endpoint {
169196
169204
  }
169197
169205
  };
169198
169206
 
169207
+ // src/matter/endpoints/composed/user-composed-endpoint.ts
169208
+ init_esm();
169209
+ init_esm7();
169210
+ import debounce4 from "debounce";
169211
+ init_home_assistant_entity_behavior();
169212
+
169199
169213
  // src/matter/endpoints/legacy/air-purifier/index.ts
169200
169214
  init_dist();
169201
169215
  init_home_assistant_entity_behavior();
@@ -170002,8 +170016,15 @@ var humidityConfig3 = {
170002
170016
  var ClimateHumidityMeasurementServer = HumidityMeasurementServer(humidityConfig3);
170003
170017
 
170004
170018
  // src/matter/endpoints/legacy/climate/behaviors/climate-on-off-server.ts
170019
+ init_home_assistant_entity_behavior();
170005
170020
  var ClimateOnOffServer = OnOffServer2({
170006
- turnOn: () => ({ action: "climate.turn_on" }),
170021
+ turnOn: (_value, agent) => {
170022
+ const entity = agent.get(HomeAssistantEntityBehavior).entity;
170023
+ if (entity.state.state !== "off") {
170024
+ return void 0;
170025
+ }
170026
+ return { action: "climate.turn_on" };
170027
+ },
170007
170028
  turnOff: () => ({ action: "climate.turn_off" })
170008
170029
  }).with("Lighting");
170009
170030
 
@@ -177908,8 +177929,190 @@ var matterDeviceTypeFactories = {
177908
177929
  })
177909
177930
  };
177910
177931
 
177932
+ // src/matter/endpoints/composed/user-composed-endpoint.ts
177933
+ var logger198 = Logger.get("UserComposedEndpoint");
177934
+ function createEndpointId4(entityId, customName) {
177935
+ const baseName = customName || entityId;
177936
+ return baseName.replace(/\./g, "_").replace(/\s+/g, "_");
177937
+ }
177938
+ function buildEntityPayload3(registry2, entityId) {
177939
+ const state = registry2.initialState(entityId);
177940
+ if (!state) return void 0;
177941
+ const entity = registry2.entity(entityId);
177942
+ const deviceRegistry = registry2.deviceOf(entityId);
177943
+ return {
177944
+ entity_id: entityId,
177945
+ state,
177946
+ registry: entity,
177947
+ deviceRegistry
177948
+ };
177949
+ }
177950
+ var UserComposedEndpoint = class _UserComposedEndpoint extends Endpoint {
177951
+ entityId;
177952
+ mappedEntityIds;
177953
+ subEndpoints = /* @__PURE__ */ new Map();
177954
+ lastStates = /* @__PURE__ */ new Map();
177955
+ debouncedUpdates = /* @__PURE__ */ new Map();
177956
+ static async create(config10) {
177957
+ const { registry: registry2, primaryEntityId, composedEntities } = config10;
177958
+ const primaryPayload = buildEntityPayload3(registry2, primaryEntityId);
177959
+ if (!primaryPayload) return void 0;
177960
+ let parentType = BridgedNodeEndpoint.with(
177961
+ BasicInformationServer2,
177962
+ IdentifyServer2,
177963
+ HomeAssistantEntityBehavior
177964
+ );
177965
+ if (config10.areaName) {
177966
+ const truncatedName = config10.areaName.length > 16 ? config10.areaName.substring(0, 16) : config10.areaName;
177967
+ parentType = parentType.with(
177968
+ FixedLabelServer.set({
177969
+ labelList: [{ label: "room", value: truncatedName }]
177970
+ })
177971
+ );
177972
+ }
177973
+ const endpointId = createEndpointId4(primaryEntityId, config10.customName);
177974
+ const parts = [];
177975
+ const subEndpointMap = /* @__PURE__ */ new Map();
177976
+ const mappedIds = [];
177977
+ const primaryType = createLegacyEndpointType(
177978
+ primaryPayload,
177979
+ config10.mapping,
177980
+ void 0,
177981
+ { vacuumOnOff: registry2.isVacuumOnOffEnabled() }
177982
+ );
177983
+ if (!primaryType) {
177984
+ logger198.warn(
177985
+ `Cannot create endpoint type for primary entity ${primaryEntityId}`
177986
+ );
177987
+ return void 0;
177988
+ }
177989
+ const primarySub = new Endpoint(primaryType, {
177990
+ id: `${endpointId}_primary`
177991
+ });
177992
+ parts.push(primarySub);
177993
+ subEndpointMap.set(primaryEntityId, primarySub);
177994
+ for (let i = 0; i < composedEntities.length; i++) {
177995
+ const sub = composedEntities[i];
177996
+ if (!sub.entityId) continue;
177997
+ const subPayload = buildEntityPayload3(registry2, sub.entityId);
177998
+ if (!subPayload) {
177999
+ logger198.warn(
178000
+ `Cannot find entity state for composed sub-entity ${sub.entityId}`
178001
+ );
178002
+ continue;
178003
+ }
178004
+ const subMapping = {
178005
+ entityId: sub.entityId,
178006
+ matterDeviceType: sub.matterDeviceType
178007
+ };
178008
+ const subType = createLegacyEndpointType(subPayload, subMapping);
178009
+ if (!subType) {
178010
+ logger198.warn(
178011
+ `Cannot create endpoint type for composed sub-entity ${sub.entityId}`
178012
+ );
178013
+ continue;
178014
+ }
178015
+ const subEndpoint = new Endpoint(subType, {
178016
+ id: `${endpointId}_sub_${i}`
178017
+ });
178018
+ parts.push(subEndpoint);
178019
+ subEndpointMap.set(sub.entityId, subEndpoint);
178020
+ mappedIds.push(sub.entityId);
178021
+ }
178022
+ if (parts.length < 2) {
178023
+ logger198.warn(
178024
+ `User composed device ${primaryEntityId}: only ${parts.length} sub-endpoint(s), need at least 2 (primary + one sub-entity). Falling back to standalone.`
178025
+ );
178026
+ return void 0;
178027
+ }
178028
+ const parentTypeWithState = parentType.set({
178029
+ homeAssistantEntity: {
178030
+ entity: primaryPayload,
178031
+ customName: config10.customName,
178032
+ mapping: config10.mapping
178033
+ }
178034
+ });
178035
+ const endpoint = new _UserComposedEndpoint(
178036
+ parentTypeWithState,
178037
+ primaryEntityId,
178038
+ endpointId,
178039
+ parts,
178040
+ mappedIds
178041
+ );
178042
+ endpoint.subEndpoints = subEndpointMap;
178043
+ const labels = parts.map(
178044
+ (_, i) => i === 0 ? primaryEntityId.split(".")[0] : composedEntities[i - 1]?.entityId?.split(".")[0] ?? "?"
178045
+ ).join("+");
178046
+ logger198.info(
178047
+ `Created user composed device ${primaryEntityId}: ${parts.length} sub-endpoint(s) [${labels}]`
178048
+ );
178049
+ return endpoint;
178050
+ }
178051
+ constructor(type, entityId, id, parts, mappedEntityIds) {
178052
+ super(type, { id, parts });
178053
+ this.entityId = entityId;
178054
+ this.mappedEntityIds = mappedEntityIds;
178055
+ }
178056
+ async updateStates(states) {
178057
+ this.scheduleUpdate(this, this.entityId, states);
178058
+ for (const [entityId, sub] of this.subEndpoints) {
178059
+ this.scheduleUpdate(sub, entityId, states);
178060
+ }
178061
+ }
178062
+ scheduleUpdate(endpoint, entityId, states) {
178063
+ const state = states[entityId];
178064
+ if (!state) return;
178065
+ const key = endpoint === this ? `_parent_:${entityId}` : entityId;
178066
+ const stateJson = JSON.stringify({
178067
+ s: state.state,
178068
+ a: state.attributes
178069
+ });
178070
+ if (this.lastStates.get(key) === stateJson) return;
178071
+ this.lastStates.set(key, stateJson);
178072
+ let debouncedFn = this.debouncedUpdates.get(key);
178073
+ if (!debouncedFn) {
178074
+ debouncedFn = debounce4(
178075
+ (ep, s) => this.flushUpdate(ep, s),
178076
+ 50
178077
+ );
178078
+ this.debouncedUpdates.set(key, debouncedFn);
178079
+ }
178080
+ debouncedFn(endpoint, state);
178081
+ }
178082
+ async flushUpdate(endpoint, state) {
178083
+ try {
178084
+ await endpoint.construction.ready;
178085
+ } catch {
178086
+ return;
178087
+ }
178088
+ try {
178089
+ const current = endpoint.stateOf(HomeAssistantEntityBehavior).entity;
178090
+ await endpoint.setStateOf(HomeAssistantEntityBehavior, {
178091
+ entity: { ...current, state }
178092
+ });
178093
+ } catch (error) {
178094
+ if (error instanceof TransactionDestroyedError || error instanceof DestroyedDependencyError) {
178095
+ return;
178096
+ }
178097
+ const errorMessage = error instanceof Error ? error.message : String(error);
178098
+ if (errorMessage.includes(
178099
+ "Endpoint storage inaccessible because endpoint is not a node and is not owned by another endpoint"
178100
+ )) {
178101
+ return;
178102
+ }
178103
+ throw error;
178104
+ }
178105
+ }
178106
+ async delete() {
178107
+ for (const fn of this.debouncedUpdates.values()) {
178108
+ fn.clear();
178109
+ }
178110
+ await super.delete();
178111
+ }
178112
+ };
178113
+
177911
178114
  // src/matter/endpoints/legacy/legacy-endpoint.ts
177912
- var logger198 = Logger.get("LegacyEndpoint");
178115
+ var logger199 = Logger.get("LegacyEndpoint");
177913
178116
  var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177914
178117
  static async create(registry2, entityId, mapping) {
177915
178118
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -177919,25 +178122,25 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177919
178122
  return;
177920
178123
  }
177921
178124
  if (registry2.isAutoBatteryMappingEnabled() && registry2.isBatteryEntityUsed(entityId)) {
177922
- logger198.debug(
178125
+ logger199.debug(
177923
178126
  `Skipping ${entityId} - already auto-assigned as battery to another device`
177924
178127
  );
177925
178128
  return;
177926
178129
  }
177927
178130
  if (registry2.isAutoHumidityMappingEnabled() && registry2.isHumidityEntityUsed(entityId)) {
177928
- logger198.debug(
178131
+ logger199.debug(
177929
178132
  `Skipping ${entityId} - already auto-assigned as humidity to a temperature sensor`
177930
178133
  );
177931
178134
  return;
177932
178135
  }
177933
178136
  if (registry2.isAutoPressureMappingEnabled() && registry2.isPressureEntityUsed(entityId)) {
177934
- logger198.debug(
178137
+ logger199.debug(
177935
178138
  `Skipping ${entityId} - already auto-assigned as pressure to a temperature sensor`
177936
178139
  );
177937
178140
  return;
177938
178141
  }
177939
178142
  if (registry2.isAutoComposedDevicesEnabled() && registry2.isComposedSubEntityUsed(entityId)) {
177940
- logger198.debug(
178143
+ logger199.debug(
177941
178144
  `Skipping ${entityId} - already consumed by a composed device`
177942
178145
  );
177943
178146
  return;
@@ -177957,7 +178160,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177957
178160
  humidityEntity: humidityEntityId
177958
178161
  };
177959
178162
  registry2.markHumidityEntityUsed(humidityEntityId);
177960
- logger198.debug(
178163
+ logger199.debug(
177961
178164
  `Auto-assigned humidity ${humidityEntityId} to ${entityId}`
177962
178165
  );
177963
178166
  }
@@ -177976,7 +178179,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177976
178179
  pressureEntity: pressureEntityId
177977
178180
  };
177978
178181
  registry2.markPressureEntityUsed(pressureEntityId);
177979
- logger198.debug(
178182
+ logger199.debug(
177980
178183
  `Auto-assigned pressure ${pressureEntityId} to ${entityId}`
177981
178184
  );
177982
178185
  }
@@ -177994,7 +178197,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
177994
178197
  batteryEntity: batteryEntityId
177995
178198
  };
177996
178199
  registry2.markBatteryEntityUsed(batteryEntityId);
177997
- logger198.debug(
178200
+ logger199.debug(
177998
178201
  `Auto-assigned battery ${batteryEntityId} to ${entityId}`
177999
178202
  );
178000
178203
  }
@@ -178012,7 +178215,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178012
178215
  powerEntity: powerEntityId
178013
178216
  };
178014
178217
  registry2.markPowerEntityUsed(powerEntityId);
178015
- logger198.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
178218
+ logger199.debug(`Auto-assigned power ${powerEntityId} to ${entityId}`);
178016
178219
  }
178017
178220
  }
178018
178221
  }
@@ -178029,7 +178232,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178029
178232
  energyEntity: energyEntityId
178030
178233
  };
178031
178234
  registry2.markEnergyEntityUsed(energyEntityId);
178032
- logger198.debug(
178235
+ logger199.debug(
178033
178236
  `Auto-assigned energy ${energyEntityId} to ${entityId}`
178034
178237
  );
178035
178238
  }
@@ -178045,7 +178248,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178045
178248
  entityId: effectiveMapping?.entityId ?? entityId,
178046
178249
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
178047
178250
  };
178048
- logger198.debug(
178251
+ logger199.debug(
178049
178252
  `Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity} to ${entityId}`
178050
178253
  );
178051
178254
  }
@@ -178055,7 +178258,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178055
178258
  entityId: effectiveMapping?.entityId ?? entityId,
178056
178259
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
178057
178260
  };
178058
- logger198.debug(
178261
+ logger199.debug(
178059
178262
  `Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity} to ${entityId}`
178060
178263
  );
178061
178264
  }
@@ -178065,7 +178268,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178065
178268
  entityId: effectiveMapping?.entityId ?? entityId,
178066
178269
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
178067
178270
  };
178068
- logger198.debug(
178271
+ logger199.debug(
178069
178272
  `Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity} to ${entityId}`
178070
178273
  );
178071
178274
  }
@@ -178080,7 +178283,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178080
178283
  entityId: effectiveMapping?.entityId ?? entityId,
178081
178284
  cleanAreaRooms
178082
178285
  };
178083
- logger198.debug(
178286
+ logger199.debug(
178084
178287
  `Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA for ${entityId}`
178085
178288
  );
178086
178289
  }
@@ -178101,7 +178304,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178101
178304
  rooms: roomsObj
178102
178305
  }
178103
178306
  };
178104
- logger198.debug(
178307
+ logger199.debug(
178105
178308
  `Auto-detected ${valetudoRooms.length} Valetudo segments for ${entityId}`
178106
178309
  );
178107
178310
  } else {
@@ -178118,7 +178321,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178118
178321
  rooms: roomsObj
178119
178322
  }
178120
178323
  };
178121
- logger198.debug(
178324
+ logger199.debug(
178122
178325
  `Auto-detected ${roborockRooms.length} Roborock rooms for ${entityId}`
178123
178326
  );
178124
178327
  }
@@ -178126,6 +178329,23 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178126
178329
  }
178127
178330
  }
178128
178331
  }
178332
+ if (registry2.isAutoComposedDevicesEnabled() && effectiveMapping?.composedEntities && effectiveMapping.composedEntities.length > 0) {
178333
+ const composedAreaName = registry2.getAreaName(entityId);
178334
+ const composed = await UserComposedEndpoint.create({
178335
+ registry: registry2,
178336
+ primaryEntityId: entityId,
178337
+ mapping: effectiveMapping,
178338
+ composedEntities: effectiveMapping.composedEntities,
178339
+ customName: effectiveMapping?.customName,
178340
+ areaName: composedAreaName
178341
+ });
178342
+ if (composed) {
178343
+ return composed;
178344
+ }
178345
+ logger199.warn(
178346
+ `User composed device creation failed for ${entityId}, falling back to standalone`
178347
+ );
178348
+ }
178129
178349
  if (registry2.isAutoComposedDevicesEnabled()) {
178130
178350
  const attrs = state.attributes;
178131
178351
  if (entityId.startsWith("sensor.") && attrs.device_class === SensorDeviceClass.temperature && (effectiveMapping?.humidityEntity || effectiveMapping?.pressureEntity)) {
@@ -178200,7 +178420,7 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178200
178420
  }
178201
178421
  constructor(type, entityId, customName, mappedEntityIds) {
178202
178422
  super(type, entityId, customName, mappedEntityIds);
178203
- this.flushUpdate = debounce4(this.flushPendingUpdate.bind(this), 50);
178423
+ this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
178204
178424
  }
178205
178425
  lastState;
178206
178426
  pendingMappedChange = false;
@@ -178217,11 +178437,11 @@ var LegacyEndpoint = class _LegacyEndpoint extends EntityEndpoint {
178217
178437
  }
178218
178438
  if (mappedChanged) {
178219
178439
  this.pendingMappedChange = true;
178220
- logger198.debug(
178440
+ logger199.debug(
178221
178441
  `Mapped entity change detected for ${this.entityId}, forcing update`
178222
178442
  );
178223
178443
  }
178224
- logger198.debug(
178444
+ logger199.debug(
178225
178445
  `State update received for ${this.entityId}: state=${state.state}`
178226
178446
  );
178227
178447
  this.lastState = state;
@@ -178265,7 +178485,7 @@ import {
178265
178485
  getCollection
178266
178486
  } from "home-assistant-js-websocket";
178267
178487
  import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js";
178268
- var logger199 = Logger.get("SubscribeEntities");
178488
+ var logger200 = Logger.get("SubscribeEntities");
178269
178489
  function processEvent(store, updates) {
178270
178490
  const state = { ...store.state };
178271
178491
  if (updates.a) {
@@ -178291,7 +178511,7 @@ function processEvent(store, updates) {
178291
178511
  for (const entityId in updates.c) {
178292
178512
  let entityState = state[entityId];
178293
178513
  if (!entityState) {
178294
- logger199.warn("Received state update for unknown entity", entityId);
178514
+ logger200.warn("Received state update for unknown entity", entityId);
178295
178515
  continue;
178296
178516
  }
178297
178517
  entityState = { ...entityState };
@@ -178361,7 +178581,7 @@ var subscribeEntities = (conn, onChange, entityIds) => entitiesColl(conn, entity
178361
178581
 
178362
178582
  // src/services/bridges/entity-isolation-service.ts
178363
178583
  init_esm();
178364
- var logger200 = Logger.get("EntityIsolation");
178584
+ var logger201 = Logger.get("EntityIsolation");
178365
178585
  var EntityIsolationServiceImpl = class {
178366
178586
  isolatedEntities = /* @__PURE__ */ new Map();
178367
178587
  isolationCallbacks = /* @__PURE__ */ new Map();
@@ -178426,13 +178646,13 @@ var EntityIsolationServiceImpl = class {
178426
178646
  }
178427
178647
  const parsed = this.parseEndpointPath(msg);
178428
178648
  if (!parsed) {
178429
- logger200.warn("Could not parse entity from error:", msg);
178649
+ logger201.warn("Could not parse entity from error:", msg);
178430
178650
  return false;
178431
178651
  }
178432
178652
  const { bridgeId, entityName } = parsed;
178433
178653
  const callback = this.isolationCallbacks.get(bridgeId);
178434
178654
  if (!callback) {
178435
- logger200.warn(
178655
+ logger201.warn(
178436
178656
  `No isolation callback registered for bridge ${bridgeId}, entity: ${entityName}`
178437
178657
  );
178438
178658
  return false;
@@ -178443,14 +178663,14 @@ var EntityIsolationServiceImpl = class {
178443
178663
  }
178444
178664
  const reason = `${classification}. Entity isolated to protect bridge stability.`;
178445
178665
  this.isolatedEntities.set(key, { entityId: entityName, reason });
178446
- logger200.warn(
178666
+ logger201.warn(
178447
178667
  `Isolating entity "${entityName}" from bridge ${bridgeId} due to: ${reason}`
178448
178668
  );
178449
178669
  try {
178450
178670
  await callback(entityName);
178451
178671
  return true;
178452
178672
  } catch (e) {
178453
- logger200.error(`Failed to isolate entity ${entityName}:`, e);
178673
+ logger201.error(`Failed to isolate entity ${entityName}:`, e);
178454
178674
  return false;
178455
178675
  }
178456
178676
  }
@@ -178749,8 +178969,15 @@ var BridgeEndpointManager = class extends Service {
178749
178969
  this.entityIds = this.registry.entityIds;
178750
178970
  if (this.registry.isAutoComposedDevicesEnabled()) {
178751
178971
  for (const eid of this.entityIds) {
178752
- if (!eid.startsWith("fan.")) continue;
178753
178972
  const m = this.getEntityMapping(eid);
178973
+ if (m?.composedEntities) {
178974
+ for (const sub of m.composedEntities) {
178975
+ if (sub.entityId) {
178976
+ this.registry.markComposedSubEntityUsed(sub.entityId);
178977
+ }
178978
+ }
178979
+ }
178980
+ if (!eid.startsWith("fan.")) continue;
178754
178981
  const matterType = m?.matterDeviceType ?? "fan";
178755
178982
  if (matterType !== "air_purifier") continue;
178756
178983
  const ent = this.registry.entity(eid);
@@ -179604,11 +179831,11 @@ init_dist();
179604
179831
  var AUTO_FORCE_SYNC_INTERVAL_MS2 = 9e4;
179605
179832
  var DEAD_SESSION_TIMEOUT_MS2 = 6e4;
179606
179833
  var ServerModeBridge = class {
179607
- constructor(logger203, dataProvider, endpointManager, server) {
179834
+ constructor(logger204, dataProvider, endpointManager, server) {
179608
179835
  this.dataProvider = dataProvider;
179609
179836
  this.endpointManager = endpointManager;
179610
179837
  this.server = server;
179611
- this.log = logger203.get(`ServerModeBridge / ${dataProvider.id}`);
179838
+ this.log = logger204.get(`ServerModeBridge / ${dataProvider.id}`);
179612
179839
  }
179613
179840
  log;
179614
179841
  status = {
@@ -180033,7 +180260,7 @@ init_service();
180033
180260
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
180034
180261
  init_esm();
180035
180262
  init_home_assistant_entity_behavior();
180036
- import debounce5 from "debounce";
180263
+ import debounce6 from "debounce";
180037
180264
 
180038
180265
  // src/matter/endpoints/legacy/vacuum/server-mode-vacuum-device.ts
180039
180266
  init_home_assistant_entity_behavior();
@@ -180097,7 +180324,7 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
180097
180324
  }
180098
180325
 
180099
180326
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
180100
- var logger201 = Logger.get("ServerModeVacuumEndpoint");
180327
+ var logger202 = Logger.get("ServerModeVacuumEndpoint");
180101
180328
  var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
180102
180329
  static async create(registry2, entityId, mapping) {
180103
180330
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -180107,7 +180334,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180107
180334
  return void 0;
180108
180335
  }
180109
180336
  let effectiveMapping = mapping;
180110
- logger201.info(
180337
+ logger202.info(
180111
180338
  `${entityId}: device_id=${entity.device_id}, manualBattery=${mapping?.batteryEntity ?? "none"}`
180112
180339
  );
180113
180340
  if (entity.device_id) {
@@ -180122,15 +180349,15 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180122
180349
  batteryEntity: batteryEntityId
180123
180350
  };
180124
180351
  registry2.markBatteryEntityUsed(batteryEntityId);
180125
- logger201.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
180352
+ logger202.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
180126
180353
  } else {
180127
180354
  const attrs = state.attributes;
180128
180355
  if (attrs.battery_level != null || attrs.battery != null) {
180129
- logger201.info(
180356
+ logger202.info(
180130
180357
  `${entityId}: No battery entity found, using battery attribute from vacuum state`
180131
180358
  );
180132
180359
  } else {
180133
- logger201.warn(
180360
+ logger202.warn(
180134
180361
  `${entityId}: No battery entity found for device ${entity.device_id}`
180135
180362
  );
180136
180363
  }
@@ -180145,7 +180372,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180145
180372
  entityId: effectiveMapping?.entityId ?? entityId,
180146
180373
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
180147
180374
  };
180148
- logger201.info(
180375
+ logger202.info(
180149
180376
  `${entityId}: Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity}`
180150
180377
  );
180151
180378
  }
@@ -180155,7 +180382,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180155
180382
  entityId: effectiveMapping?.entityId ?? entityId,
180156
180383
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
180157
180384
  };
180158
- logger201.info(
180385
+ logger202.info(
180159
180386
  `${entityId}: Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity}`
180160
180387
  );
180161
180388
  }
@@ -180165,7 +180392,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180165
180392
  entityId: effectiveMapping?.entityId ?? entityId,
180166
180393
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
180167
180394
  };
180168
- logger201.info(
180395
+ logger202.info(
180169
180396
  `${entityId}: Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity}`
180170
180397
  );
180171
180398
  }
@@ -180180,7 +180407,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180180
180407
  entityId: effectiveMapping?.entityId ?? entityId,
180181
180408
  cleanAreaRooms
180182
180409
  };
180183
- logger201.info(
180410
+ logger202.info(
180184
180411
  `${entityId}: Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA`
180185
180412
  );
180186
180413
  }
@@ -180201,7 +180428,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180201
180428
  rooms: roomsObj
180202
180429
  }
180203
180430
  };
180204
- logger201.info(
180431
+ logger202.info(
180205
180432
  `${entityId}: Auto-detected ${valetudoRooms.length} Valetudo segments`
180206
180433
  );
180207
180434
  } else {
@@ -180218,14 +180445,14 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180218
180445
  rooms: roomsObj
180219
180446
  }
180220
180447
  };
180221
- logger201.info(
180448
+ logger202.info(
180222
180449
  `${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
180223
180450
  );
180224
180451
  }
180225
180452
  }
180226
180453
  }
180227
180454
  } else {
180228
- logger201.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
180455
+ logger202.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
180229
180456
  }
180230
180457
  const payload = {
180231
180458
  entity_id: entityId,
@@ -180275,7 +180502,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180275
180502
  flushUpdate;
180276
180503
  constructor(type, entityId, customName, mappedEntityIds) {
180277
180504
  super(type, entityId, customName, mappedEntityIds);
180278
- this.flushUpdate = debounce5(this.flushPendingUpdate.bind(this), 50);
180505
+ this.flushUpdate = debounce6(this.flushPendingUpdate.bind(this), 50);
180279
180506
  }
180280
180507
  async delete() {
180281
180508
  this.flushUpdate.clear();
@@ -180289,11 +180516,11 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
180289
180516
  }
180290
180517
  if (mappedChanged) {
180291
180518
  this.pendingMappedChange = true;
180292
- logger201.debug(
180519
+ logger202.debug(
180293
180520
  `Mapped entity change detected for ${this.entityId}, forcing update`
180294
180521
  );
180295
180522
  }
180296
- logger201.debug(
180523
+ logger202.debug(
180297
180524
  `State update received for ${this.entityId}: state=${state.state}`
180298
180525
  );
180299
180526
  this.lastState = state;
@@ -180707,10 +180934,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
180707
180934
  // src/core/ioc/app-environment.ts
180708
180935
  var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180709
180936
  constructor(rootEnv, options) {
180710
- const logger203 = rootEnv.get(LoggerService);
180937
+ const logger204 = rootEnv.get(LoggerService);
180711
180938
  super({
180712
180939
  id: "App",
180713
- log: logger203.get("AppContainer"),
180940
+ log: logger204.get("AppContainer"),
180714
180941
  parent: rootEnv
180715
180942
  });
180716
180943
  this.options = options;
@@ -180723,8 +180950,8 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180723
180950
  }
180724
180951
  construction;
180725
180952
  async init() {
180726
- const logger203 = this.get(LoggerService);
180727
- this.set(LoggerService, logger203);
180953
+ const logger204 = this.get(LoggerService);
180954
+ this.set(LoggerService, logger204);
180728
180955
  this.set(AppStorage, new AppStorage(await this.load(StorageService)));
180729
180956
  this.set(BridgeStorage, new BridgeStorage(await this.load(AppStorage)));
180730
180957
  this.set(
@@ -180741,7 +180968,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180741
180968
  );
180742
180969
  this.set(
180743
180970
  HomeAssistantClient,
180744
- new HomeAssistantClient(logger203, this.options.homeAssistant)
180971
+ new HomeAssistantClient(logger204, this.options.homeAssistant)
180745
180972
  );
180746
180973
  this.set(
180747
180974
  HomeAssistantConfig,
@@ -180749,7 +180976,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180749
180976
  );
180750
180977
  this.set(
180751
180978
  HomeAssistantActions,
180752
- new HomeAssistantActions(logger203, await this.load(HomeAssistantClient))
180979
+ new HomeAssistantActions(logger204, await this.load(HomeAssistantClient))
180753
180980
  );
180754
180981
  this.set(
180755
180982
  HomeAssistantRegistry,
@@ -180785,7 +181012,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
180785
181012
  this.set(
180786
181013
  WebApi,
180787
181014
  new WebApi(
180788
- logger203,
181015
+ logger204,
180789
181016
  await this.load(BridgeService),
180790
181017
  await this.load(HomeAssistantClient),
180791
181018
  await this.load(HomeAssistantRegistry),
@@ -180811,7 +181038,7 @@ init_nodejs();
180811
181038
  init_level_control();
180812
181039
 
180813
181040
  // src/matter/patches/patch-level-control-tlv.ts
180814
- var logger202 = Logger.get("PatchLevelControlTlv");
181041
+ var logger203 = Logger.get("PatchLevelControlTlv");
180815
181042
  function patchLevelControlTlv() {
180816
181043
  let patched = 0;
180817
181044
  const moveToLevelFields = LevelControl3.TlvMoveToLevelRequest.fieldDefinitions;
@@ -180825,11 +181052,11 @@ function patchLevelControlTlv() {
180825
181052
  patched++;
180826
181053
  }
180827
181054
  if (patched > 0) {
180828
- logger202.info(
181055
+ logger203.info(
180829
181056
  `Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
180830
181057
  );
180831
181058
  } else {
180832
- logger202.warn(
181059
+ logger203.warn(
180833
181060
  "Failed to patch LevelControl TLV schemas \u2014 field definitions not found. Google Home brightness adjustment may not work."
180834
181061
  );
180835
181062
  }