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

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(logger248) {
1771
- this.#logger = logger248;
1770
+ static set logger(logger250) {
1771
+ this.#logger = logger250;
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, logger248, options) {
3478
+ static addLogger(identifier, logger250, 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 = logger248;
3484
+ legacy.log = logger250;
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 logger248 = Logger.get(this.#subject.constructor.name);
5691
- logger248.error(...args);
5690
+ const logger250 = Logger.get(this.#subject.constructor.name);
5691
+ logger250.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(logger248, operation, session, messenger, fabric, peerNodeId) {
64074
- logger248.info(
64073
+ function logNew(logger250, operation, session, messenger, fabric, peerNodeId) {
64074
+ logger250.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 logger248 = loggers.get(facility);
117718
- if (!logger248) {
117719
- loggers.set(facility, logger248 = Logger.get(facility));
117717
+ let logger250 = loggers.get(facility);
117718
+ if (!logger250) {
117719
+ loggers.set(facility, logger250 = Logger.get(facility));
117720
117720
  }
117721
- logger248[level](Diagnostic.via(id || "(anon)"), message);
117721
+ logger250[level](Diagnostic.via(id || "(anon)"), message);
117722
117722
  }
117723
117723
  Api2.log = log;
117724
117724
  function logRequest(facility, id, method, target) {
@@ -132631,10 +132631,10 @@ var init_home_assistant_actions = __esm({
132631
132631
  circuitBreakerResetMs: 3e4
132632
132632
  };
132633
132633
  HomeAssistantActions = class extends Service {
132634
- constructor(logger248, client, config11) {
132634
+ constructor(logger250, client, config11) {
132635
132635
  super("HomeAssistantActions");
132636
132636
  this.client = client;
132637
- this.log = logger248.get(this);
132637
+ this.log = logger250.get(this);
132638
132638
  this.config = { ...defaultConfig, ...config11 };
132639
132639
  this.circuitBreaker = new CircuitBreaker(
132640
132640
  this.config.circuitBreakerThreshold,
@@ -132980,10 +132980,10 @@ var DiagnosticService = class {
132980
132980
  };
132981
132981
 
132982
132982
  // src/api/access-log.ts
132983
- function accessLogger(logger248) {
132983
+ function accessLogger(logger250) {
132984
132984
  return (req, res, next) => {
132985
132985
  res.on("finish", () => {
132986
- logger248.debug(
132986
+ logger250.debug(
132987
132987
  `${req.method} ${decodeURI(req.originalUrl)} ${res.statusCode} ${res.statusMessage} from ${req.socket.remoteAddress}`
132988
132988
  );
132989
132989
  });
@@ -137530,7 +137530,7 @@ var WebSocketApi = class {
137530
137530
 
137531
137531
  // src/api/web-api.ts
137532
137532
  var WebApi = class extends Service {
137533
- constructor(logger248, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, identityStorage, lockCredentialStorage, settingsStorage, backupService, props) {
137533
+ constructor(logger250, bridgeService, haClient, haRegistry, bridgeStorage, mappingStorage, identityStorage, lockCredentialStorage, settingsStorage, backupService, props) {
137534
137534
  super("WebApi");
137535
137535
  this.bridgeService = bridgeService;
137536
137536
  this.haClient = haClient;
@@ -137542,8 +137542,8 @@ var WebApi = class extends Service {
137542
137542
  this.settingsStorage = settingsStorage;
137543
137543
  this.backupService = backupService;
137544
137544
  this.props = props;
137545
- this.logger = logger248;
137546
- this.log = logger248.get(this);
137545
+ this.logger = logger250;
137546
+ this.log = logger250.get(this);
137547
137547
  this.accessLogger = accessLogger(this.log.createChild("Access Log"));
137548
137548
  this.startTime = Date.now();
137549
137549
  this.wsApi = new WebSocketApi(
@@ -138734,10 +138734,10 @@ function isTransientConnectError(reason) {
138734
138734
  return msg.includes("socket hang up") || msg.includes("tls") || msg.includes("TLS");
138735
138735
  }
138736
138736
  var HomeAssistantClient = class extends Service {
138737
- constructor(logger248, options) {
138737
+ constructor(logger250, options) {
138738
138738
  super("HomeAssistantClient");
138739
138739
  this.options = options;
138740
- this.log = logger248.get(this);
138740
+ this.log = logger250.get(this);
138741
138741
  }
138742
138742
  options;
138743
138743
  static Options = /* @__PURE__ */ Symbol.for("HomeAssistantClientProps");
@@ -157250,6 +157250,70 @@ function seedExistingSessionStarts(startedAt, sessions, now = Date.now()) {
157250
157250
  }
157251
157251
  }
157252
157252
 
157253
+ // src/services/bridges/subscription-summary.ts
157254
+ function readPaths(value) {
157255
+ return Array.isArray(value) ? value : null;
157256
+ }
157257
+ function unknownSummary(id) {
157258
+ return {
157259
+ id,
157260
+ attributePaths: 0,
157261
+ eventPaths: 0,
157262
+ scope: "unknown",
157263
+ endpointIds: []
157264
+ };
157265
+ }
157266
+ function summarizeSubscription(sub) {
157267
+ let id = null;
157268
+ let request;
157269
+ try {
157270
+ const s = sub;
157271
+ if (typeof s.subscriptionId === "number") {
157272
+ id = s.subscriptionId;
157273
+ }
157274
+ request = s.request;
157275
+ } catch {
157276
+ return unknownSummary(id);
157277
+ }
157278
+ if (request == null || typeof request !== "object") {
157279
+ return unknownSummary(id);
157280
+ }
157281
+ const req = request;
157282
+ const attrPaths = readPaths(req.attributeRequests);
157283
+ const eventPaths = readPaths(req.eventRequests);
157284
+ if (attrPaths === null && eventPaths === null) {
157285
+ return unknownSummary(id);
157286
+ }
157287
+ const all = [...attrPaths ?? [], ...eventPaths ?? []];
157288
+ let wildcard = false;
157289
+ const endpointIds = /* @__PURE__ */ new Set();
157290
+ for (const path14 of all) {
157291
+ const ep = path14?.endpointId;
157292
+ if (ep == null) {
157293
+ wildcard = true;
157294
+ } else if (typeof ep === "number" || typeof ep === "bigint") {
157295
+ endpointIds.add(Number(ep));
157296
+ }
157297
+ }
157298
+ return {
157299
+ id,
157300
+ attributePaths: attrPaths?.length ?? 0,
157301
+ eventPaths: eventPaths?.length ?? 0,
157302
+ scope: wildcard ? "wildcard" : "endpoint-specific",
157303
+ endpointIds: [...endpointIds].sort((a, b) => a - b)
157304
+ };
157305
+ }
157306
+ function summarizeSubscriptions(subscriptions) {
157307
+ const out = [];
157308
+ try {
157309
+ for (const sub of subscriptions) {
157310
+ out.push(summarizeSubscription(sub));
157311
+ }
157312
+ } catch {
157313
+ }
157314
+ return out;
157315
+ }
157316
+
157253
157317
  // src/services/bridges/wedge-watchdog.ts
157254
157318
  var WEDGE_WARMUP_MS = 15 * 60 * 1e3;
157255
157319
  var WEDGE_IM_SILENCE_MS = 45 * 60 * 1e3;
@@ -157271,11 +157335,11 @@ var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
157271
157335
  var SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS = 2500;
157272
157336
  var MDNS_ADDRESS_CHECK_INTERVAL_MS = 6e4;
157273
157337
  var Bridge = class {
157274
- constructor(env, logger248, dataProvider, endpointManager, serverOptions) {
157338
+ constructor(env, logger250, dataProvider, endpointManager, serverOptions) {
157275
157339
  this.dataProvider = dataProvider;
157276
157340
  this.endpointManager = endpointManager;
157277
157341
  this.serverOptions = serverOptions;
157278
- this.log = logger248.get(`Bridge / ${dataProvider.id}`);
157342
+ this.log = logger250.get(`Bridge / ${dataProvider.id}`);
157279
157343
  this.server = new BridgeServerNode(
157280
157344
  env,
157281
157345
  this.dataProvider,
@@ -157366,6 +157430,7 @@ var Bridge = class {
157366
157430
  const sessionList = sessions.map((s) => {
157367
157431
  const subCount = s.subscriptions.size;
157368
157432
  totalSubscriptions += subCount;
157433
+ const subscriptions = summarizeSubscriptions(s.subscriptions);
157369
157434
  const fi = typeof s.fabric?.fabricIndex === "number" ? s.fabric.fabricIndex : null;
157370
157435
  if (fi !== null) {
157371
157436
  const existing = fabricMap.get(fi) ?? {
@@ -157387,6 +157452,7 @@ var Bridge = class {
157387
157452
  peerNodeId: String(s.peerNodeId),
157388
157453
  fabricIndex: fi,
157389
157454
  subscriptionCount: subCount,
157455
+ subscriptions,
157390
157456
  lastActiveMsAgo,
157391
157457
  lastAnyActivityMsAgo,
157392
157458
  lastImRequestMsAgo,
@@ -172891,18 +172957,32 @@ var CameraAvStreamServer = class extends Base4 {
172891
172957
  })(CameraAvStreamServer || (CameraAvStreamServer = {}));
172892
172958
 
172893
172959
  // src/plugins/builtin/camera/webrtc-provider-server.ts
172960
+ init_esm();
172894
172961
  init_esm7();
172895
172962
  init_esm3();
172963
+ var logger244 = Logger.get("CameraWebRtc");
172896
172964
  var CameraWebRtcProviderServer = class extends WebRtcTransportProviderServer {
172897
172965
  solicitOffer(request) {
172898
172966
  const id = this.state.nextSessionId++;
172899
172967
  this.trackSession(id, request.streamUsage, request.originatingEndpointId);
172900
- void this.state.bridge.startSession(id, this.state.entityId).catch(() => {
172901
- });
172968
+ logger244.info(
172969
+ `solicitOffer session=${id} (${this.state.entityId}), deferred offer`
172970
+ );
172971
+ void this.state.bridge.startSession(id, this.state.entityId, {
172972
+ iceServers: request.iceServers,
172973
+ iceTransportPolicy: request.iceTransportPolicy
172974
+ }).catch(
172975
+ (err) => logger244.info(
172976
+ `solicitOffer startSession failed for ${this.state.entityId}: ${errText(err)}`
172977
+ )
172978
+ );
172902
172979
  return { webRtcSessionId: id, deferredOffer: true };
172903
172980
  }
172904
172981
  async provideOffer(request) {
172905
172982
  const id = request.webRtcSessionId ?? this.state.nextSessionId++;
172983
+ logger244.info(
172984
+ `provideOffer entry: entityId=${this.state.entityId} session=${id} (sdp ${request.sdp.length} chars)`
172985
+ );
172906
172986
  if (request.webRtcSessionId == null) {
172907
172987
  this.trackSession(
172908
172988
  id,
@@ -172910,30 +172990,62 @@ var CameraWebRtcProviderServer = class extends WebRtcTransportProviderServer {
172910
172990
  request.originatingEndpointId ?? EndpointNumber(0)
172911
172991
  );
172912
172992
  }
172913
- const answerSdp = await this.state.bridge.acceptControllerOffer(
172914
- id,
172915
- this.state.entityId,
172916
- request.sdp
172993
+ let answerSdp;
172994
+ try {
172995
+ answerSdp = await this.state.bridge.acceptControllerOffer(
172996
+ id,
172997
+ this.state.entityId,
172998
+ request.sdp,
172999
+ {
173000
+ iceServers: request.iceServers,
173001
+ iceTransportPolicy: request.iceTransportPolicy
173002
+ }
173003
+ );
173004
+ } catch (err) {
173005
+ const message = errText(err);
173006
+ logger244.info(
173007
+ `provideOffer failed for ${this.state.entityId} session=${id}: ${message}`
173008
+ );
173009
+ this.state.currentSessions = this.state.currentSessions.filter(
173010
+ (s) => s.id !== id
173011
+ );
173012
+ throw new StatusResponseError(
173013
+ `WebRTC offer failed: ${message}`,
173014
+ StatusCode.Failure
173015
+ );
173016
+ }
173017
+ logger244.info(
173018
+ `provideOffer answer computed for ${this.state.entityId} session=${id} (${answerSdp.length} chars); awaiting requestor delivery path`
172917
173019
  );
172918
173020
  void answerSdp;
172919
173021
  return { webRtcSessionId: id };
172920
173022
  }
172921
173023
  provideAnswer(request) {
173024
+ logger244.info(
173025
+ `provideAnswer session=${request.webRtcSessionId} (sdp ${request.sdp.length} chars, ${this.state.entityId})`
173026
+ );
172922
173027
  return this.state.bridge.acceptControllerAnswer(
172923
173028
  request.webRtcSessionId,
172924
173029
  request.sdp
172925
173030
  );
172926
173031
  }
172927
173032
  async provideIceCandidates(request) {
173033
+ logger244.info(
173034
+ `provideIceCandidates session=${request.webRtcSessionId}: ${request.iceCandidates.length} candidate(s) (${this.state.entityId})`
173035
+ );
172928
173036
  for (const c of request.iceCandidates) {
172929
173037
  await this.state.bridge.addControllerIceCandidate(
172930
173038
  request.webRtcSessionId,
172931
173039
  c.candidate,
172932
- c.sdpMid
173040
+ c.sdpMid,
173041
+ c.sdpmLineIndex
172933
173042
  );
172934
173043
  }
172935
173044
  }
172936
173045
  async endSession(request) {
173046
+ logger244.info(
173047
+ `endSession session=${request.webRtcSessionId} (${this.state.entityId})`
173048
+ );
172937
173049
  await this.state.bridge.endSession(request.webRtcSessionId);
172938
173050
  this.state.currentSessions = this.state.currentSessions.filter(
172939
173051
  (s) => s.id !== request.webRtcSessionId
@@ -172954,6 +173066,9 @@ var CameraWebRtcProviderServer = class extends WebRtcTransportProviderServer {
172954
173066
  ];
172955
173067
  }
172956
173068
  };
173069
+ function errText(err) {
173070
+ return err instanceof Error ? err.message : String(err);
173071
+ }
172957
173072
  ((CameraWebRtcProviderServer2) => {
172958
173073
  class State extends WebRtcTransportProviderServer.State {
172959
173074
  bridge;
@@ -173040,108 +173155,180 @@ function createCameraEndpointType(bridge, entityId, sensor = defaultSensorParams
173040
173155
  }
173041
173156
 
173042
173157
  // src/plugins/builtin/camera/webrtc-bridge.ts
173158
+ init_esm();
173043
173159
  import {
173044
173160
  createConnection as createConnection3,
173045
173161
  createLongLivedTokenAuth as createLongLivedTokenAuth2
173046
173162
  } from "home-assistant-js-websocket";
173047
173163
  import { RTCPeerConnection } from "werift";
173164
+ var logger245 = Logger.get("CameraWebRtc");
173165
+ var DEFAULT_HA_WEBRTC_TIMEOUT_MS = 15e3;
173166
+ var haWebRtcTimeoutMs = DEFAULT_HA_WEBRTC_TIMEOUT_MS;
173048
173167
  var WebRtcBridge = class {
173049
- constructor(config11) {
173168
+ constructor(config11, deps = {}) {
173050
173169
  this.config = config11;
173170
+ this.deps = deps;
173051
173171
  }
173052
173172
  config;
173173
+ deps;
173053
173174
  connection;
173054
173175
  sessions = /* @__PURE__ */ new Map();
173055
173176
  async ha() {
173056
173177
  if (this.connection) return this.connection;
173057
- const auth = createLongLivedTokenAuth2(
173058
- this.config.haUrl,
173059
- this.config.haToken
173060
- );
173061
- this.connection = await createConnection3({ auth });
173178
+ if (this.deps.connect) {
173179
+ this.connection = await this.deps.connect(this.config);
173180
+ } else {
173181
+ const auth = createLongLivedTokenAuth2(
173182
+ this.config.haUrl,
173183
+ this.config.haToken
173184
+ );
173185
+ this.connection = await createConnection3({ auth });
173186
+ }
173062
173187
  return this.connection;
173063
173188
  }
173064
173189
  // We offer to the controller; it answers. Pulls HA media first, then forwards.
173065
- async startSession(matterSessionId, entityId) {
173190
+ async startSession(matterSessionId, entityId, ice) {
173066
173191
  const haPeer = new RTCPeerConnection();
173067
- const controllerPeer = new RTCPeerConnection();
173068
- haPeer.onTrack.subscribe((track) => {
173069
- const transceiver = controllerPeer.addTransceiver(
173070
- track.kind,
173071
- { direction: "sendonly" }
173192
+ const controllerPeer = new RTCPeerConnection(
173193
+ this.controllerConfig(entityId, ice)
173194
+ );
173195
+ this.wireStateLogging(haPeer, "haPeer", entityId);
173196
+ this.wireStateLogging(controllerPeer, "controllerPeer", entityId);
173197
+ const senders = /* @__PURE__ */ new Map();
173198
+ senders.set(
173199
+ "video",
173200
+ controllerPeer.addTransceiver("video", { direction: "sendonly" })
173201
+ );
173202
+ this.forwardHaTracks(haPeer, senders, entityId);
173203
+ this.sessions.set(matterSessionId, { entityId, haPeer, controllerPeer });
173204
+ try {
173205
+ haPeer.addTransceiver("video", { direction: "recvonly" });
173206
+ haPeer.addTransceiver("audio", { direction: "recvonly" });
173207
+ const haOffer = await haPeer.createOffer();
173208
+ await haPeer.setLocalDescription(haOffer);
173209
+ const haOfferSdp = this.localSdp(haPeer);
173210
+ logger245.info(`HA offer sent for ${entityId} (${haOfferSdp.length} chars)`);
173211
+ const { answer, sessionId, unsubscribe } = await this.requestHaWebRtc(
173212
+ entityId,
173213
+ haOfferSdp,
173214
+ haPeer
173072
173215
  );
173073
- track.onReceiveRtp.subscribe((rtp) => transceiver.sender.sendRtp(rtp));
173074
- });
173075
- haPeer.addTransceiver("video", { direction: "recvonly" });
173076
- haPeer.addTransceiver("audio", { direction: "recvonly" });
173077
- const haOffer = await haPeer.createOffer();
173078
- await haPeer.setLocalDescription(haOffer);
173079
- const haAnswer = await this.requestHaWebRtc(entityId, haOffer.sdp);
173080
- await haPeer.setRemoteDescription({ type: "answer", sdp: haAnswer });
173081
- const controllerOffer = await controllerPeer.createOffer();
173082
- await controllerPeer.setLocalDescription(controllerOffer);
173083
- const iceCandidates = [];
173084
- controllerPeer.onIceCandidate.subscribe((c) => {
173085
- if (c?.candidate)
173086
- iceCandidates.push({
173087
- candidate: c.candidate,
173088
- sdpMid: c.sdpMid ?? null
173089
- });
173090
- });
173091
- this.sessions.set(matterSessionId, {
173092
- entityId,
173093
- haPeer,
173094
- controllerPeer
173095
- });
173096
- return { sdp: controllerOffer.sdp, iceCandidates };
173216
+ const session = this.sessions.get(matterSessionId);
173217
+ if (session) {
173218
+ session.haSessionId = sessionId;
173219
+ session.haUnsubscribe = unsubscribe;
173220
+ }
173221
+ await haPeer.setRemoteDescription({ type: "answer", sdp: answer });
173222
+ const iceCandidates = [];
173223
+ controllerPeer.onIceCandidate.subscribe((c) => {
173224
+ if (c?.candidate)
173225
+ iceCandidates.push({
173226
+ candidate: c.candidate,
173227
+ sdpMid: c.sdpMid ?? null
173228
+ });
173229
+ });
173230
+ const controllerOffer = await controllerPeer.createOffer();
173231
+ await controllerPeer.setLocalDescription(controllerOffer);
173232
+ const sdp = this.localSdp(controllerPeer);
173233
+ logger245.info(
173234
+ `controller offer created for ${entityId} (${sdp.length} chars)`
173235
+ );
173236
+ return { sdp, iceCandidates };
173237
+ } catch (err) {
173238
+ logger245.info(`startSession failed for ${entityId}: ${errText2(err)}`);
173239
+ await this.endSession(matterSessionId);
173240
+ throw err;
173241
+ }
173097
173242
  }
173098
173243
  // ProvideOffer flow: controller sent its offer. Pull HA media, answer it.
173099
173244
  // Returning the answer to the controller is the unverified Requestor path.
173100
- async acceptControllerOffer(matterSessionId, entityId, controllerOfferSdp) {
173245
+ async acceptControllerOffer(matterSessionId, entityId, controllerOfferSdp, ice) {
173101
173246
  const haPeer = new RTCPeerConnection();
173102
- const controllerPeer = new RTCPeerConnection();
173103
- haPeer.onTrack.subscribe((track) => {
173104
- const transceiver = controllerPeer.addTransceiver(track.kind, {
173105
- direction: "sendonly"
173106
- });
173107
- track.onReceiveRtp.subscribe((rtp) => transceiver.sender.sendRtp(rtp));
173108
- });
173247
+ const controllerPeer = new RTCPeerConnection(
173248
+ this.controllerConfig(entityId, ice)
173249
+ );
173250
+ this.wireStateLogging(haPeer, "haPeer", entityId);
173251
+ this.wireStateLogging(controllerPeer, "controllerPeer", entityId);
173252
+ const senders = /* @__PURE__ */ new Map();
173253
+ for (const kind of offerKinds(controllerOfferSdp)) {
173254
+ senders.set(
173255
+ kind,
173256
+ controllerPeer.addTransceiver(kind, { direction: "sendonly" })
173257
+ );
173258
+ }
173259
+ this.forwardHaTracks(haPeer, senders, entityId);
173109
173260
  this.sessions.set(matterSessionId, { entityId, haPeer, controllerPeer });
173110
- haPeer.addTransceiver("video", { direction: "recvonly" });
173111
- haPeer.addTransceiver("audio", { direction: "recvonly" });
173112
- const haOffer = await haPeer.createOffer();
173113
- await haPeer.setLocalDescription(haOffer);
173114
- const haAnswer = await this.requestHaWebRtc(entityId, haOffer.sdp);
173115
- await haPeer.setRemoteDescription({ type: "answer", sdp: haAnswer });
173116
- await controllerPeer.setRemoteDescription({
173117
- type: "offer",
173118
- sdp: controllerOfferSdp
173119
- });
173120
- const answer = await controllerPeer.createAnswer();
173121
- await controllerPeer.setLocalDescription(answer);
173122
- return answer.sdp;
173261
+ try {
173262
+ haPeer.addTransceiver("video", { direction: "recvonly" });
173263
+ haPeer.addTransceiver("audio", { direction: "recvonly" });
173264
+ const haOffer = await haPeer.createOffer();
173265
+ await haPeer.setLocalDescription(haOffer);
173266
+ const haOfferSdp = this.localSdp(haPeer);
173267
+ logger245.info(`HA offer sent for ${entityId} (${haOfferSdp.length} chars)`);
173268
+ const { answer, sessionId, unsubscribe } = await this.requestHaWebRtc(
173269
+ entityId,
173270
+ haOfferSdp,
173271
+ haPeer
173272
+ );
173273
+ const session = this.sessions.get(matterSessionId);
173274
+ if (session) {
173275
+ session.haSessionId = sessionId;
173276
+ session.haUnsubscribe = unsubscribe;
173277
+ }
173278
+ await haPeer.setRemoteDescription({ type: "answer", sdp: answer });
173279
+ await controllerPeer.setRemoteDescription({
173280
+ type: "offer",
173281
+ sdp: controllerOfferSdp
173282
+ });
173283
+ const controllerAnswer = await controllerPeer.createAnswer();
173284
+ await controllerPeer.setLocalDescription(controllerAnswer);
173285
+ const answerSdp = this.localSdp(controllerPeer);
173286
+ logger245.info(
173287
+ `controller answer created for ${entityId} (${answerSdp.length} chars)`
173288
+ );
173289
+ return answerSdp;
173290
+ } catch (err) {
173291
+ logger245.info(
173292
+ `acceptControllerOffer failed for ${entityId}: ${errText2(err)}`
173293
+ );
173294
+ await this.endSession(matterSessionId);
173295
+ throw err;
173296
+ }
173123
173297
  }
173124
173298
  /** Apply the Matter controller's SDP answer to the controller peer. */
173125
173299
  async acceptControllerAnswer(matterSessionId, sdp) {
173126
173300
  const session = this.sessions.get(matterSessionId);
173127
173301
  if (!session) return;
173302
+ logger245.debug(`controller answer applied for ${session.entityId}`);
173128
173303
  await session.controllerPeer.setRemoteDescription({ type: "answer", sdp });
173129
173304
  }
173130
173305
  /** Add a remote ICE candidate from the Matter controller. */
173131
- async addControllerIceCandidate(matterSessionId, candidate, sdpMid) {
173306
+ async addControllerIceCandidate(matterSessionId, candidate, sdpMid, sdpMLineIndex) {
173132
173307
  const session = this.sessions.get(matterSessionId);
173133
173308
  if (!session) return;
173309
+ logger245.debug(
173310
+ `controller ICE candidate for ${session.entityId}: ${candidate}`
173311
+ );
173134
173312
  await session.controllerPeer.addIceCandidate({
173135
173313
  candidate,
173136
- sdpMid: sdpMid ?? void 0
173314
+ sdpMid: sdpMid ?? void 0,
173315
+ sdpMLineIndex: sdpMLineIndex ?? void 0
173137
173316
  });
173138
173317
  }
173139
173318
  async endSession(matterSessionId) {
173140
173319
  const session = this.sessions.get(matterSessionId);
173141
173320
  if (!session) return;
173321
+ logger245.info(`ending session ${matterSessionId} (${session.entityId})`);
173142
173322
  this.sessions.delete(matterSessionId);
173143
- await session.haPeer.close();
173144
- await session.controllerPeer.close();
173323
+ if (session.haUnsubscribe) {
173324
+ await Promise.resolve(session.haUnsubscribe()).catch(
173325
+ (err) => logger245.debug(`HA unsubscribe failed: ${errText2(err)}`)
173326
+ );
173327
+ }
173328
+ await session.haPeer.close().catch(() => {
173329
+ });
173330
+ await session.controllerPeer.close().catch(() => {
173331
+ });
173145
173332
  if (session.haSessionId) {
173146
173333
  try {
173147
173334
  const conn = await this.ha();
@@ -173158,10 +173345,19 @@ var WebRtcBridge = class {
173158
173345
  // Grab a still JPEG via HA's camera proxy (for CaptureSnapshot).
173159
173346
  async snapshot(entityId) {
173160
173347
  const url2 = `${this.config.haUrl}/api/camera_proxy/${entityId}`;
173161
- const res = await fetch(url2, {
173162
- headers: { Authorization: `Bearer ${this.config.haToken}` }
173163
- });
173348
+ let res;
173349
+ try {
173350
+ res = await fetch(url2, {
173351
+ headers: { Authorization: `Bearer ${this.config.haToken}` }
173352
+ });
173353
+ } catch (err) {
173354
+ logger245.info(`snapshot fetch failed for ${entityId}: ${errText2(err)}`);
173355
+ throw err;
173356
+ }
173164
173357
  if (!res.ok) {
173358
+ logger245.info(
173359
+ `snapshot fetch failed for ${entityId}: camera_proxy ${res.status}`
173360
+ );
173165
173361
  throw new Error(`HA camera_proxy ${entityId}: ${res.status}`);
173166
173362
  }
173167
173363
  return new Uint8Array(await res.arrayBuffer());
@@ -173173,29 +173369,157 @@ var WebRtcBridge = class {
173173
173369
  this.connection?.close();
173174
173370
  this.connection = void 0;
173175
173371
  }
173176
- // Offer to HA's WebRTC, resolve on the first answer event off the same sub.
173177
- async requestHaWebRtc(entityId, offerSdp) {
173372
+ // Forward every track HA sends into the matching pre-added controller sender.
173373
+ forwardHaTracks(haPeer, senders, entityId) {
173374
+ haPeer.onTrack.subscribe((track) => {
173375
+ logger245.info(`onTrack ${track.kind} from HA (${entityId})`);
173376
+ const transceiver = senders.get(track.kind);
173377
+ if (!transceiver) {
173378
+ logger245.info(
173379
+ `no controller transceiver for ${track.kind} (${entityId})`
173380
+ );
173381
+ return;
173382
+ }
173383
+ let firstLogged = false;
173384
+ track.onReceiveRtp.subscribe((rtp) => {
173385
+ if (!firstLogged) {
173386
+ firstLogged = true;
173387
+ logger245.info(`first RTP forwarded for ${track.kind} (${entityId})`);
173388
+ }
173389
+ transceiver.sender.sendRtp(rtp);
173390
+ });
173391
+ });
173392
+ }
173393
+ // Log connection/ice state transitions (these events fire on change only).
173394
+ wireStateLogging(peer, label, entityId) {
173395
+ peer.connectionStateChange.subscribe(
173396
+ (s) => logger245.info(`${label} connectionState=${s} (${entityId})`)
173397
+ );
173398
+ peer.iceConnectionStateChange.subscribe(
173399
+ (s) => logger245.info(`${label} iceConnectionState=${s} (${entityId})`)
173400
+ );
173401
+ }
173402
+ // Map the Matter ICE struct onto werift's config. Only the controller peer
173403
+ // uses controller-supplied servers; the HA peer keeps werift defaults.
173404
+ controllerConfig(entityId, ice) {
173405
+ const iceServers = (ice?.iceServers ?? []).flatMap(
173406
+ (s) => (s.urLs ?? []).map((url2) => ({
173407
+ urls: url2,
173408
+ username: s.username,
173409
+ credential: s.credential
173410
+ }))
173411
+ );
173412
+ if (iceServers.length === 0) {
173413
+ logger245.info(`controller peer using default ICE servers (${entityId})`);
173414
+ return void 0;
173415
+ }
173416
+ const policy = ice?.iceTransportPolicy === "relay" || ice?.iceTransportPolicy === "all" ? ice.iceTransportPolicy : void 0;
173417
+ logger245.info(
173418
+ `controller peer using ${iceServers.length} controller ICE server(s)${policy ? ` policy=${policy}` : ""} (${entityId})`
173419
+ );
173420
+ return policy ? { iceServers, iceTransportPolicy: policy } : { iceServers };
173421
+ }
173422
+ localSdp(peer) {
173423
+ const local = peer.localDescription;
173424
+ if (!local) throw new Error("no local description");
173425
+ return local.sdp;
173426
+ }
173427
+ // Feed an HA trickle-ICE candidate into the HA peer.
173428
+ feedHaCandidate(entityId, haPeer, candidate) {
173429
+ const value = candidate?.candidate;
173430
+ if (!value) return;
173431
+ logger245.debug(`HA ICE candidate for ${entityId}: ${value}`);
173432
+ void haPeer.addIceCandidate({
173433
+ candidate: value,
173434
+ sdpMid: candidate?.sdpMid ?? void 0,
173435
+ sdpMLineIndex: candidate?.sdpMLineIndex ?? void 0
173436
+ }).catch(
173437
+ (e) => logger245.debug(
173438
+ `HA addIceCandidate failed for ${entityId}: ${errText2(e)}`
173439
+ )
173440
+ );
173441
+ }
173442
+ // Offer to HA's WebRTC and resolve on the first answer. Rejects on an error
173443
+ // reply or after a timeout; keeps the subscription open past the answer so HA
173444
+ // trickle candidates keep flowing into the HA peer until the session ends.
173445
+ async requestHaWebRtc(entityId, offerSdp, haPeer) {
173178
173446
  const conn = await this.ha();
173179
- return new Promise((resolve11, reject) => {
173447
+ let unsubscribe = () => {
173448
+ };
173449
+ let sessionId;
173450
+ const answer = await new Promise((resolve11, reject) => {
173180
173451
  let settled = false;
173452
+ let rejected = false;
173453
+ let timer;
173454
+ const settle = (fn) => {
173455
+ if (settled) return;
173456
+ settled = true;
173457
+ clearTimeout(timer);
173458
+ fn();
173459
+ };
173460
+ const fail = (err) => settle(() => {
173461
+ rejected = true;
173462
+ unsubscribe();
173463
+ reject(err);
173464
+ });
173465
+ timer = setTimeout(() => {
173466
+ logger245.info(
173467
+ `HA WebRTC timed out after ${haWebRtcTimeoutMs}ms for ${entityId}`
173468
+ );
173469
+ fail(
173470
+ new Error(`HA WebRTC request timed out after ${haWebRtcTimeoutMs}ms`)
173471
+ );
173472
+ }, haWebRtcTimeoutMs);
173181
173473
  conn.subscribeMessage(
173182
173474
  (msg) => {
173183
- if (msg.type === "answer" && msg.answer && !settled) {
173184
- settled = true;
173185
- const session = [...this.sessions.values()].find(
173186
- (s) => s.entityId === entityId
173187
- );
173188
- if (session && msg.session_id) {
173189
- session.haSessionId = msg.session_id;
173475
+ switch (msg.type) {
173476
+ case "session":
173477
+ if (msg.session_id) sessionId = msg.session_id;
173478
+ break;
173479
+ case "answer":
173480
+ if (msg.answer) {
173481
+ if (msg.session_id) sessionId = msg.session_id;
173482
+ const ans = msg.answer;
173483
+ settle(() => {
173484
+ logger245.info(
173485
+ `HA answer received for ${entityId} (${ans.length} chars)`
173486
+ );
173487
+ resolve11(ans);
173488
+ });
173489
+ }
173490
+ break;
173491
+ case "error": {
173492
+ const detail = msg.message ?? msg.code ?? "unknown error";
173493
+ logger245.info(`HA WebRTC error for ${entityId}: ${detail}`);
173494
+ fail(new Error(`HA WebRTC error: ${detail}`));
173495
+ break;
173190
173496
  }
173191
- resolve11(msg.answer);
173497
+ case "candidate":
173498
+ this.feedHaCandidate(entityId, haPeer, msg.candidate);
173499
+ break;
173192
173500
  }
173193
173501
  },
173194
173502
  { type: "camera/webrtc/offer", entity_id: entityId, offer: offerSdp }
173195
- ).catch(reject);
173503
+ ).then((unsub) => {
173504
+ unsubscribe = unsub;
173505
+ if (rejected) unsub();
173506
+ }).catch((err) => {
173507
+ fail(err instanceof Error ? err : new Error(String(err)));
173508
+ });
173196
173509
  });
173510
+ return { answer, sessionId, unsubscribe };
173197
173511
  }
173198
173512
  };
173513
+ function offerKinds(sdp) {
173514
+ const kinds = [];
173515
+ for (const m of sdp.matchAll(/^m=(video|audio)/gm)) {
173516
+ kinds.push(m[1]);
173517
+ }
173518
+ return kinds;
173519
+ }
173520
+ function errText2(err) {
173521
+ return err instanceof Error ? err.message : String(err);
173522
+ }
173199
173523
 
173200
173524
  // src/plugins/builtin/camera/camera-plugin.ts
173201
173525
  var CONFIG_KEY = "config";
@@ -173341,7 +173665,7 @@ import {
173341
173665
  getCollection
173342
173666
  } from "home-assistant-js-websocket";
173343
173667
  import { atLeastHaVersion } from "home-assistant-js-websocket/dist/util.js";
173344
- var logger244 = Logger.get("SubscribeEntities");
173668
+ var logger246 = Logger.get("SubscribeEntities");
173345
173669
  function processEvent(store, updates) {
173346
173670
  const state = { ...store.state };
173347
173671
  if (updates.a) {
@@ -173367,7 +173691,7 @@ function processEvent(store, updates) {
173367
173691
  for (const entityId in updates.c) {
173368
173692
  let entityState = state[entityId];
173369
173693
  if (!entityState) {
173370
- logger244.warn("Received state update for unknown entity", entityId);
173694
+ logger246.warn("Received state update for unknown entity", entityId);
173371
173695
  continue;
173372
173696
  }
173373
173697
  entityState = { ...entityState };
@@ -173456,7 +173780,7 @@ var subscribeEntities = (conn, onChange, entityIds) => {
173456
173780
  // src/services/bridges/entity-isolation-service.ts
173457
173781
  init_esm();
173458
173782
  init_diagnostic_event_bus();
173459
- var logger245 = Logger.get("EntityIsolation");
173783
+ var logger247 = Logger.get("EntityIsolation");
173460
173784
  var EntityIsolationServiceImpl = class {
173461
173785
  isolatedEntities = /* @__PURE__ */ new Map();
173462
173786
  isolationCallbacks = /* @__PURE__ */ new Map();
@@ -173521,13 +173845,13 @@ var EntityIsolationServiceImpl = class {
173521
173845
  }
173522
173846
  const parsed = this.parseEndpointPath(msg);
173523
173847
  if (!parsed) {
173524
- logger245.warn("Could not parse entity from error:", msg);
173848
+ logger247.warn("Could not parse entity from error:", msg);
173525
173849
  return false;
173526
173850
  }
173527
173851
  const { bridgeId, entityName } = parsed;
173528
173852
  const callback = this.isolationCallbacks.get(bridgeId);
173529
173853
  if (!callback) {
173530
- logger245.warn(
173854
+ logger247.warn(
173531
173855
  `No isolation callback registered for bridge ${bridgeId}, entity: ${entityName}`
173532
173856
  );
173533
173857
  return false;
@@ -173542,7 +173866,7 @@ var EntityIsolationServiceImpl = class {
173542
173866
  reason,
173543
173867
  failedAt: (/* @__PURE__ */ new Date()).toISOString()
173544
173868
  });
173545
- logger245.warn(
173869
+ logger247.warn(
173546
173870
  `Isolating entity "${entityName}" from bridge ${bridgeId} due to: ${reason}`
173547
173871
  );
173548
173872
  diagnosticEventBus.emit("entity_error", `Entity isolated: ${entityName}`, {
@@ -173554,7 +173878,7 @@ var EntityIsolationServiceImpl = class {
173554
173878
  await callback(entityName);
173555
173879
  return true;
173556
173880
  } catch (e) {
173557
- logger245.error(`Failed to isolate entity ${entityName}:`, e);
173881
+ logger247.error(`Failed to isolate entity ${entityName}:`, e);
173558
173882
  return false;
173559
173883
  }
173560
173884
  }
@@ -175072,11 +175396,11 @@ function makeWarmStartState(state, now = (/* @__PURE__ */ new Date()).toISOStrin
175072
175396
  return { ...state, last_updated: now };
175073
175397
  }
175074
175398
  var ServerModeBridge = class {
175075
- constructor(logger248, dataProvider, endpointManager, server) {
175399
+ constructor(logger250, dataProvider, endpointManager, server) {
175076
175400
  this.dataProvider = dataProvider;
175077
175401
  this.endpointManager = endpointManager;
175078
175402
  this.server = server;
175079
- this.log = logger248.get(`ServerModeBridge / ${dataProvider.id}`);
175403
+ this.log = logger250.get(`ServerModeBridge / ${dataProvider.id}`);
175080
175404
  }
175081
175405
  dataProvider;
175082
175406
  endpointManager;
@@ -175166,6 +175490,7 @@ var ServerModeBridge = class {
175166
175490
  const sessionList = sessions.map((s) => {
175167
175491
  const subCount = s.subscriptions.size;
175168
175492
  totalSubscriptions += subCount;
175493
+ const subscriptions = summarizeSubscriptions(s.subscriptions);
175169
175494
  const fi = typeof s.fabric?.fabricIndex === "number" ? s.fabric.fabricIndex : null;
175170
175495
  if (fi !== null) {
175171
175496
  const existing = fabricMap.get(fi) ?? {
@@ -175187,6 +175512,7 @@ var ServerModeBridge = class {
175187
175512
  peerNodeId: String(s.peerNodeId),
175188
175513
  fabricIndex: fi,
175189
175514
  subscriptionCount: subCount,
175515
+ subscriptions,
175190
175516
  lastActiveMsAgo,
175191
175517
  lastAnyActivityMsAgo,
175192
175518
  lastImRequestMsAgo,
@@ -176104,7 +176430,7 @@ function ServerModeVacuumDevice(homeAssistantEntity, includeOnOff = false, clean
176104
176430
  }
176105
176431
 
176106
176432
  // src/matter/endpoints/server-mode-vacuum-endpoint.ts
176107
- var logger246 = Logger.get("ServerModeVacuumEndpoint");
176433
+ var logger248 = Logger.get("ServerModeVacuumEndpoint");
176108
176434
  var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEndpoint {
176109
176435
  static async create(registry2, entityId, mapping, endpointId) {
176110
176436
  const deviceRegistry = registry2.deviceOf(entityId);
@@ -176117,7 +176443,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176117
176443
  if (mapping?.disableBatteryMapping && mapping.batteryEntity) {
176118
176444
  effectiveMapping = { ...mapping, batteryEntity: void 0 };
176119
176445
  }
176120
- logger246.info(
176446
+ logger248.info(
176121
176447
  `${entityId}: device_id=${entity.device_id}, manualBattery=${mapping?.batteryEntity ?? "none"}`
176122
176448
  );
176123
176449
  if (entity.device_id) {
@@ -176132,15 +176458,15 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176132
176458
  batteryEntity: batteryEntityId
176133
176459
  };
176134
176460
  registry2.markBatteryEntityUsed(batteryEntityId);
176135
- logger246.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
176461
+ logger248.info(`${entityId}: Auto-assigned battery ${batteryEntityId}`);
176136
176462
  } else {
176137
176463
  const attrs = state.attributes;
176138
176464
  if (attrs.battery_level != null || attrs.battery != null) {
176139
- logger246.info(
176465
+ logger248.info(
176140
176466
  `${entityId}: No battery entity found, using battery attribute from vacuum state`
176141
176467
  );
176142
176468
  } else {
176143
- logger246.warn(
176469
+ logger248.warn(
176144
176470
  `${entityId}: No battery entity found for device ${entity.device_id}`
176145
176471
  );
176146
176472
  }
@@ -176155,7 +176481,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176155
176481
  entityId: effectiveMapping?.entityId ?? entityId,
176156
176482
  cleaningModeEntity: vacuumEntities.cleaningModeEntity
176157
176483
  };
176158
- logger246.info(
176484
+ logger248.info(
176159
176485
  `${entityId}: Auto-assigned cleaningMode ${vacuumEntities.cleaningModeEntity}`
176160
176486
  );
176161
176487
  }
@@ -176165,7 +176491,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176165
176491
  entityId: effectiveMapping?.entityId ?? entityId,
176166
176492
  suctionLevelEntity: vacuumEntities.suctionLevelEntity
176167
176493
  };
176168
- logger246.info(
176494
+ logger248.info(
176169
176495
  `${entityId}: Auto-assigned suctionLevel ${vacuumEntities.suctionLevelEntity}`
176170
176496
  );
176171
176497
  }
@@ -176175,7 +176501,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176175
176501
  entityId: effectiveMapping?.entityId ?? entityId,
176176
176502
  mopIntensityEntity: vacuumEntities.mopIntensityEntity
176177
176503
  };
176178
- logger246.info(
176504
+ logger248.info(
176179
176505
  `${entityId}: Auto-assigned mopIntensity ${vacuumEntities.mopIntensityEntity}`
176180
176506
  );
176181
176507
  }
@@ -176185,7 +176511,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176185
176511
  entityId: effectiveMapping?.entityId ?? entityId,
176186
176512
  currentRoomEntity: vacuumEntities.currentRoomEntity
176187
176513
  };
176188
- logger246.info(
176514
+ logger248.info(
176189
176515
  `${entityId}: Auto-assigned currentRoom ${vacuumEntities.currentRoomEntity}`
176190
176516
  );
176191
176517
  }
@@ -176200,7 +176526,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176200
176526
  entityId: effectiveMapping?.entityId ?? entityId,
176201
176527
  cleanAreaRooms
176202
176528
  };
176203
- logger246.info(
176529
+ logger248.info(
176204
176530
  `${entityId}: Using ${cleanAreaRooms.length} HA areas via CLEAN_AREA`
176205
176531
  );
176206
176532
  }
@@ -176221,7 +176547,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176221
176547
  rooms: roomsObj
176222
176548
  }
176223
176549
  };
176224
- logger246.info(
176550
+ logger248.info(
176225
176551
  `${entityId}: Auto-detected ${valetudoRooms.length} Valetudo segments`
176226
176552
  );
176227
176553
  } else {
@@ -176238,14 +176564,14 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176238
176564
  rooms: roomsObj
176239
176565
  }
176240
176566
  };
176241
- logger246.info(
176567
+ logger248.info(
176242
176568
  `${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
176243
176569
  );
176244
176570
  }
176245
176571
  }
176246
176572
  }
176247
176573
  } else {
176248
- logger246.warn(`${entityId}: No device_id, cannot auto-assign battery`);
176574
+ logger248.warn(`${entityId}: No device_id, cannot auto-assign battery`);
176249
176575
  }
176250
176576
  const payload = {
176251
176577
  entity_id: entityId,
@@ -176318,11 +176644,11 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
176318
176644
  }
176319
176645
  if (mappedChanged) {
176320
176646
  this.pendingMappedChange = true;
176321
- logger246.debug(
176647
+ logger248.debug(
176322
176648
  `Mapped entity change detected for ${this.entityId}, forcing update`
176323
176649
  );
176324
176650
  }
176325
- logger246.debug(
176651
+ logger248.debug(
176326
176652
  `State update received for ${this.entityId}: state=${state.state}`
176327
176653
  );
176328
176654
  this.lastState = state;
@@ -176900,10 +177226,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
176900
177226
  // src/core/ioc/app-environment.ts
176901
177227
  var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176902
177228
  constructor(rootEnv, options) {
176903
- const logger248 = rootEnv.get(LoggerService);
177229
+ const logger250 = rootEnv.get(LoggerService);
176904
177230
  super({
176905
177231
  id: "App",
176906
- log: logger248.get("AppContainer"),
177232
+ log: logger250.get("AppContainer"),
176907
177233
  parent: rootEnv
176908
177234
  });
176909
177235
  this.options = options;
@@ -176917,8 +177243,8 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176917
177243
  }
176918
177244
  construction;
176919
177245
  async init() {
176920
- const logger248 = this.get(LoggerService);
176921
- this.set(LoggerService, logger248);
177246
+ const logger250 = this.get(LoggerService);
177247
+ this.set(LoggerService, logger250);
176922
177248
  this.set(AppStorage, new AppStorage(await this.load(StorageService)));
176923
177249
  this.set(BridgeStorage, new BridgeStorage(await this.load(AppStorage)));
176924
177250
  this.set(
@@ -176939,7 +177265,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176939
177265
  );
176940
177266
  this.set(
176941
177267
  HomeAssistantClient,
176942
- new HomeAssistantClient(logger248, this.options.homeAssistant)
177268
+ new HomeAssistantClient(logger250, this.options.homeAssistant)
176943
177269
  );
176944
177270
  this.set(
176945
177271
  HomeAssistantConfig,
@@ -176947,7 +177273,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176947
177273
  );
176948
177274
  this.set(
176949
177275
  HomeAssistantActions,
176950
- new HomeAssistantActions(logger248, await this.load(HomeAssistantClient))
177276
+ new HomeAssistantActions(logger250, await this.load(HomeAssistantClient))
176951
177277
  );
176952
177278
  this.set(
176953
177279
  HomeAssistantRegistry,
@@ -176983,7 +177309,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
176983
177309
  this.set(
176984
177310
  WebApi,
176985
177311
  new WebApi(
176986
- logger248,
177312
+ logger250,
176987
177313
  await this.load(BridgeService),
176988
177314
  await this.load(HomeAssistantClient),
176989
177315
  await this.load(HomeAssistantRegistry),
@@ -177004,7 +177330,7 @@ var AppEnvironment = class _AppEnvironment extends EnvironmentBase {
177004
177330
 
177005
177331
  // src/matter/patches/patch-level-control-tlv.ts
177006
177332
  init_esm();
177007
- var logger247 = Logger.get("PatchLevelControlTlv");
177333
+ var logger249 = Logger.get("PatchLevelControlTlv");
177008
177334
  var optionalFieldModels = /* @__PURE__ */ new WeakSet();
177009
177335
  var fieldModelMandatoryGetterPatched = false;
177010
177336
  function patchLevelControlTlv() {
@@ -177060,11 +177386,11 @@ function patchLevelControlTlv() {
177060
177386
  patched++;
177061
177387
  }
177062
177388
  if (patched > 0) {
177063
- logger247.info(
177389
+ logger249.info(
177064
177390
  `Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
177065
177391
  );
177066
177392
  } else {
177067
- logger247.warn(
177393
+ logger249.warn(
177068
177394
  "Failed to patch LevelControl TLV schemas, field definitions not found. Google Home brightness adjustment may not work.",
177069
177395
  {
177070
177396
  moveToLevel: describeFields(