smart-home-engine 1.53.3 → 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-D2gZU7ce.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-D2gZU7ce.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.3",
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 = {};
@@ -448,7 +454,7 @@ function getEndpoints(nodeId) {
448
454
  const attrs = {};
449
455
  if (clusterState && typeof clusterState === 'object') {
450
456
  for (const [k, v] of Object.entries(clusterState)) {
451
- attrs[k] = _jsonSafe(v);
457
+ attrs[k] = _attrValue(clusterName, k, v);
452
458
  }
453
459
  }
454
460
  clusters.push({ name: clusterName, attrs });
@@ -638,6 +644,88 @@ async function sendCommand(nodeId, endpointId, clusterName, commandName, args) {
638
644
  * @param {object} node ClientNode
639
645
  * @param {string} nodeId decimal string
640
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
+
641
729
  function _broadcastNodeAttributes(node, nodeId) {
642
730
  if (!_broadcast) return;
643
731
  if (_attrBroadcastNodes.has(nodeId)) return;
@@ -664,7 +752,7 @@ function _broadcastNodeAttributes(node, nodeId) {
664
752
  endpointId,
665
753
  clusterName,
666
754
  attrName,
667
- value: _jsonSafe(value),
755
+ value: _attrValue(clusterName, attrName, value),
668
756
  ts: Date.now(),
669
757
  });
670
758
  // Device/endpoint labels feed the device list — rebroadcast it
@@ -806,6 +894,8 @@ function isStarted() {
806
894
  }
807
895
 
808
896
  module.exports = {
897
+ // for the unit tests: the value conversion, without a running controller
898
+ _attrValue,
809
899
  init,
810
900
  close,
811
901
  isStarted,