smart-home-engine 1.20.3 → 1.20.5
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-Dt8a9eeo.js";import{t as I}from"./index-
|
|
1
|
+
import{m as O}from"./monaco-langs-Dt8a9eeo.js";import{t as I}from"./index-DvxIhiIs.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
package/dist/web/index.html
CHANGED
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
}
|
|
173
173
|
})();
|
|
174
174
|
</script>
|
|
175
|
-
<script type="module" crossorigin src="/assets/index-
|
|
175
|
+
<script type="module" crossorigin src="/assets/index-DvxIhiIs.js"></script>
|
|
176
176
|
<link rel="modulepreload" crossorigin href="/assets/monaco-langs-Dt8a9eeo.js">
|
|
177
177
|
<link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
|
|
178
178
|
<link rel="stylesheet" crossorigin href="/assets/index-B4M4MeEk.css">
|
package/package.json
CHANGED
package/src/matter/controller.js
CHANGED
|
@@ -403,6 +403,13 @@ async function getAttribute(nodeId, endpointId, clusterName, attrName) {
|
|
|
403
403
|
* Write a single attribute value (a remote write for client nodes — the
|
|
404
404
|
* attribute must be writable per the device's Matter data model).
|
|
405
405
|
*
|
|
406
|
+
* Uses the interaction-level write rather than Endpoint.set(): set() requires
|
|
407
|
+
* a synthesized client behavior, which matter.js does not create for every
|
|
408
|
+
* cluster (e.g. basicInformation on the root endpoint reports
|
|
409
|
+
* "Behavior ... is not present on this endpoint" although the cluster and its
|
|
410
|
+
* state are there). The interaction write goes over the same wire path that
|
|
411
|
+
* commands use and needs no behavior instance.
|
|
412
|
+
*
|
|
406
413
|
* @param {string} nodeId
|
|
407
414
|
* @param {number} endpointId
|
|
408
415
|
* @param {string} clusterName camelCase cluster name, e.g. "basicInformation"
|
|
@@ -412,8 +419,14 @@ async function getAttribute(nodeId, endpointId, clusterName, attrName) {
|
|
|
412
419
|
*/
|
|
413
420
|
async function setAttribute(nodeId, endpointId, clusterName, attrName, value) {
|
|
414
421
|
const node = _findClientNode(nodeId);
|
|
415
|
-
const
|
|
416
|
-
|
|
422
|
+
const { Write, WriteResult } = require('@matter/protocol');
|
|
423
|
+
const clusters = require('@matter/main/clusters');
|
|
424
|
+
const clusterDef = clusters[clusterName.charAt(0).toUpperCase() + clusterName.slice(1)]?.Complete;
|
|
425
|
+
if (!clusterDef) throw new Error(`Unknown cluster "${clusterName}"`);
|
|
426
|
+
if (!clusterDef.attributes?.[attrName]) throw new Error(`Unknown attribute "${clusterName}.${attrName}"`);
|
|
427
|
+
const request = Write(Write.Attribute({ endpoint: Number(endpointId), cluster: clusterDef, attributes: attrName, value }));
|
|
428
|
+
const result = await node.interaction.write(request);
|
|
429
|
+
WriteResult.assertSuccess(result);
|
|
417
430
|
}
|
|
418
431
|
|
|
419
432
|
/**
|
|
@@ -434,18 +447,43 @@ async function rename(nodeId, name) {
|
|
|
434
447
|
|
|
435
448
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
436
449
|
|
|
450
|
+
/** Lowercase the first character (PascalCase model names → camelCase). */
|
|
451
|
+
function _camelize(s) {
|
|
452
|
+
return s.charAt(0).toLowerCase() + s.slice(1);
|
|
453
|
+
}
|
|
454
|
+
|
|
437
455
|
/**
|
|
438
456
|
* Best-effort: list the mandatory field names (camelCase) of a command's
|
|
439
|
-
* request schema.
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
457
|
+
* request schema. Resolution mirrors matter.js's own command lookup:
|
|
458
|
+
* 1. the behavior's cluster definition (unwrapping a `.cluster`-wrapped
|
|
459
|
+
* specifier like @matter/protocol Specifier.clusterFor does), commands as
|
|
460
|
+
* a TLV Command (requestSchema/fieldDefinitions) or a model-based command
|
|
461
|
+
* ({ schema: CommandModel });
|
|
462
|
+
* 2. fallback: the behavior's ClusterModel schema — request-direction
|
|
463
|
+
* CommandModel children matched by camelized name (the shape generated
|
|
464
|
+
* for discovered client clusters).
|
|
465
|
+
* Returns [] when unresolvable.
|
|
466
|
+
* @param {object} behavior the cluster behavior (agent[clusterName])
|
|
443
467
|
* @param {string} commandName
|
|
444
468
|
* @returns {string[]}
|
|
445
469
|
*/
|
|
446
|
-
function _mandatoryCommandFields(
|
|
470
|
+
function _mandatoryCommandFields(behavior, commandName) {
|
|
447
471
|
try {
|
|
448
|
-
|
|
472
|
+
let cluster = behavior?.cluster;
|
|
473
|
+
if (cluster && typeof cluster === 'object' && 'cluster' in cluster) cluster = cluster.cluster;
|
|
474
|
+
let cmd = cluster?.commands?.[commandName];
|
|
475
|
+
if (!cmd) {
|
|
476
|
+
// Discovered client clusters: command models live on the ClusterModel schema
|
|
477
|
+
const schemaModel = behavior?.schema ?? behavior?.constructor?.schema;
|
|
478
|
+
if (schemaModel?.children) {
|
|
479
|
+
for (const c of schemaModel.children) {
|
|
480
|
+
if (c.tag === 'command' && c.name && _camelize(c.name) === commandName && c.direction !== 'response') {
|
|
481
|
+
cmd = { schema: c };
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
449
487
|
const schema = cmd?.schema ?? cmd?.requestSchema;
|
|
450
488
|
if (schema?.fieldDefinitions) {
|
|
451
489
|
return Object.entries(schema.fieldDefinitions)
|
|
@@ -453,7 +491,7 @@ function _mandatoryCommandFields(cluster, commandName) {
|
|
|
453
491
|
.map(([name]) => name);
|
|
454
492
|
}
|
|
455
493
|
if (schema?.children) {
|
|
456
|
-
return [...schema.children].filter((c) => c.effectiveConformance?.isMandatory !== false).map((c) =>
|
|
494
|
+
return [...schema.children].filter((c) => c.effectiveConformance?.isMandatory !== false).map((c) => _camelize(c.name));
|
|
457
495
|
}
|
|
458
496
|
} catch {
|
|
459
497
|
/* best-effort — fall through */
|
|
@@ -489,7 +527,17 @@ async function sendCommand(nodeId, endpointId, clusterName, commandName, args) {
|
|
|
489
527
|
if (typeof cmd !== 'function') throw new Error(`Command "${commandName}" not found in cluster "${clusterName}"`);
|
|
490
528
|
// Fill neutral defaults for mandatory boilerplate fields the caller omitted.
|
|
491
529
|
const merged = args !== undefined && args !== null && typeof args === 'object' ? { ...args } : {};
|
|
492
|
-
|
|
530
|
+
let mandatoryFields = _mandatoryCommandFields(clusterAgent, commandName);
|
|
531
|
+
if (mandatoryFields.length === 0) {
|
|
532
|
+
_log?.debug(`matter: could not resolve request fields for ${clusterName}.${commandName}`);
|
|
533
|
+
// Last resort: every LevelControl/ColorControl command carries the two
|
|
534
|
+
// options bitmaps (and the TLV encoder ignores unknown fields), so
|
|
535
|
+
// injecting them is safe even for the few commands without them.
|
|
536
|
+
if (_clusterName(clusterName) === 'levelControl' || _clusterName(clusterName) === 'colorControl') {
|
|
537
|
+
mandatoryFields = ['optionsMask', 'optionsOverride'];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (const field of mandatoryFields) {
|
|
493
541
|
if (merged[field] !== undefined) continue;
|
|
494
542
|
if (field === 'optionsMask' || field === 'optionsOverride') merged[field] = {};
|
|
495
543
|
else if (field === 'transitionTime') merged[field] = 0;
|
|
@@ -540,6 +588,13 @@ function _broadcastNodeAttributes(node, nodeId) {
|
|
|
540
588
|
value: _jsonSafe(value),
|
|
541
589
|
ts: Date.now(),
|
|
542
590
|
});
|
|
591
|
+
// Device/endpoint labels feed the device list — rebroadcast it
|
|
592
|
+
// when they change. This is also what completes a rename():
|
|
593
|
+
// the local mirror only learns the new nodeLabel when the
|
|
594
|
+
// device's subscription report arrives, i.e. exactly now.
|
|
595
|
+
if (attrName === 'nodeLabel' || attrName === 'productName') {
|
|
596
|
+
_broadcast?.({ type: 'matter:deviceList', devices: listPaired() });
|
|
597
|
+
}
|
|
543
598
|
});
|
|
544
599
|
count++;
|
|
545
600
|
}
|