smart-home-engine 1.53.2 → 1.53.4

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.
@@ -1,4 +1,4 @@
1
- import{m as O}from"./monaco-langs-DKJI_o9J.js";import{t as I}from"./index-lCqN_GjH.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-DKJI_o9J.js";import{t as I}from"./index-DPRtP55L.js";/*!-----------------------------------------------------------------------------
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
4
4
  * Released under the MIT license
@@ -175,10 +175,10 @@
175
175
  }
176
176
  })();
177
177
  </script>
178
- <script type="module" crossorigin src="/assets/index-lCqN_GjH.js"></script>
178
+ <script type="module" crossorigin src="/assets/index-DPRtP55L.js"></script>
179
179
  <link rel="modulepreload" crossorigin href="/assets/monaco-langs-DKJI_o9J.js">
180
180
  <link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
181
- <link rel="stylesheet" crossorigin href="/assets/index-BsO7OQRT.css">
181
+ <link rel="stylesheet" crossorigin href="/assets/index-Cp--IWao.css">
182
182
  </head>
183
183
  <body>
184
184
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "1.53.2",
3
+ "version": "1.53.4",
4
4
  "description": "Node.js based script runner for use in MQTT based Smart Home environments",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -81,9 +81,15 @@ function _resolveEndpoint(node, endpointIdOrName) {
81
81
  throw new Error(`Endpoint not found: ${endpointIdOrName}`);
82
82
  }
83
83
 
84
- /** Recursively convert BigInt values to numbers/strings so the result is JSON-serialisable. */
84
+ /**
85
+ * Recursively convert values so the result is JSON-serialisable: BigInt to a number (or a string
86
+ * beyond 2^53), byte arrays to `{ $bytes: "<hex>" }` — JSON.stringify would turn a Uint8Array into
87
+ * an object of numbered keys, which the UI cannot tell from a struct.
88
+ */
85
89
  function _jsonSafe(v) {
86
90
  if (typeof v === 'bigint') return v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v.toString();
91
+ if (ArrayBuffer.isView(v)) return { $bytes: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString('hex') };
92
+ if (v instanceof ArrayBuffer) return { $bytes: Buffer.from(v).toString('hex') };
87
93
  if (Array.isArray(v)) return v.map(_jsonSafe);
88
94
  if (v !== null && typeof v === 'object') {
89
95
  const out = {};
@@ -361,7 +367,18 @@ async function commission(options) {
361
367
  }
362
368
 
363
369
  async function _commissionInner(options) {
364
- const { discoveryAddress, ...commissionOpts } = options;
370
+ const { discoveryAddress, ...rest } = options;
371
+ // matter.js (0.17.0 … 0.17.9) passes an absent regulatory location and country through
372
+ // CommissioningClient as `undefined`, which overrides the commissioner's own default "XX":
373
+ // every Thread or Wi-Fi device then fails at SetRegulatoryConfig ("Missing mandatory field
374
+ // countryCode"). "XX" (unknown country) is always valid; IndoorOutdoor is used only when the
375
+ // device reports that capability, otherwise the device keeps its own location.
376
+ const { GeneralCommissioning } = require('@matter/main/clusters');
377
+ const commissionOpts = {
378
+ regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
379
+ regulatoryCountryCode: 'XX',
380
+ ...rest,
381
+ };
365
382
  let clientNode;
366
383
  if (discoveryAddress) {
367
384
  const colonIdx = discoveryAddress.lastIndexOf(':');
@@ -437,7 +454,7 @@ function getEndpoints(nodeId) {
437
454
  const attrs = {};
438
455
  if (clusterState && typeof clusterState === 'object') {
439
456
  for (const [k, v] of Object.entries(clusterState)) {
440
- attrs[k] = _jsonSafe(v);
457
+ attrs[k] = _attrValue(clusterName, k, v);
441
458
  }
442
459
  }
443
460
  clusters.push({ name: clusterName, attrs });
@@ -627,6 +644,88 @@ async function sendCommand(nodeId, endpointId, clusterName, commandName, args) {
627
644
  * @param {object} node ClientNode
628
645
  * @param {string} nodeId decimal string
629
646
  */
647
+ /** Matter's attribute OIDs (1.3.6.1.4.1.37244.1.x, Matter core spec 6.5.6.1) by name. */
648
+ const MATTER_DN_OIDS = {
649
+ '1.3.6.1.4.1.37244.1.1': 'matter-node-id',
650
+ '1.3.6.1.4.1.37244.1.2': 'matter-firmware-signing-id',
651
+ '1.3.6.1.4.1.37244.1.3': 'matter-icac-id',
652
+ '1.3.6.1.4.1.37244.1.4': 'matter-rcac-id',
653
+ '1.3.6.1.4.1.37244.1.5': 'matter-fabric-id',
654
+ '1.3.6.1.4.1.37244.1.6': 'matter-noc-cat',
655
+ };
656
+
657
+ /** node's X509Certificate distinguished name ("oid=value\n…") with Matter's OIDs named */
658
+ function _matterDn(dn) {
659
+ return String(dn || '')
660
+ .split('\n')
661
+ .filter(Boolean)
662
+ .map((part) => {
663
+ const eq = part.indexOf('=');
664
+ const key = part.slice(0, eq);
665
+ return `${MATTER_DN_OIDS[key] || key}=${part.slice(eq + 1)}`;
666
+ })
667
+ .join(', ');
668
+ }
669
+
670
+ /**
671
+ * A Matter operational certificate (TLV, as the OperationalCredentials cluster holds it) as
672
+ * `{ $cert: { kind, subject, validFrom, validTo, fingerprint, pem } }`: converted to X.509 DER by
673
+ * matter.js, read back by node's X509Certificate. Anything that does not convert stays `$bytes`.
674
+ */
675
+ function _certValue(kind, bytes) {
676
+ if (!ArrayBuffer.isView(bytes)) return _jsonSafe(bytes);
677
+ try {
678
+ const { Noc, Icac, Rcac } = require('@matter/protocol');
679
+ const cert = { NOC: Noc, ICAC: Icac, RCAC: Rcac }[kind].fromTlv(bytes);
680
+ const der = Buffer.from(cert.asSignedDer());
681
+ const x509 = new (require('crypto').X509Certificate)(der);
682
+ const lines = der
683
+ .toString('base64')
684
+ .match(/.{1,64}/g)
685
+ .join('\n');
686
+ return {
687
+ $cert: {
688
+ kind,
689
+ // Matter's own fields (nodeId, fabricId, rcacId, icacId, caseAuthenticatedTags …)
690
+ subject: _jsonSafe(cert.cert.subject),
691
+ issuer: _jsonSafe(cert.cert.issuer),
692
+ // the same as X.509 distinguished names, with Matter's OIDs named
693
+ subjectDn: _matterDn(x509.subject),
694
+ issuerDn: _matterDn(x509.issuer),
695
+ serialNumber: x509.serialNumber,
696
+ validFrom: new Date(x509.validFrom).toISOString(),
697
+ validTo: new Date(x509.validTo).toISOString(),
698
+ ca: x509.ca,
699
+ // key usage, extended key usage, basic constraints, key identifiers — all the
700
+ // certificate carries; Matter operational certificates have no CRL distribution point
701
+ extensions: _jsonSafe(cert.cert.extensions),
702
+ fingerprint: x509.fingerprint256,
703
+ pem: `-----BEGIN CERTIFICATE-----\n${lines}\n-----END CERTIFICATE-----`,
704
+ },
705
+ };
706
+ } catch {
707
+ return _jsonSafe(bytes);
708
+ }
709
+ }
710
+
711
+ /** An attribute value for the UI and the REST API: certificates as PEM, the rest via _jsonSafe. */
712
+ function _attrValue(clusterName, attrName, value) {
713
+ if (clusterName === 'operationalCredentials') {
714
+ if (attrName === 'nocs' && Array.isArray(value)) {
715
+ return value.map((entry) => {
716
+ const out = _jsonSafe(entry);
717
+ if (entry?.noc) out.noc = _certValue('NOC', entry.noc);
718
+ if (entry?.icac) out.icac = _certValue('ICAC', entry.icac);
719
+ return out;
720
+ });
721
+ }
722
+ if (attrName === 'trustedRootCertificates' && Array.isArray(value)) {
723
+ return value.map((root) => _certValue('RCAC', root));
724
+ }
725
+ }
726
+ return _jsonSafe(value);
727
+ }
728
+
630
729
  function _broadcastNodeAttributes(node, nodeId) {
631
730
  if (!_broadcast) return;
632
731
  if (_attrBroadcastNodes.has(nodeId)) return;
@@ -653,7 +752,7 @@ function _broadcastNodeAttributes(node, nodeId) {
653
752
  endpointId,
654
753
  clusterName,
655
754
  attrName,
656
- value: _jsonSafe(value),
755
+ value: _attrValue(clusterName, attrName, value),
657
756
  ts: Date.now(),
658
757
  });
659
758
  // Device/endpoint labels feed the device list — rebroadcast it
@@ -795,6 +894,8 @@ function isStarted() {
795
894
  }
796
895
 
797
896
  module.exports = {
897
+ // for the unit tests: the value conversion, without a running controller
898
+ _attrValue,
798
899
  init,
799
900
  close,
800
901
  isStarted,