@riddix/hamh 2.1.0-alpha.866 → 2.1.0-alpha.867

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.
@@ -132397,6 +132397,12 @@ var init_bridge_config_schema = __esm({
132397
132397
  type: "boolean",
132398
132398
  default: false
132399
132399
  },
132400
+ supportTermsAndConditions: {
132401
+ title: "Accept Terms and Conditions commands (Alexa pairing diagnostic)",
132402
+ description: "Advertise the Matter 1.4 TermsAndConditions feature and accept the SetTcAcknowledgements command instead of rejecting it as unsupported. No terms are enforced, the bridge accepts any acknowledgement. Alexa sends this command during pairing and some Echo firmwares may stall when it fails (#449). Restart the bridge and re-pair after changing it. Default off.",
132403
+ type: "boolean",
132404
+ default: false
132405
+ },
132400
132406
  fastSessionRecovery: {
132401
132407
  title: "Fast Session Recovery (Google offline workaround)",
132402
132408
  description: "When a controller drops all subscriptions, clean up the dead session and re-announce after 5 seconds instead of 60. Opt-in for Google Home users whose devices go offline after a cancelled subscription (#386). It shortens the offline window but cannot stop the controller from rejecting the subscription. Default off.",
@@ -155281,165 +155287,6 @@ function matterSubscriptionOptions() {
155281
155287
  };
155282
155288
  }
155283
155289
 
155284
- // src/matter/endpoints/server-mode-server-node.ts
155285
- var logger196 = Logger.get("ServerModeServerNode");
155286
- var ServerModeServerNode = class extends ServerNode {
155287
- deviceEndpoints = /* @__PURE__ */ new Map();
155288
- featureFlags;
155289
- serialNumberSuffix;
155290
- constructor(env, bridgeData) {
155291
- super({
155292
- id: bridgeData.id,
155293
- environment: env,
155294
- network: {
155295
- port: bridgeData.port,
155296
- // Shared zero-jitter window, see matterSubscriptionOptions: 60s max so
155297
- // iOS does not show a stale "Updating" tile (#287), no jitter so the
155298
- // keepalive stays inside a controller's ceiling (#386).
155299
- subscriptionOptions: matterSubscriptionOptions()
155300
- },
155301
- productDescription: {
155302
- name: bridgeData.name,
155303
- deviceType: DeviceTypeId(RoboticVacuumCleanerDevice.deviceType)
155304
- },
155305
- basicInformation: {
155306
- ...legacySpecBasicInformation(bridgeData.featureFlags),
155307
- uniqueId: bridgeData.id,
155308
- nodeLabel: trimToLength(bridgeData.name, 32, "..."),
155309
- vendorId: VendorId(bridgeData.basicInformation.vendorId),
155310
- vendorName: bridgeData.basicInformation.vendorName,
155311
- productId: bridgeData.basicInformation.productId,
155312
- productName: bridgeData.basicInformation.productName,
155313
- productLabel: bridgeData.basicInformation.productLabel,
155314
- serialNumber: `server-${bridgeData.id}`.substring(0, 32),
155315
- hardwareVersion: bridgeData.basicInformation.hardwareVersion,
155316
- softwareVersion: bridgeData.basicInformation.softwareVersion,
155317
- hardwareVersionString: bridgeData.basicInformation.hardwareVersionString,
155318
- softwareVersionString: bridgeData.basicInformation.softwareVersionString ?? String(bridgeData.basicInformation.softwareVersion),
155319
- ...bridgeData.countryCode ? { location: bridgeData.countryCode } : {}
155320
- },
155321
- subscriptions: {
155322
- persistenceEnabled: false
155323
- }
155324
- });
155325
- this.featureFlags = bridgeData.featureFlags;
155326
- this.serialNumberSuffix = bridgeData.serialNumberSuffix;
155327
- }
155328
- /** Number of device endpoints currently attached. */
155329
- get deviceCount() {
155330
- return this.deviceEndpoints.size;
155331
- }
155332
- /**
155333
- * Add a device endpoint to this server node. Several endpoints per node are
155334
- * supported (#301); the call is idempotent per endpoint id.
155335
- */
155336
- async addDevice(endpoint) {
155337
- if (this.deviceEndpoints.has(endpoint.id)) {
155338
- return;
155339
- }
155340
- this.deviceEndpoints.set(endpoint.id, endpoint);
155341
- await this.add(endpoint);
155342
- }
155343
- /**
155344
- * Drop one device reference after the endpoint has been deleted externally.
155345
- * Must be called before re-adding an endpoint with the same id.
155346
- */
155347
- forgetDevice(endpoint) {
155348
- this.deviceEndpoints.delete(endpoint.id);
155349
- }
155350
- /** Drop all device references after the endpoints were deleted externally. */
155351
- clearDevices() {
155352
- this.deviceEndpoints.clear();
155353
- }
155354
- /**
155355
- * Update root-level BasicInformation with entity-specific data.
155356
- * In server mode, controllers (Apple Home, Alexa) read the root node's
155357
- * BasicInformation, not the device endpoint's BridgedDeviceBasicInformation.
155358
- * Without this, server-mode devices show bridge defaults (e.g. "riddix" / "MatterHub").
155359
- */
155360
- async updateDeviceIdentity(entityId, device, mapping, friendlyName) {
155361
- const nodeLabel = trimToLength(mapping?.customName, 32, "...") ?? trimToLength(friendlyName, 32, "...") ?? trimToLength(entityId, 32, "...");
155362
- const productNameFromNodeLabel = this.featureFlags?.productNameFromNodeLabel === true ? trimToLength(sanitizeMatterString(nodeLabel ?? ""), 32, "...") ?? void 0 : void 0;
155363
- const maxRawLen = 32 - (this.serialNumberSuffix?.length ?? 0);
155364
- const registrySerial = this.featureFlags?.useHaRegistrySerial ? trimToLength(device?.serial_number, maxRawLen, "...") : void 0;
155365
- const rawSerial = trimToLength(mapping?.customSerialNumber, maxRawLen, "...") ?? registrySerial;
155366
- const serialNumber = rawSerial && this.serialNumberSuffix ? `${rawSerial}${this.serialNumberSuffix}` : rawSerial;
155367
- const basicInformation = dropUndefined({
155368
- vendorName: trimToLength(mapping?.customVendorName, 32, "...") ?? trimToLength(device?.manufacturer, 32, "..."),
155369
- productName: trimToLength(mapping?.customProductName, 32, "...") ?? productNameFromNodeLabel ?? trimToLength(device?.model_id, 32, "...") ?? trimToLength(device?.model, 32, "..."),
155370
- productLabel: trimToLength(device?.model, 64, "..."),
155371
- nodeLabel,
155372
- serialNumber,
155373
- hardwareVersionString: trimToLength(device?.hw_version, 64, "..."),
155374
- softwareVersionString: trimToLength(device?.sw_version, 64, "...")
155375
- });
155376
- if (Object.keys(basicInformation).length === 0) {
155377
- return;
155378
- }
155379
- try {
155380
- await this.set({ basicInformation });
155381
- } catch (e) {
155382
- const msg = e instanceof Error ? e.message : String(e);
155383
- logger196.warn(
155384
- `Failed to apply server-mode identity for ${entityId}: ${msg}`
155385
- );
155386
- }
155387
- }
155388
- // align the pairing device-type hint with the real device (default is vacuum)
155389
- async updateAdvertisedDeviceType(deviceType) {
155390
- try {
155391
- await this.set({ productDescription: { deviceType } });
155392
- } catch (e) {
155393
- const msg = e instanceof Error ? e.message : String(e);
155394
- logger196.warn(`Failed to set server-mode device type: ${msg}`);
155395
- }
155396
- }
155397
- async factoryReset() {
155398
- await this.cancel();
155399
- await this.erase();
155400
- }
155401
- };
155402
- function dropUndefined(obj) {
155403
- const result = {};
155404
- for (const key in obj) {
155405
- if (obj[key] !== void 0) {
155406
- result[key] = obj[key];
155407
- }
155408
- }
155409
- return result;
155410
- }
155411
-
155412
- // src/plugins/builtin/camera/camera-tcp-requirement.ts
155413
- import { readFileSync as readFileSync6 } from "node:fs";
155414
- var CAMERA_TCP_CONFIG = { incoming: true, outgoing: false };
155415
- function parseCameraList(cameras) {
155416
- if (typeof cameras !== "string") return [];
155417
- return cameras.split(",").map((s) => s.trim()).filter(Boolean);
155418
- }
155419
- function bridgeNeedsTcpForCameras(storageDir, bridgeId) {
155420
- try {
155421
- const raw = readFileSync6(
155422
- pluginStorageFilePath(storageDir, bridgeId, "camera"),
155423
- "utf-8"
155424
- );
155425
- const json = JSON.parse(raw);
155426
- return parseCameraList(json.config?.cameras).length > 0;
155427
- } catch {
155428
- return false;
155429
- }
155430
- }
155431
-
155432
- // src/plugins/plugin-manager.ts
155433
- init_esm();
155434
- import * as fs10 from "node:fs";
155435
- import * as path12 from "node:path";
155436
-
155437
- // src/plugins/plugin-device-factory.ts
155438
- init_esm();
155439
-
155440
- // src/matter/behaviors/identify-server.ts
155441
- init_esm();
155442
-
155443
155290
  // ../../node_modules/.pnpm/@matter+main@0.17.9/node_modules/@matter/main/dist/esm/behaviors.js
155444
155291
  init_nodejs();
155445
155292
 
@@ -155733,7 +155580,7 @@ init_esm4();
155733
155580
  init_esm3();
155734
155581
  init_access_control();
155735
155582
  init_groupcast();
155736
- var logger197 = Logger.get("GroupcastServer");
155583
+ var logger196 = Logger.get("GroupcastServer");
155737
155584
  var UNMAPPED_KEYSET_ID = 65535;
155738
155585
  var GROUPCAST_IS_PROVISIONAL = true;
155739
155586
  var GroupcastServer = class extends GroupcastBehavior {
@@ -156150,7 +155997,7 @@ var GroupcastServer = class extends GroupcastBehavior {
156150
155997
  if (hasMembership) continue;
156151
155998
  const fabricGroups = gkmState.groupTable.filter((g) => g.fabricIndex === fi);
156152
155999
  if (fabricGroups.length === 0) continue;
156153
- logger197.info(`Migrating ${fabricGroups.length} legacy group(s) for fabric ${fi} to Groupcast`);
156000
+ logger196.info(`Migrating ${fabricGroups.length} legacy group(s) for fabric ${fi} to Groupcast`);
156154
156001
  const newEntries = fabricGroups.map((group) => {
156155
156002
  const keyMapping = gkmState.groupKeyMap.find((m) => m.fabricIndex === fi && m.groupId === group.groupId);
156156
156003
  const mcastAddrPolicy = this.features.perGroup ? Groupcast3.MulticastAddrPolicy.PerGroup : Groupcast3.MulticastAddrPolicy.IanaAddr;
@@ -156172,7 +156019,7 @@ var GroupcastServer = class extends GroupcastBehavior {
156172
156019
  }
156173
156020
  if (migrated) {
156174
156021
  this.#updateUsedMcastAddrCount();
156175
- logger197.info("Groupcast migration complete");
156022
+ logger196.info("Groupcast migration complete");
156176
156023
  }
156177
156024
  }
156178
156025
  async [Symbol.asyncDispose]() {
@@ -156706,7 +156553,193 @@ var WebRtcTransportDefinitions3 = ClusterType(WebRtcTransportDefinitions2);
156706
156553
  // ../../node_modules/.pnpm/@matter+types@0.17.9/node_modules/@matter/types/dist/esm/clusters/index.js
156707
156554
  init_wi_fi_network_diagnostics();
156708
156555
 
156556
+ // src/matter/tc-general-commissioning.ts
156557
+ var TcBase = GeneralCommissioningServer.with(
156558
+ GeneralCommissioning3.Feature.TermsAndConditions
156559
+ );
156560
+ var TcGeneralCommissioningServer = class extends TcBase {
156561
+ setTcAcknowledgements({
156562
+ tcVersion
156563
+ }) {
156564
+ this.state.tcAcceptedVersion = tcVersion;
156565
+ return { errorCode: GeneralCommissioning3.CommissioningError.Ok };
156566
+ }
156567
+ };
156568
+ ((TcGeneralCommissioningServer2) => {
156569
+ class State extends TcBase.State {
156570
+ tcAcceptedVersion = 0;
156571
+ tcMinRequiredVersion = 0;
156572
+ // typed number upstream, managed as a bitmap object at runtime
156573
+ tcAcknowledgements = {};
156574
+ // model default is true, but nothing is enforced here
156575
+ tcAcknowledgementsRequired = false;
156576
+ tcUpdateDeadline = null;
156577
+ }
156578
+ TcGeneralCommissioningServer2.State = State;
156579
+ })(TcGeneralCommissioningServer || (TcGeneralCommissioningServer = {}));
156580
+ function rootEndpointType(flags2) {
156581
+ return flags2?.supportTermsAndConditions ? ServerNode.RootEndpoint.with(TcGeneralCommissioningServer) : ServerNode.RootEndpoint;
156582
+ }
156583
+
156584
+ // src/matter/endpoints/server-mode-server-node.ts
156585
+ var logger197 = Logger.get("ServerModeServerNode");
156586
+ var ServerModeServerNode = class extends ServerNode {
156587
+ deviceEndpoints = /* @__PURE__ */ new Map();
156588
+ featureFlags;
156589
+ serialNumberSuffix;
156590
+ constructor(env, bridgeData) {
156591
+ super({
156592
+ type: rootEndpointType(bridgeData.featureFlags),
156593
+ id: bridgeData.id,
156594
+ environment: env,
156595
+ network: {
156596
+ port: bridgeData.port,
156597
+ // Shared zero-jitter window, see matterSubscriptionOptions: 60s max so
156598
+ // iOS does not show a stale "Updating" tile (#287), no jitter so the
156599
+ // keepalive stays inside a controller's ceiling (#386).
156600
+ subscriptionOptions: matterSubscriptionOptions()
156601
+ },
156602
+ productDescription: {
156603
+ name: bridgeData.name,
156604
+ deviceType: DeviceTypeId(RoboticVacuumCleanerDevice.deviceType)
156605
+ },
156606
+ basicInformation: {
156607
+ ...legacySpecBasicInformation(bridgeData.featureFlags),
156608
+ uniqueId: bridgeData.id,
156609
+ nodeLabel: trimToLength(bridgeData.name, 32, "..."),
156610
+ vendorId: VendorId(bridgeData.basicInformation.vendorId),
156611
+ vendorName: bridgeData.basicInformation.vendorName,
156612
+ productId: bridgeData.basicInformation.productId,
156613
+ productName: bridgeData.basicInformation.productName,
156614
+ productLabel: bridgeData.basicInformation.productLabel,
156615
+ serialNumber: `server-${bridgeData.id}`.substring(0, 32),
156616
+ hardwareVersion: bridgeData.basicInformation.hardwareVersion,
156617
+ softwareVersion: bridgeData.basicInformation.softwareVersion,
156618
+ hardwareVersionString: bridgeData.basicInformation.hardwareVersionString,
156619
+ softwareVersionString: bridgeData.basicInformation.softwareVersionString ?? String(bridgeData.basicInformation.softwareVersion),
156620
+ ...bridgeData.countryCode ? { location: bridgeData.countryCode } : {}
156621
+ },
156622
+ subscriptions: {
156623
+ persistenceEnabled: false
156624
+ }
156625
+ });
156626
+ this.featureFlags = bridgeData.featureFlags;
156627
+ this.serialNumberSuffix = bridgeData.serialNumberSuffix;
156628
+ }
156629
+ /** Number of device endpoints currently attached. */
156630
+ get deviceCount() {
156631
+ return this.deviceEndpoints.size;
156632
+ }
156633
+ /**
156634
+ * Add a device endpoint to this server node. Several endpoints per node are
156635
+ * supported (#301); the call is idempotent per endpoint id.
156636
+ */
156637
+ async addDevice(endpoint) {
156638
+ if (this.deviceEndpoints.has(endpoint.id)) {
156639
+ return;
156640
+ }
156641
+ this.deviceEndpoints.set(endpoint.id, endpoint);
156642
+ await this.add(endpoint);
156643
+ }
156644
+ /**
156645
+ * Drop one device reference after the endpoint has been deleted externally.
156646
+ * Must be called before re-adding an endpoint with the same id.
156647
+ */
156648
+ forgetDevice(endpoint) {
156649
+ this.deviceEndpoints.delete(endpoint.id);
156650
+ }
156651
+ /** Drop all device references after the endpoints were deleted externally. */
156652
+ clearDevices() {
156653
+ this.deviceEndpoints.clear();
156654
+ }
156655
+ /**
156656
+ * Update root-level BasicInformation with entity-specific data.
156657
+ * In server mode, controllers (Apple Home, Alexa) read the root node's
156658
+ * BasicInformation, not the device endpoint's BridgedDeviceBasicInformation.
156659
+ * Without this, server-mode devices show bridge defaults (e.g. "riddix" / "MatterHub").
156660
+ */
156661
+ async updateDeviceIdentity(entityId, device, mapping, friendlyName) {
156662
+ const nodeLabel = trimToLength(mapping?.customName, 32, "...") ?? trimToLength(friendlyName, 32, "...") ?? trimToLength(entityId, 32, "...");
156663
+ const productNameFromNodeLabel = this.featureFlags?.productNameFromNodeLabel === true ? trimToLength(sanitizeMatterString(nodeLabel ?? ""), 32, "...") ?? void 0 : void 0;
156664
+ const maxRawLen = 32 - (this.serialNumberSuffix?.length ?? 0);
156665
+ const registrySerial = this.featureFlags?.useHaRegistrySerial ? trimToLength(device?.serial_number, maxRawLen, "...") : void 0;
156666
+ const rawSerial = trimToLength(mapping?.customSerialNumber, maxRawLen, "...") ?? registrySerial;
156667
+ const serialNumber = rawSerial && this.serialNumberSuffix ? `${rawSerial}${this.serialNumberSuffix}` : rawSerial;
156668
+ const basicInformation = dropUndefined({
156669
+ vendorName: trimToLength(mapping?.customVendorName, 32, "...") ?? trimToLength(device?.manufacturer, 32, "..."),
156670
+ productName: trimToLength(mapping?.customProductName, 32, "...") ?? productNameFromNodeLabel ?? trimToLength(device?.model_id, 32, "...") ?? trimToLength(device?.model, 32, "..."),
156671
+ productLabel: trimToLength(device?.model, 64, "..."),
156672
+ nodeLabel,
156673
+ serialNumber,
156674
+ hardwareVersionString: trimToLength(device?.hw_version, 64, "..."),
156675
+ softwareVersionString: trimToLength(device?.sw_version, 64, "...")
156676
+ });
156677
+ if (Object.keys(basicInformation).length === 0) {
156678
+ return;
156679
+ }
156680
+ try {
156681
+ await this.set({ basicInformation });
156682
+ } catch (e) {
156683
+ const msg = e instanceof Error ? e.message : String(e);
156684
+ logger197.warn(
156685
+ `Failed to apply server-mode identity for ${entityId}: ${msg}`
156686
+ );
156687
+ }
156688
+ }
156689
+ // align the pairing device-type hint with the real device (default is vacuum)
156690
+ async updateAdvertisedDeviceType(deviceType) {
156691
+ try {
156692
+ await this.set({ productDescription: { deviceType } });
156693
+ } catch (e) {
156694
+ const msg = e instanceof Error ? e.message : String(e);
156695
+ logger197.warn(`Failed to set server-mode device type: ${msg}`);
156696
+ }
156697
+ }
156698
+ async factoryReset() {
156699
+ await this.cancel();
156700
+ await this.erase();
156701
+ }
156702
+ };
156703
+ function dropUndefined(obj) {
156704
+ const result = {};
156705
+ for (const key in obj) {
156706
+ if (obj[key] !== void 0) {
156707
+ result[key] = obj[key];
156708
+ }
156709
+ }
156710
+ return result;
156711
+ }
156712
+
156713
+ // src/plugins/builtin/camera/camera-tcp-requirement.ts
156714
+ import { readFileSync as readFileSync6 } from "node:fs";
156715
+ var CAMERA_TCP_CONFIG = { incoming: true, outgoing: false };
156716
+ function parseCameraList(cameras) {
156717
+ if (typeof cameras !== "string") return [];
156718
+ return cameras.split(",").map((s) => s.trim()).filter(Boolean);
156719
+ }
156720
+ function bridgeNeedsTcpForCameras(storageDir, bridgeId) {
156721
+ try {
156722
+ const raw = readFileSync6(
156723
+ pluginStorageFilePath(storageDir, bridgeId, "camera"),
156724
+ "utf-8"
156725
+ );
156726
+ const json = JSON.parse(raw);
156727
+ return parseCameraList(json.config?.cameras).length > 0;
156728
+ } catch {
156729
+ return false;
156730
+ }
156731
+ }
156732
+
156733
+ // src/plugins/plugin-manager.ts
156734
+ init_esm();
156735
+ import * as fs10 from "node:fs";
156736
+ import * as path12 from "node:path";
156737
+
156738
+ // src/plugins/plugin-device-factory.ts
156739
+ init_esm();
156740
+
156709
156741
  // src/matter/behaviors/identify-server.ts
156742
+ init_esm();
156710
156743
  init_home_assistant_entity_behavior();
156711
156744
  var logger198 = Logger.get("IdentifyServer");
156712
156745
  var IDENTIFY_BUTTON_SUFFIXES = ["_identify", "_locate", "_find_me"];
@@ -158079,7 +158112,7 @@ Object.freeze(SecondaryNetworkInterfaceEndpointDefinition);
158079
158112
  init_types2();
158080
158113
  function createBridgeServerConfig(data, options) {
158081
158114
  return {
158082
- type: ServerNode.RootEndpoint,
158115
+ type: rootEndpointType(data.featureFlags),
158083
158116
  id: data.id,
158084
158117
  network: {
158085
158118
  port: data.port,