@riddix/hamh 2.1.0-alpha.754 → 2.1.0-alpha.756
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
|
@@ -124071,6 +124071,14 @@ function computeControllerWarnings(controllers, exposed) {
|
|
|
124071
124071
|
}
|
|
124072
124072
|
return warnings;
|
|
124073
124073
|
}
|
|
124074
|
+
function controllerWarningsForFabrics(fabrics, exposed) {
|
|
124075
|
+
const controllers = [
|
|
124076
|
+
...new Set(fabrics.map((f) => classifyController(f.rootVendorId)).filter((c) => c !== void 0))
|
|
124077
|
+
];
|
|
124078
|
+
if (controllers.length === 0)
|
|
124079
|
+
return [];
|
|
124080
|
+
return computeControllerWarnings(controllers, exposed);
|
|
124081
|
+
}
|
|
124074
124082
|
var controllerByVendorId, deviceTypeIdSupport, controllerLabels;
|
|
124075
124083
|
var init_controller_compat = __esm({
|
|
124076
124084
|
"../common/dist/controller-compat.js"() {
|
|
@@ -127121,12 +127129,7 @@ function healthApi(bridgeService, haClient, version2, startTime) {
|
|
|
127121
127129
|
const fabrics = data.commissioning?.fabrics ?? [];
|
|
127122
127130
|
const sessionInfo = b.getSessionInfo();
|
|
127123
127131
|
const exposed = b.getExposedDeviceTypes();
|
|
127124
|
-
const
|
|
127125
|
-
...new Set(
|
|
127126
|
-
fabrics.map((f) => classifyController(f.rootVendorId)).filter((c) => c !== void 0)
|
|
127127
|
-
)
|
|
127128
|
-
];
|
|
127129
|
-
const controllerWarnings = controllers.length > 0 ? computeControllerWarnings(controllers, exposed) : [];
|
|
127132
|
+
const controllerWarnings = data.controllerWarnings ?? [];
|
|
127130
127133
|
const entityDiagnostics = buildEntityDiagnostics(
|
|
127131
127134
|
exposed,
|
|
127132
127135
|
data.failedEntities ?? [],
|
|
@@ -128353,15 +128356,13 @@ function metricsApi(bridgeService, haClient, haRegistry, startTime) {
|
|
|
128353
128356
|
router.get("/", (_, res) => {
|
|
128354
128357
|
const memoryUsage = process.memoryUsage();
|
|
128355
128358
|
const bridges = bridgeService.bridges;
|
|
128356
|
-
const
|
|
128357
|
-
const
|
|
128358
|
-
const
|
|
128359
|
-
const
|
|
128360
|
-
|
|
128361
|
-
|
|
128362
|
-
|
|
128363
|
-
const totalFabrics = bridges.reduce(
|
|
128364
|
-
(sum, b) => sum + (b.data.commissioning?.fabrics?.length ?? 0),
|
|
128359
|
+
const datas = bridges.map((b) => b.data);
|
|
128360
|
+
const running = datas.filter((d) => d.status === "running").length;
|
|
128361
|
+
const stopped = datas.filter((d) => d.status === "stopped").length;
|
|
128362
|
+
const failed = datas.filter((d) => d.status === "failed").length;
|
|
128363
|
+
const totalDevices = datas.reduce((sum, d) => sum + d.deviceCount, 0);
|
|
128364
|
+
const totalFabrics = datas.reduce(
|
|
128365
|
+
(sum, d) => sum + (d.commissioning?.fabrics?.length ?? 0),
|
|
128365
128366
|
0
|
|
128366
128367
|
);
|
|
128367
128368
|
const metrics = {
|
|
@@ -128393,15 +128394,13 @@ function metricsApi(bridgeService, haClient, haRegistry, startTime) {
|
|
|
128393
128394
|
const memoryUsage = process.memoryUsage();
|
|
128394
128395
|
const bridges = bridgeService.bridges;
|
|
128395
128396
|
const uptime2 = Math.floor((Date.now() - startTime) / 1e3);
|
|
128396
|
-
const
|
|
128397
|
-
const
|
|
128398
|
-
const
|
|
128399
|
-
const
|
|
128400
|
-
|
|
128401
|
-
|
|
128402
|
-
|
|
128403
|
-
const totalFabrics = bridges.reduce(
|
|
128404
|
-
(sum, b) => sum + (b.data.commissioning?.fabrics?.length ?? 0),
|
|
128397
|
+
const datas = bridges.map((b) => b.data);
|
|
128398
|
+
const running = datas.filter((d) => d.status === "running").length;
|
|
128399
|
+
const stopped = datas.filter((d) => d.status === "stopped").length;
|
|
128400
|
+
const failed = datas.filter((d) => d.status === "failed").length;
|
|
128401
|
+
const totalDevices = datas.reduce((sum, d) => sum + d.deviceCount, 0);
|
|
128402
|
+
const totalFabrics = datas.reduce(
|
|
128403
|
+
(sum, d) => sum + (d.commissioning?.fabrics?.length ?? 0),
|
|
128405
128404
|
0
|
|
128406
128405
|
);
|
|
128407
128406
|
const lines = [
|
|
@@ -128459,8 +128458,9 @@ function metricsApi(bridgeService, haClient, haRegistry, startTime) {
|
|
|
128459
128458
|
""
|
|
128460
128459
|
];
|
|
128461
128460
|
for (const bridge of bridges) {
|
|
128462
|
-
const
|
|
128463
|
-
const
|
|
128461
|
+
const data = bridge.data;
|
|
128462
|
+
const status3 = data.status === "running" ? 1 : 0;
|
|
128463
|
+
const safeName = data.name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
128464
128464
|
lines.push(
|
|
128465
128465
|
`# HELP hamh_bridge_status Bridge status (1=running, 0=not running)`,
|
|
128466
128466
|
`# TYPE hamh_bridge_status gauge`,
|
|
@@ -128468,7 +128468,7 @@ function metricsApi(bridgeService, haClient, haRegistry, startTime) {
|
|
|
128468
128468
|
"",
|
|
128469
128469
|
`# HELP hamh_bridge_devices Number of devices on bridge`,
|
|
128470
128470
|
`# TYPE hamh_bridge_devices gauge`,
|
|
128471
|
-
`hamh_bridge_devices{bridge_id="${bridge.id}",bridge_name="${safeName}"} ${
|
|
128471
|
+
`hamh_bridge_devices{bridge_id="${bridge.id}",bridge_name="${safeName}"} ${data.deviceCount}`,
|
|
128472
128472
|
""
|
|
128473
128473
|
);
|
|
128474
128474
|
}
|
|
@@ -147233,6 +147233,7 @@ init_esm7();
|
|
|
147233
147233
|
import crypto4 from "node:crypto";
|
|
147234
147234
|
|
|
147235
147235
|
// src/services/bridges/bridge-data-provider.ts
|
|
147236
|
+
init_dist();
|
|
147236
147237
|
init_service();
|
|
147237
147238
|
import { values as values2 } from "lodash-es";
|
|
147238
147239
|
var BridgeDataProvider = class extends Service {
|
|
@@ -147289,8 +147290,13 @@ var BridgeDataProvider = class extends Service {
|
|
|
147289
147290
|
/**
|
|
147290
147291
|
* @deprecated
|
|
147291
147292
|
*/
|
|
147292
|
-
withMetadata(status3, serverNode, deviceCount, failedEntities = []) {
|
|
147293
|
+
withMetadata(status3, serverNode, deviceCount, failedEntities = [], exposedDeviceTypes = []) {
|
|
147293
147294
|
const commissioning = serverNode.state.commissioning;
|
|
147295
|
+
const fabrics = commissioning ? values2(commissioning.fabrics) : [];
|
|
147296
|
+
const controllerWarnings = controllerWarningsForFabrics(
|
|
147297
|
+
fabrics,
|
|
147298
|
+
exposedDeviceTypes
|
|
147299
|
+
);
|
|
147294
147300
|
return {
|
|
147295
147301
|
id: this.id,
|
|
147296
147302
|
name: this.name,
|
|
@@ -147311,7 +147317,7 @@ var BridgeDataProvider = class extends Service {
|
|
|
147311
147317
|
discriminator: commissioning.discriminator,
|
|
147312
147318
|
manualPairingCode: commissioning.pairingCodes.manualPairingCode,
|
|
147313
147319
|
qrPairingCode: commissioning.pairingCodes.qrPairingCode,
|
|
147314
|
-
fabrics:
|
|
147320
|
+
fabrics: fabrics.map((fabric) => ({
|
|
147315
147321
|
fabricIndex: fabric.fabricIndex,
|
|
147316
147322
|
fabricId: Number(fabric.fabricId),
|
|
147317
147323
|
nodeId: Number(fabric.nodeId),
|
|
@@ -147321,7 +147327,8 @@ var BridgeDataProvider = class extends Service {
|
|
|
147321
147327
|
}))
|
|
147322
147328
|
} : void 0,
|
|
147323
147329
|
deviceCount,
|
|
147324
|
-
failedEntities: failedEntities.length > 0 ? failedEntities : void 0
|
|
147330
|
+
failedEntities: failedEntities.length > 0 ? failedEntities : void 0,
|
|
147331
|
+
controllerWarnings: controllerWarnings.length > 0 ? controllerWarnings : void 0
|
|
147325
147332
|
};
|
|
147326
147333
|
}
|
|
147327
147334
|
};
|
|
@@ -148436,6 +148443,7 @@ function seedExistingSessionStarts(startedAt, sessions, now = Date.now()) {
|
|
|
148436
148443
|
// src/services/bridges/bridge.ts
|
|
148437
148444
|
var AUTO_FORCE_SYNC_INTERVAL_MS = 9e4;
|
|
148438
148445
|
var DEAD_SESSION_TIMEOUT_MS = 6e4;
|
|
148446
|
+
var SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS = 2500;
|
|
148439
148447
|
var Bridge = class {
|
|
148440
148448
|
constructor(env, logger234, dataProvider, endpointManager) {
|
|
148441
148449
|
this.dataProvider = dataProvider;
|
|
@@ -148499,7 +148507,8 @@ var Bridge = class {
|
|
|
148499
148507
|
this.status,
|
|
148500
148508
|
this.server,
|
|
148501
148509
|
this.aggregator.parts.size,
|
|
148502
|
-
this.endpointManager.failedEntities
|
|
148510
|
+
this.endpointManager.failedEntities,
|
|
148511
|
+
this.getExposedDeviceTypes()
|
|
148503
148512
|
);
|
|
148504
148513
|
}
|
|
148505
148514
|
getSessionInfo() {
|
|
@@ -148685,6 +148694,7 @@ ${e?.toString()}`);
|
|
|
148685
148694
|
async runStop(code, reason) {
|
|
148686
148695
|
this.unwireSessionDiagnostics();
|
|
148687
148696
|
this.stopAutoForceSync();
|
|
148697
|
+
await this.closeActiveSessions();
|
|
148688
148698
|
await this.endpointManager.stopPlugins();
|
|
148689
148699
|
this.endpointManager.stopObserving();
|
|
148690
148700
|
try {
|
|
@@ -148715,6 +148725,7 @@ ${e?.toString()}`);
|
|
|
148715
148725
|
this.log.info(`Force sync: every ${AUTO_FORCE_SYNC_INTERVAL_MS / 1e3}s`);
|
|
148716
148726
|
}
|
|
148717
148727
|
wireSessionDiagnostics() {
|
|
148728
|
+
this.unwireSessionDiagnostics();
|
|
148718
148729
|
try {
|
|
148719
148730
|
const sessionManager = this.server.env.get(SessionManager);
|
|
148720
148731
|
seedExistingSessionStarts(this.sessionStartedAt, sessionManager.sessions);
|
|
@@ -148870,6 +148881,42 @@ ${e?.toString()}`);
|
|
|
148870
148881
|
} catch {
|
|
148871
148882
|
}
|
|
148872
148883
|
}
|
|
148884
|
+
// Close every active session on shutdown so each controller is told to drop
|
|
148885
|
+
// its CASE session instead of being left with a stale one. Mirrors the
|
|
148886
|
+
// dead-session close, but covers all sessions and waits (capped) so the
|
|
148887
|
+
// close actually reaches the peer before the server is canceled.
|
|
148888
|
+
async closeActiveSessions() {
|
|
148889
|
+
try {
|
|
148890
|
+
const sessionManager = this.server.env.get(SessionManager);
|
|
148891
|
+
const closes = [];
|
|
148892
|
+
for (const s of [...sessionManager.sessions]) {
|
|
148893
|
+
if (s.isClosing) {
|
|
148894
|
+
continue;
|
|
148895
|
+
}
|
|
148896
|
+
closes.push(
|
|
148897
|
+
s.initiateClose().catch(() => {
|
|
148898
|
+
return s.initiateForceClose({
|
|
148899
|
+
cause: new Error("graceful close failed, forcing")
|
|
148900
|
+
});
|
|
148901
|
+
})
|
|
148902
|
+
);
|
|
148903
|
+
}
|
|
148904
|
+
if (closes.length === 0) {
|
|
148905
|
+
return;
|
|
148906
|
+
}
|
|
148907
|
+
this.log.info(`Closing ${closes.length} active session(s) on shutdown`);
|
|
148908
|
+
let timer;
|
|
148909
|
+
const timeout = new Promise((resolve11) => {
|
|
148910
|
+
timer = setTimeout(resolve11, SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS);
|
|
148911
|
+
});
|
|
148912
|
+
try {
|
|
148913
|
+
await Promise.race([Promise.allSettled(closes), timeout]);
|
|
148914
|
+
} finally {
|
|
148915
|
+
clearTimeout(timer);
|
|
148916
|
+
}
|
|
148917
|
+
} catch {
|
|
148918
|
+
}
|
|
148919
|
+
}
|
|
148873
148920
|
/**
|
|
148874
148921
|
* Force a fresh mDNS operational advertisement after session cleanup.
|
|
148875
148922
|
* matter.js DeviceAdvertiser only re-announces when a subscription is
|
|
@@ -164341,6 +164388,7 @@ init_dist();
|
|
|
164341
164388
|
init_diagnostic_event_bus();
|
|
164342
164389
|
var AUTO_FORCE_SYNC_INTERVAL_MS2 = 9e4;
|
|
164343
164390
|
var DEAD_SESSION_TIMEOUT_MS2 = 6e4;
|
|
164391
|
+
var SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS2 = 2500;
|
|
164344
164392
|
function makeWarmStartState(state, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
164345
164393
|
return { ...state, last_updated: now };
|
|
164346
164394
|
}
|
|
@@ -164387,7 +164435,8 @@ var ServerModeBridge = class {
|
|
|
164387
164435
|
this.status,
|
|
164388
164436
|
this.server,
|
|
164389
164437
|
this.endpointManager.devices.length,
|
|
164390
|
-
this.endpointManager.failedEntities
|
|
164438
|
+
this.endpointManager.failedEntities,
|
|
164439
|
+
this.getExposedDeviceTypes()
|
|
164391
164440
|
);
|
|
164392
164441
|
}
|
|
164393
164442
|
/**
|
|
@@ -164504,6 +164553,7 @@ ${e?.toString()}`);
|
|
|
164504
164553
|
this.unwireSessionDiagnostics();
|
|
164505
164554
|
this.cancelWarmStart();
|
|
164506
164555
|
this.stopAutoForceSync();
|
|
164556
|
+
await this.closeActiveSessions();
|
|
164507
164557
|
this.endpointManager.stopObserving();
|
|
164508
164558
|
try {
|
|
164509
164559
|
await this.server.cancel();
|
|
@@ -164573,6 +164623,7 @@ ${e?.toString()}`);
|
|
|
164573
164623
|
this.log.info(`Force sync: every ${AUTO_FORCE_SYNC_INTERVAL_MS2 / 1e3}s`);
|
|
164574
164624
|
}
|
|
164575
164625
|
wireSessionDiagnostics() {
|
|
164626
|
+
this.unwireSessionDiagnostics();
|
|
164576
164627
|
try {
|
|
164577
164628
|
const sessionManager = this.server.env.get(SessionManager);
|
|
164578
164629
|
this.sessionDiagHandler = (session) => {
|
|
@@ -164693,6 +164744,42 @@ ${e?.toString()}`);
|
|
|
164693
164744
|
} catch {
|
|
164694
164745
|
}
|
|
164695
164746
|
}
|
|
164747
|
+
// Close every active session on shutdown so each controller is told to drop
|
|
164748
|
+
// its CASE session instead of being left with a stale one. Mirrors the
|
|
164749
|
+
// dead-session close, but covers all sessions and waits (capped) so the
|
|
164750
|
+
// close actually reaches the peer before the server is canceled.
|
|
164751
|
+
async closeActiveSessions() {
|
|
164752
|
+
try {
|
|
164753
|
+
const sessionManager = this.server.env.get(SessionManager);
|
|
164754
|
+
const closes = [];
|
|
164755
|
+
for (const s of [...sessionManager.sessions]) {
|
|
164756
|
+
if (s.isClosing) {
|
|
164757
|
+
continue;
|
|
164758
|
+
}
|
|
164759
|
+
closes.push(
|
|
164760
|
+
s.initiateClose().catch(() => {
|
|
164761
|
+
return s.initiateForceClose({
|
|
164762
|
+
cause: new Error("graceful close failed, forcing")
|
|
164763
|
+
});
|
|
164764
|
+
})
|
|
164765
|
+
);
|
|
164766
|
+
}
|
|
164767
|
+
if (closes.length === 0) {
|
|
164768
|
+
return;
|
|
164769
|
+
}
|
|
164770
|
+
this.log.info(`Closing ${closes.length} active session(s) on shutdown`);
|
|
164771
|
+
let timer;
|
|
164772
|
+
const timeout = new Promise((resolve11) => {
|
|
164773
|
+
timer = setTimeout(resolve11, SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS2);
|
|
164774
|
+
});
|
|
164775
|
+
try {
|
|
164776
|
+
await Promise.race([Promise.allSettled(closes), timeout]);
|
|
164777
|
+
} finally {
|
|
164778
|
+
clearTimeout(timer);
|
|
164779
|
+
}
|
|
164780
|
+
} catch {
|
|
164781
|
+
}
|
|
164782
|
+
}
|
|
164696
164783
|
/**
|
|
164697
164784
|
* Force a fresh mDNS operational advertisement after session cleanup.
|
|
164698
164785
|
* matter.js DeviceAdvertiser only re-announces when a subscription is
|
|
@@ -165918,6 +166005,14 @@ async function startHandler(startOptions, webUiDist) {
|
|
|
165918
166005
|
if (shuttingDown) return;
|
|
165919
166006
|
shuttingDown = true;
|
|
165920
166007
|
console.log(`Received ${signal}, shutting down gracefully...`);
|
|
166008
|
+
try {
|
|
166009
|
+
await Promise.race([
|
|
166010
|
+
bridgeService.stopAll(),
|
|
166011
|
+
new Promise((resolve11) => setTimeout(resolve11, 1e4))
|
|
166012
|
+
]);
|
|
166013
|
+
} catch (e) {
|
|
166014
|
+
console.warn("Stopping bridges during shutdown failed:", e);
|
|
166015
|
+
}
|
|
165921
166016
|
try {
|
|
165922
166017
|
await Promise.race([
|
|
165923
166018
|
backupService.createAutoBackup(),
|