@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.
package/dist/backend/cli.js
CHANGED
|
@@ -1767,8 +1767,8 @@ var init_Cancelable = __esm({
|
|
|
1767
1767
|
};
|
|
1768
1768
|
return result;
|
|
1769
1769
|
}
|
|
1770
|
-
static set logger(
|
|
1771
|
-
this.#logger =
|
|
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,
|
|
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 =
|
|
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
|
|
5691
|
-
|
|
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(
|
|
64074
|
-
|
|
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
|
|
117718
|
-
if (!
|
|
117719
|
-
loggers.set(facility,
|
|
117717
|
+
let logger250 = loggers.get(facility);
|
|
117718
|
+
if (!logger250) {
|
|
117719
|
+
loggers.set(facility, logger250 = Logger.get(facility));
|
|
117720
117720
|
}
|
|
117721
|
-
|
|
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(
|
|
132634
|
+
constructor(logger250, client, config11) {
|
|
132635
132635
|
super("HomeAssistantActions");
|
|
132636
132636
|
this.client = client;
|
|
132637
|
-
this.log =
|
|
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(
|
|
132983
|
+
function accessLogger(logger250) {
|
|
132984
132984
|
return (req, res, next) => {
|
|
132985
132985
|
res.on("finish", () => {
|
|
132986
|
-
|
|
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(
|
|
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 =
|
|
137546
|
-
this.log =
|
|
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(
|
|
138737
|
+
constructor(logger250, options) {
|
|
138738
138738
|
super("HomeAssistantClient");
|
|
138739
138739
|
this.options = options;
|
|
138740
|
-
this.log =
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
172914
|
-
|
|
172915
|
-
this.state.
|
|
172916
|
-
|
|
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
|
-
|
|
173058
|
-
this.config
|
|
173059
|
-
|
|
173060
|
-
|
|
173061
|
-
|
|
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
|
-
|
|
173069
|
-
|
|
173070
|
-
|
|
173071
|
-
|
|
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
|
-
|
|
173074
|
-
|
|
173075
|
-
|
|
173076
|
-
|
|
173077
|
-
|
|
173078
|
-
|
|
173079
|
-
|
|
173080
|
-
|
|
173081
|
-
|
|
173082
|
-
|
|
173083
|
-
|
|
173084
|
-
|
|
173085
|
-
|
|
173086
|
-
|
|
173087
|
-
|
|
173088
|
-
|
|
173089
|
-
|
|
173090
|
-
|
|
173091
|
-
|
|
173092
|
-
|
|
173093
|
-
|
|
173094
|
-
|
|
173095
|
-
|
|
173096
|
-
|
|
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
|
-
|
|
173104
|
-
|
|
173105
|
-
|
|
173106
|
-
|
|
173107
|
-
|
|
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
|
-
|
|
173111
|
-
|
|
173112
|
-
|
|
173113
|
-
|
|
173114
|
-
|
|
173115
|
-
|
|
173116
|
-
|
|
173117
|
-
|
|
173118
|
-
|
|
173119
|
-
|
|
173120
|
-
|
|
173121
|
-
|
|
173122
|
-
|
|
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
|
-
|
|
173144
|
-
|
|
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
|
-
|
|
173162
|
-
|
|
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
|
-
//
|
|
173177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
173184
|
-
|
|
173185
|
-
|
|
173186
|
-
|
|
173187
|
-
|
|
173188
|
-
|
|
173189
|
-
|
|
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
|
-
|
|
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
|
-
).
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
175399
|
+
constructor(logger250, dataProvider, endpointManager, server) {
|
|
175076
175400
|
this.dataProvider = dataProvider;
|
|
175077
175401
|
this.endpointManager = endpointManager;
|
|
175078
175402
|
this.server = server;
|
|
175079
|
-
this.log =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176465
|
+
logger248.info(
|
|
176140
176466
|
`${entityId}: No battery entity found, using battery attribute from vacuum state`
|
|
176141
176467
|
);
|
|
176142
176468
|
} else {
|
|
176143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176567
|
+
logger248.info(
|
|
176242
176568
|
`${entityId}: Auto-detected ${roborockRooms.length} Roborock rooms`
|
|
176243
176569
|
);
|
|
176244
176570
|
}
|
|
176245
176571
|
}
|
|
176246
176572
|
}
|
|
176247
176573
|
} else {
|
|
176248
|
-
|
|
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
|
-
|
|
176647
|
+
logger248.debug(
|
|
176322
176648
|
`Mapped entity change detected for ${this.entityId}, forcing update`
|
|
176323
176649
|
);
|
|
176324
176650
|
}
|
|
176325
|
-
|
|
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
|
|
177229
|
+
const logger250 = rootEnv.get(LoggerService);
|
|
176904
177230
|
super({
|
|
176905
177231
|
id: "App",
|
|
176906
|
-
log:
|
|
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
|
|
176921
|
-
this.set(LoggerService,
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
177389
|
+
logger249.info(
|
|
177064
177390
|
`Patched ${patched} LevelControl TLV schema(s): transitionTime is now optional (Google Home compatibility)`
|
|
177065
177391
|
);
|
|
177066
177392
|
} else {
|
|
177067
|
-
|
|
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(
|