zigbee-herdsman 5.0.4 → 6.0.1
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/.github/dependabot.yml +3 -0
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/typedoc.yaml +1 -1
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +29 -0
- package/biome.json +1 -1
- package/dist/adapter/ember/ezsp/buffalo.d.ts +0 -2
- package/dist/adapter/ember/ezsp/buffalo.d.ts.map +1 -1
- package/dist/adapter/ember/ezsp/buffalo.js +0 -4
- package/dist/adapter/ember/ezsp/buffalo.js.map +1 -1
- package/dist/adapter/ember/uart/ash.d.ts.map +1 -1
- package/dist/adapter/ember/uart/ash.js +0 -2
- package/dist/adapter/ember/uart/ash.js.map +1 -1
- package/dist/buffalo/buffalo.d.ts +5 -0
- package/dist/buffalo/buffalo.d.ts.map +1 -1
- package/dist/buffalo/buffalo.js +7 -0
- package/dist/buffalo/buffalo.js.map +1 -1
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/controller/controller.js +8 -11
- package/dist/controller/controller.js.map +1 -1
- package/dist/controller/events.d.ts +2 -1
- package/dist/controller/events.d.ts.map +1 -1
- package/dist/controller/helpers/request.d.ts.map +1 -1
- package/dist/controller/helpers/request.js +2 -1
- package/dist/controller/helpers/request.js.map +1 -1
- package/dist/controller/helpers/zclFrameConverter.d.ts +2 -4
- package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
- package/dist/controller/helpers/zclFrameConverter.js +2 -0
- package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
- package/dist/controller/model/device.d.ts +13 -24
- package/dist/controller/model/device.d.ts.map +1 -1
- package/dist/controller/model/device.js +88 -129
- package/dist/controller/model/device.js.map +1 -1
- package/dist/controller/model/endpoint.d.ts +17 -16
- package/dist/controller/model/endpoint.d.ts.map +1 -1
- package/dist/controller/model/endpoint.js +31 -16
- package/dist/controller/model/endpoint.js.map +1 -1
- package/dist/controller/model/group.d.ts +6 -6
- package/dist/controller/model/group.d.ts.map +1 -1
- package/dist/controller/model/group.js +5 -3
- package/dist/controller/model/group.js.map +1 -1
- package/dist/controller/model/index.d.ts +1 -0
- package/dist/controller/model/index.d.ts.map +1 -1
- package/dist/controller/model/index.js +3 -1
- package/dist/controller/model/index.js.map +1 -1
- package/dist/controller/model/zigbeeEntity.d.ts +8 -0
- package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
- package/dist/controller/model/zigbeeEntity.js +11 -0
- package/dist/controller/model/zigbeeEntity.js.map +1 -0
- package/dist/controller/tstype.d.ts +39 -0
- package/dist/controller/tstype.d.ts.map +1 -1
- package/dist/zspec/zcl/buffaloZcl.d.ts +32 -17
- package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
- package/dist/zspec/zcl/buffaloZcl.js +257 -121
- package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
- package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/cluster.js +156 -33
- package/dist/zspec/zcl/definition/cluster.js.map +1 -1
- package/dist/zspec/zcl/definition/clusters-typegen.d.ts +2 -0
- package/dist/zspec/zcl/definition/clusters-typegen.d.ts.map +1 -0
- package/dist/zspec/zcl/definition/clusters-typegen.js +348 -0
- package/dist/zspec/zcl/definition/clusters-typegen.js.map +1 -0
- package/dist/zspec/zcl/definition/clusters-types.d.ts +7238 -0
- package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
- package/dist/zspec/zcl/definition/clusters-types.js +3 -0
- package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
- package/dist/zspec/zcl/definition/enums.d.ts +14 -6
- package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/enums.js +15 -6
- package/dist/zspec/zcl/definition/enums.js.map +1 -1
- package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/foundation.js +43 -15
- package/dist/zspec/zcl/definition/foundation.js.map +1 -1
- package/dist/zspec/zcl/definition/tstype.d.ts +105 -11
- package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
- package/dist/zspec/zcl/index.d.ts +1 -0
- package/dist/zspec/zcl/index.d.ts.map +1 -1
- package/dist/zspec/zcl/index.js.map +1 -1
- package/dist/zspec/zcl/utils.d.ts +1 -1
- package/dist/zspec/zcl/utils.d.ts.map +1 -1
- package/dist/zspec/zcl/utils.js +1 -1
- package/dist/zspec/zcl/utils.js.map +1 -1
- package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
- package/dist/zspec/zcl/zclFrame.js +32 -20
- package/dist/zspec/zcl/zclFrame.js.map +1 -1
- package/dist/zspec/zdo/buffaloZdo.d.ts +0 -6
- package/dist/zspec/zdo/buffaloZdo.d.ts.map +1 -1
- package/dist/zspec/zdo/buffaloZdo.js +0 -8
- package/dist/zspec/zdo/buffaloZdo.js.map +1 -1
- package/package.json +3 -3
- package/src/adapter/ember/ezsp/buffalo.ts +0 -5
- package/src/adapter/ember/uart/ash.ts +0 -2
- package/src/adapter/ezsp/driver/driver.ts +1 -1
- package/src/buffalo/buffalo.ts +8 -0
- package/src/controller/controller.ts +13 -16
- package/src/controller/events.ts +2 -1
- package/src/controller/greenPower.ts +4 -4
- package/src/controller/helpers/request.ts +3 -1
- package/src/controller/helpers/zclFrameConverter.ts +13 -17
- package/src/controller/model/device.ts +103 -148
- package/src/controller/model/endpoint.ts +112 -64
- package/src/controller/model/group.ts +33 -9
- package/src/controller/model/index.ts +1 -0
- package/src/controller/model/zigbeeEntity.ts +30 -0
- package/src/controller/tstype.ts +251 -16
- package/src/zspec/zcl/buffaloZcl.ts +323 -238
- package/src/zspec/zcl/definition/cluster.ts +156 -33
- package/src/zspec/zcl/definition/clusters-typegen.ts +588 -0
- package/src/zspec/zcl/definition/clusters-types.ts +7331 -0
- package/src/zspec/zcl/definition/enums.ts +14 -5
- package/src/zspec/zcl/definition/foundation.ts +43 -15
- package/src/zspec/zcl/definition/tstype.ts +118 -8
- package/src/zspec/zcl/index.ts +1 -0
- package/src/zspec/zcl/utils.ts +1 -1
- package/src/zspec/zcl/zclFrame.ts +37 -19
- package/src/zspec/zdo/buffaloZdo.ts +0 -9
- package/test/controller.test.ts +356 -896
- package/test/greenpower.test.ts +0 -12
- package/test/zcl.test.ts +13 -11
- package/test/zspec/zcl/buffalo.test.ts +216 -74
- package/test/zspec/zcl/frame.test.ts +62 -28
- package/test/zspec/zcl/utils.test.ts +4 -4
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
-
|
|
3
2
|
import type {Events as AdapterEvents} from "../../adapter";
|
|
4
3
|
import {logger} from "../../utils/logger";
|
|
5
4
|
import * as ZSpec from "../../zspec";
|
|
6
5
|
import {BroadcastAddress} from "../../zspec/enums";
|
|
7
6
|
import type {Eui64} from "../../zspec/tstypes";
|
|
8
7
|
import * as Zcl from "../../zspec/zcl";
|
|
8
|
+
import type {TFoundation} from "../../zspec/zcl/definition/clusters-types";
|
|
9
9
|
import type * as ZclTypes from "../../zspec/zcl/definition/tstype";
|
|
10
10
|
import * as Zdo from "../../zspec/zdo";
|
|
11
11
|
import Request from "../helpers/request";
|
|
12
12
|
import RequestQueue from "../helpers/requestQueue";
|
|
13
13
|
import * as ZclFrameConverter from "../helpers/zclFrameConverter";
|
|
14
14
|
import zclTransactionSequenceNumber from "../helpers/zclTransactionSequenceNumber";
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
ClusterOrRawAttributeKeys,
|
|
17
|
+
ClusterOrRawAttributes,
|
|
18
|
+
ClusterOrRawPayload,
|
|
19
|
+
ClusterOrRawWriteAttributes,
|
|
20
|
+
FoundationOrRawPayload,
|
|
21
|
+
KeyValue,
|
|
22
|
+
PartialClusterOrRawWriteAttributes,
|
|
23
|
+
SendPolicy,
|
|
24
|
+
TCustomCluster,
|
|
25
|
+
} from "../tstype";
|
|
16
26
|
import Device from "./device";
|
|
17
27
|
import Entity from "./entity";
|
|
18
28
|
import Group from "./group";
|
|
29
|
+
import {ZigbeeEntity} from "./zigbeeEntity";
|
|
19
30
|
|
|
20
31
|
const NS = "zh:controller:endpoint";
|
|
21
32
|
|
|
22
|
-
export interface ConfigureReportingItem {
|
|
23
|
-
attribute:
|
|
33
|
+
export interface ConfigureReportingItem<Cl extends string | number, Custom extends TCustomCluster | undefined = undefined> {
|
|
34
|
+
attribute: ClusterOrRawAttributeKeys<Cl, Custom>[number] | {ID: number; type: number};
|
|
24
35
|
minimumReportInterval: number;
|
|
25
36
|
maximumReportInterval: number;
|
|
26
37
|
reportableChange: number;
|
|
@@ -86,7 +97,7 @@ interface ConfiguredReporting {
|
|
|
86
97
|
reportableChange: number;
|
|
87
98
|
}
|
|
88
99
|
|
|
89
|
-
export class Endpoint extends
|
|
100
|
+
export class Endpoint extends ZigbeeEntity {
|
|
90
101
|
public deviceID?: number;
|
|
91
102
|
public inputClusters: number[];
|
|
92
103
|
public outputClusters: number[];
|
|
@@ -372,16 +383,22 @@ export class Endpoint extends Entity {
|
|
|
372
383
|
if (invalid) throw new Zcl.StatusError(invalid);
|
|
373
384
|
}
|
|
374
385
|
|
|
375
|
-
public async report
|
|
386
|
+
public async report<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
387
|
+
clusterKey: Cl,
|
|
388
|
+
attributes: PartialClusterOrRawWriteAttributes<Cl, Custom>,
|
|
389
|
+
options?: Options,
|
|
390
|
+
): Promise<void> {
|
|
376
391
|
const cluster = this.getCluster(clusterKey, undefined, options?.manufacturerCode);
|
|
377
|
-
const payload:
|
|
392
|
+
const payload: TFoundation["report"] = [];
|
|
378
393
|
|
|
379
|
-
for (const
|
|
394
|
+
for (const nameOrID in attributes) {
|
|
380
395
|
const attribute = cluster.getAttribute(nameOrID);
|
|
381
396
|
|
|
382
397
|
if (attribute) {
|
|
383
|
-
payload.push({attrId: attribute.ID, attrData:
|
|
398
|
+
payload.push({attrId: attribute.ID, attrData: attributes[nameOrID], dataType: attribute.type});
|
|
384
399
|
} else if (!Number.isNaN(Number(nameOrID))) {
|
|
400
|
+
const value = attributes[nameOrID];
|
|
401
|
+
|
|
385
402
|
payload.push({attrId: Number(nameOrID), attrData: value.value, dataType: value.type});
|
|
386
403
|
} else {
|
|
387
404
|
throw new Error(`Unknown attribute '${nameOrID}', specify either an existing attribute or a number`);
|
|
@@ -391,23 +408,29 @@ export class Endpoint extends Entity {
|
|
|
391
408
|
await this.zclCommand(cluster, "report", payload, options, attributes);
|
|
392
409
|
}
|
|
393
410
|
|
|
394
|
-
public async write
|
|
411
|
+
public async write<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
412
|
+
clusterKey: Cl,
|
|
413
|
+
attributes: PartialClusterOrRawWriteAttributes<Cl, Custom>,
|
|
414
|
+
options?: Options,
|
|
415
|
+
): Promise<void> {
|
|
395
416
|
const cluster = this.getCluster(clusterKey, undefined, options?.manufacturerCode);
|
|
396
417
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, true, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
397
|
-
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet(
|
|
418
|
+
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet<Cl, Custom>(
|
|
398
419
|
cluster,
|
|
399
420
|
Object.keys(attributes),
|
|
400
421
|
optionsWithDefaults.manufacturerCode,
|
|
401
422
|
"write",
|
|
402
423
|
);
|
|
424
|
+
const payload: TFoundation["write"] = [];
|
|
403
425
|
|
|
404
|
-
const
|
|
405
|
-
for (const [nameOrID, value] of Object.entries(attributes)) {
|
|
426
|
+
for (const nameOrID in attributes) {
|
|
406
427
|
const attribute = cluster.getAttribute(nameOrID);
|
|
407
428
|
|
|
408
429
|
if (attribute) {
|
|
409
|
-
payload.push({attrId: attribute.ID, attrData:
|
|
430
|
+
payload.push({attrId: attribute.ID, attrData: attributes[nameOrID], dataType: attribute.type});
|
|
410
431
|
} else if (!Number.isNaN(Number(nameOrID))) {
|
|
432
|
+
const value = attributes[nameOrID];
|
|
433
|
+
|
|
411
434
|
payload.push({attrId: Number(nameOrID), attrData: value.value, dataType: value.type});
|
|
412
435
|
} else {
|
|
413
436
|
throw new Error(`Unknown attribute '${nameOrID}', specify either an existing attribute or a number`);
|
|
@@ -417,17 +440,21 @@ export class Endpoint extends Entity {
|
|
|
417
440
|
await this.zclCommand(cluster, optionsWithDefaults.writeUndiv ? "writeUndiv" : "write", payload, optionsWithDefaults, attributes, true);
|
|
418
441
|
}
|
|
419
442
|
|
|
420
|
-
public async writeResponse(
|
|
421
|
-
clusterKey:
|
|
443
|
+
public async writeResponse<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
444
|
+
clusterKey: Cl,
|
|
422
445
|
transactionSequenceNumber: number,
|
|
423
|
-
attributes:
|
|
446
|
+
attributes: Partial<Record<ClusterOrRawAttributeKeys<Cl, Custom>[number], TFoundation["writeRsp"][number]>> &
|
|
447
|
+
Record<number, TFoundation["writeRsp"][number]>,
|
|
424
448
|
options?: Options,
|
|
425
449
|
): Promise<void> {
|
|
426
450
|
assert(options?.transactionSequenceNumber === undefined, "Use parameter");
|
|
427
451
|
const cluster = this.getCluster(clusterKey, undefined, options?.manufacturerCode);
|
|
428
|
-
const payload:
|
|
452
|
+
const payload: TFoundation["writeRsp"] = [];
|
|
453
|
+
|
|
454
|
+
for (const nameOrID in attributes) {
|
|
455
|
+
// biome-ignore lint/style/noNonNullAssertion: from loop
|
|
456
|
+
const value = attributes[nameOrID]!;
|
|
429
457
|
|
|
430
|
-
for (const [nameOrID, value] of Object.entries(attributes)) {
|
|
431
458
|
if (value.status !== undefined) {
|
|
432
459
|
const attribute = cluster.getAttribute(nameOrID);
|
|
433
460
|
|
|
@@ -452,17 +479,22 @@ export class Endpoint extends Entity {
|
|
|
452
479
|
);
|
|
453
480
|
}
|
|
454
481
|
|
|
455
|
-
|
|
482
|
+
// XXX: ideally, the return type should limit to the contents of the `attributes` param
|
|
483
|
+
public async read<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
484
|
+
clusterKey: Cl,
|
|
485
|
+
attributes: ClusterOrRawAttributeKeys<Cl, Custom>,
|
|
486
|
+
options?: Options,
|
|
487
|
+
): Promise<ClusterOrRawAttributes<Cl, Custom>> {
|
|
456
488
|
const device = this.getDevice();
|
|
457
489
|
const cluster = this.getCluster(clusterKey, device, options?.manufacturerCode);
|
|
458
490
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, true, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
459
|
-
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet(
|
|
491
|
+
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet<Cl, Custom>(
|
|
460
492
|
cluster,
|
|
461
493
|
attributes,
|
|
462
494
|
optionsWithDefaults.manufacturerCode,
|
|
463
495
|
"read",
|
|
464
496
|
);
|
|
465
|
-
const payload:
|
|
497
|
+
const payload: TFoundation["read"] = [];
|
|
466
498
|
|
|
467
499
|
for (const attribute of attributes) {
|
|
468
500
|
if (typeof attribute === "number") {
|
|
@@ -480,29 +512,30 @@ export class Endpoint extends Entity {
|
|
|
480
512
|
|
|
481
513
|
const resultFrame = await this.zclCommand(cluster, "read", payload, optionsWithDefaults, attributes, true);
|
|
482
514
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return {};
|
|
515
|
+
return resultFrame
|
|
516
|
+
? ZclFrameConverter.attributeKeyValue<Cl, Custom>(resultFrame, device.manufacturerID, device.customClusters)
|
|
517
|
+
: ({} as ClusterOrRawWriteAttributes<Cl, Custom>);
|
|
488
518
|
}
|
|
489
519
|
|
|
490
|
-
public async readResponse(
|
|
491
|
-
clusterKey:
|
|
520
|
+
public async readResponse<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
521
|
+
clusterKey: Cl,
|
|
492
522
|
transactionSequenceNumber: number,
|
|
493
|
-
attributes:
|
|
523
|
+
attributes: PartialClusterOrRawWriteAttributes<Cl, Custom>,
|
|
494
524
|
options?: Options,
|
|
495
525
|
): Promise<void> {
|
|
496
526
|
assert(options?.transactionSequenceNumber === undefined, "Use parameter");
|
|
497
527
|
|
|
498
528
|
const cluster = this.getCluster(clusterKey, undefined, options?.manufacturerCode);
|
|
499
|
-
const payload:
|
|
500
|
-
|
|
529
|
+
const payload: TFoundation["readRsp"] = [];
|
|
530
|
+
|
|
531
|
+
for (const nameOrID in attributes) {
|
|
501
532
|
const attribute = cluster.getAttribute(nameOrID);
|
|
502
533
|
|
|
503
534
|
if (attribute) {
|
|
504
|
-
payload.push({attrId: attribute.ID, attrData:
|
|
535
|
+
payload.push({attrId: attribute.ID, attrData: attributes[nameOrID], dataType: attribute.type, status: 0});
|
|
505
536
|
} else if (!Number.isNaN(Number(nameOrID))) {
|
|
537
|
+
const value = attributes[nameOrID];
|
|
538
|
+
|
|
506
539
|
payload.push({attrId: Number(nameOrID), attrData: value.value, dataType: value.type, status: 0});
|
|
507
540
|
} else {
|
|
508
541
|
throw new Error(`Unknown attribute '${nameOrID}', specify either an existing attribute or a number`);
|
|
@@ -688,19 +721,23 @@ export class Endpoint extends Entity {
|
|
|
688
721
|
await this.zclCommand(clusterID, "defaultRsp", payload, {direction: Zcl.Direction.SERVER_TO_CLIENT, ...options, transactionSequenceNumber});
|
|
689
722
|
}
|
|
690
723
|
|
|
691
|
-
public async configureReporting
|
|
724
|
+
public async configureReporting<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
725
|
+
clusterKey: Cl,
|
|
726
|
+
items: ConfigureReportingItem<Cl, Custom>[],
|
|
727
|
+
options?: Options,
|
|
728
|
+
): Promise<void> {
|
|
692
729
|
const cluster = this.getCluster(clusterKey, undefined, options?.manufacturerCode);
|
|
693
730
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, true, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
694
|
-
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet(
|
|
731
|
+
optionsWithDefaults.manufacturerCode = this.ensureManufacturerCodeIsUniqueAndGet<Cl, Custom>(
|
|
695
732
|
cluster,
|
|
696
733
|
items,
|
|
697
734
|
optionsWithDefaults.manufacturerCode,
|
|
698
735
|
"configureReporting",
|
|
699
736
|
);
|
|
700
737
|
|
|
701
|
-
const payload = items.map((item):
|
|
702
|
-
let dataType: number
|
|
703
|
-
let attrId: number
|
|
738
|
+
const payload = items.map((item): TFoundation["configReport"][number] => {
|
|
739
|
+
let dataType: number;
|
|
740
|
+
let attrId: number;
|
|
704
741
|
|
|
705
742
|
if (typeof item.attribute === "object") {
|
|
706
743
|
dataType = item.attribute.type;
|
|
@@ -711,13 +748,15 @@ export class Endpoint extends Entity {
|
|
|
711
748
|
if (attribute) {
|
|
712
749
|
dataType = attribute.type;
|
|
713
750
|
attrId = attribute.ID;
|
|
751
|
+
} else {
|
|
752
|
+
throw new Error(`Invalid attribute '${item.attribute}' for cluster '${clusterKey}'`);
|
|
714
753
|
}
|
|
715
754
|
}
|
|
716
755
|
|
|
717
756
|
return {
|
|
718
757
|
direction: Zcl.Direction.CLIENT_TO_SERVER,
|
|
719
|
-
attrId,
|
|
720
|
-
dataType,
|
|
758
|
+
attrId,
|
|
759
|
+
dataType,
|
|
721
760
|
minRepIntval: item.minimumReportInterval,
|
|
722
761
|
maxRepIntval: item.maximumReportInterval,
|
|
723
762
|
repChange: item.reportableChange,
|
|
@@ -742,9 +781,10 @@ export class Endpoint extends Entity {
|
|
|
742
781
|
this._configuredReportings.push({
|
|
743
782
|
cluster: cluster.ID,
|
|
744
783
|
attrId: entry.attrId,
|
|
745
|
-
minRepIntval: entry.minRepIntval,
|
|
746
|
-
maxRepIntval: entry.maxRepIntval,
|
|
747
|
-
|
|
784
|
+
minRepIntval: entry.minRepIntval as number,
|
|
785
|
+
maxRepIntval: entry.maxRepIntval as number,
|
|
786
|
+
// expects items[].attribute to always point to a number DataType
|
|
787
|
+
repChange: entry.repChange as number,
|
|
748
788
|
manufacturerCode: optionsWithDefaults.manufacturerCode,
|
|
749
789
|
});
|
|
750
790
|
}
|
|
@@ -753,15 +793,19 @@ export class Endpoint extends Entity {
|
|
|
753
793
|
this.save();
|
|
754
794
|
}
|
|
755
795
|
|
|
756
|
-
public async writeStructured
|
|
796
|
+
public async writeStructured<Cl extends number | string>(
|
|
797
|
+
clusterKey: Cl,
|
|
798
|
+
payload: TFoundation["writeStructured"],
|
|
799
|
+
options?: Options,
|
|
800
|
+
): Promise<void> {
|
|
757
801
|
await this.zclCommand(clusterKey, "writeStructured", payload, options);
|
|
758
802
|
// TODO: support `writeStructuredResponse`
|
|
759
803
|
}
|
|
760
804
|
|
|
761
|
-
public async command(
|
|
762
|
-
clusterKey:
|
|
763
|
-
commandKey:
|
|
764
|
-
payload:
|
|
805
|
+
public async command<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
806
|
+
clusterKey: Cl,
|
|
807
|
+
commandKey: Co,
|
|
808
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom>,
|
|
765
809
|
options?: Options,
|
|
766
810
|
): Promise<undefined | KeyValue> {
|
|
767
811
|
const frame = await this.zclCommand(clusterKey, commandKey, payload, options, undefined, false, Zcl.FrameType.SPECIFIC);
|
|
@@ -770,10 +814,10 @@ export class Endpoint extends Entity {
|
|
|
770
814
|
}
|
|
771
815
|
}
|
|
772
816
|
|
|
773
|
-
public async commandResponse(
|
|
774
|
-
clusterKey:
|
|
775
|
-
commandKey:
|
|
776
|
-
payload:
|
|
817
|
+
public async commandResponse<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
818
|
+
clusterKey: Cl,
|
|
819
|
+
commandKey: Co,
|
|
820
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom>,
|
|
777
821
|
options?: Options,
|
|
778
822
|
transactionSequenceNumber?: number,
|
|
779
823
|
): Promise<void> {
|
|
@@ -844,8 +888,12 @@ export class Endpoint extends Entity {
|
|
|
844
888
|
const promise = new Promise<{header: Zcl.Header; payload: KeyValue}>((resolve, reject) => {
|
|
845
889
|
waiter.promise.then(
|
|
846
890
|
(payload) => {
|
|
847
|
-
|
|
848
|
-
|
|
891
|
+
try {
|
|
892
|
+
const frame = Zcl.Frame.fromBuffer(payload.clusterID, payload.header, payload.data, device.customClusters);
|
|
893
|
+
resolve({header: frame.header, payload: frame.payload});
|
|
894
|
+
} catch (error) {
|
|
895
|
+
reject(error);
|
|
896
|
+
}
|
|
849
897
|
},
|
|
850
898
|
(error) => reject(error),
|
|
851
899
|
);
|
|
@@ -875,9 +923,9 @@ export class Endpoint extends Entity {
|
|
|
875
923
|
};
|
|
876
924
|
}
|
|
877
925
|
|
|
878
|
-
private ensureManufacturerCodeIsUniqueAndGet(
|
|
926
|
+
private ensureManufacturerCodeIsUniqueAndGet<Cl extends string | number, Custom extends TCustomCluster | undefined = undefined>(
|
|
879
927
|
cluster: ZclTypes.Cluster,
|
|
880
|
-
attributes: (string | number)[] | ConfigureReportingItem[],
|
|
928
|
+
attributes: (string | number)[] | ConfigureReportingItem<Cl, Custom>[],
|
|
881
929
|
fallbackManufacturerCode: number | undefined, // XXX: problematic undefined for a "fallback"?
|
|
882
930
|
caller: string,
|
|
883
931
|
): number | undefined {
|
|
@@ -958,10 +1006,10 @@ export class Endpoint extends Entity {
|
|
|
958
1006
|
}
|
|
959
1007
|
}
|
|
960
1008
|
|
|
961
|
-
public async zclCommand(
|
|
962
|
-
clusterKey:
|
|
963
|
-
commandKey:
|
|
964
|
-
payload:
|
|
1009
|
+
public async zclCommand<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
1010
|
+
clusterKey: Cl | ZclTypes.Cluster,
|
|
1011
|
+
commandKey: Co | ZclTypes.Command,
|
|
1012
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom> | FoundationOrRawPayload<Co>,
|
|
965
1013
|
options?: Options,
|
|
966
1014
|
logPayload?: KeyValue,
|
|
967
1015
|
checkStatus = false,
|
|
@@ -1015,12 +1063,12 @@ export class Endpoint extends Entity {
|
|
|
1015
1063
|
}
|
|
1016
1064
|
}
|
|
1017
1065
|
|
|
1018
|
-
public async zclCommandBroadcast(
|
|
1066
|
+
public async zclCommandBroadcast<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
1019
1067
|
endpoint: number,
|
|
1020
1068
|
destination: BroadcastAddress,
|
|
1021
|
-
clusterKey:
|
|
1022
|
-
commandKey:
|
|
1023
|
-
payload:
|
|
1069
|
+
clusterKey: Cl,
|
|
1070
|
+
commandKey: Co,
|
|
1071
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom> | FoundationOrRawPayload<Co>,
|
|
1024
1072
|
options?: Options,
|
|
1025
1073
|
): Promise<void> {
|
|
1026
1074
|
const device = this.getDevice();
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import {logger} from "../../utils/logger";
|
|
3
3
|
import * as Zcl from "../../zspec/zcl";
|
|
4
|
+
import type {TFoundation} from "../../zspec/zcl/definition/clusters-types";
|
|
4
5
|
import type {CustomClusters} from "../../zspec/zcl/definition/tstype";
|
|
5
6
|
import zclTransactionSequenceNumber from "../helpers/zclTransactionSequenceNumber";
|
|
6
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
ClusterOrRawAttributeKeys,
|
|
9
|
+
ClusterOrRawPayload,
|
|
10
|
+
DatabaseEntry,
|
|
11
|
+
KeyValue,
|
|
12
|
+
PartialClusterOrRawWriteAttributes,
|
|
13
|
+
TCustomCluster,
|
|
14
|
+
} from "../tstype";
|
|
7
15
|
import Device from "./device";
|
|
8
16
|
import type Endpoint from "./endpoint";
|
|
9
17
|
import Entity from "./entity";
|
|
18
|
+
import {ZigbeeEntity} from "./zigbeeEntity";
|
|
10
19
|
|
|
11
20
|
const NS = "zh:controller:group";
|
|
12
21
|
|
|
@@ -23,7 +32,7 @@ interface OptionsWithDefaults extends Options {
|
|
|
23
32
|
reservedBits: number;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
export class Group extends
|
|
35
|
+
export class Group extends ZigbeeEntity {
|
|
27
36
|
private databaseID: number;
|
|
28
37
|
public readonly groupID: number;
|
|
29
38
|
private readonly _members: Endpoint[];
|
|
@@ -248,18 +257,24 @@ export class Group extends Entity {
|
|
|
248
257
|
* Zigbee functions
|
|
249
258
|
*/
|
|
250
259
|
|
|
251
|
-
public async write
|
|
260
|
+
public async write<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
261
|
+
clusterKey: Cl,
|
|
262
|
+
attributes: PartialClusterOrRawWriteAttributes<Cl, Custom>,
|
|
263
|
+
options?: Options,
|
|
264
|
+
): Promise<void> {
|
|
252
265
|
const customClusters = this.#customClusters[options?.direction === Zcl.Direction.SERVER_TO_CLIENT ? 1 : 0 /* default to CLIENT_TO_SERVER */];
|
|
253
266
|
const cluster = Zcl.Utils.getCluster(clusterKey, options?.manufacturerCode, customClusters);
|
|
254
267
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
255
|
-
const payload:
|
|
268
|
+
const payload: TFoundation["write"] = [];
|
|
256
269
|
|
|
257
|
-
for (const
|
|
270
|
+
for (const nameOrID in attributes) {
|
|
258
271
|
const attribute = cluster.getAttribute(nameOrID);
|
|
259
272
|
|
|
260
273
|
if (attribute) {
|
|
261
|
-
payload.push({attrId: attribute.ID, attrData:
|
|
274
|
+
payload.push({attrId: attribute.ID, attrData: attributes[nameOrID], dataType: attribute.type});
|
|
262
275
|
} else if (!Number.isNaN(Number(nameOrID))) {
|
|
276
|
+
const value = attributes[nameOrID];
|
|
277
|
+
|
|
263
278
|
payload.push({attrId: Number(nameOrID), attrData: value.value, dataType: value.type});
|
|
264
279
|
} else {
|
|
265
280
|
throw new Error(`Unknown attribute '${nameOrID}', specify either an existing attribute or a number`);
|
|
@@ -295,11 +310,15 @@ export class Group extends Entity {
|
|
|
295
310
|
}
|
|
296
311
|
}
|
|
297
312
|
|
|
298
|
-
public async read
|
|
313
|
+
public async read<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
314
|
+
clusterKey: Cl,
|
|
315
|
+
attributes: ClusterOrRawAttributeKeys<Cl, Custom>,
|
|
316
|
+
options?: Options,
|
|
317
|
+
): Promise<undefined> {
|
|
299
318
|
const customClusters = this.#customClusters[options?.direction === Zcl.Direction.SERVER_TO_CLIENT ? 1 : 0 /* default to CLIENT_TO_SERVER */];
|
|
300
319
|
const cluster = Zcl.Utils.getCluster(clusterKey, options?.manufacturerCode, customClusters);
|
|
301
320
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
302
|
-
const payload:
|
|
321
|
+
const payload: TFoundation["read"] = [];
|
|
303
322
|
|
|
304
323
|
for (const attribute of attributes) {
|
|
305
324
|
if (typeof attribute === "number") {
|
|
@@ -344,7 +363,12 @@ export class Group extends Entity {
|
|
|
344
363
|
}
|
|
345
364
|
}
|
|
346
365
|
|
|
347
|
-
public async command
|
|
366
|
+
public async command<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
367
|
+
clusterKey: Cl,
|
|
368
|
+
commandKey: Co,
|
|
369
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom>,
|
|
370
|
+
options?: Options,
|
|
371
|
+
): Promise<undefined> {
|
|
348
372
|
const customClusters = this.#customClusters[options?.direction === Zcl.Direction.SERVER_TO_CLIENT ? 1 : 0 /* default to CLIENT_TO_SERVER */];
|
|
349
373
|
const cluster = Zcl.Utils.getCluster(clusterKey, options?.manufacturerCode, customClusters);
|
|
350
374
|
const optionsWithDefaults = this.getOptionsWithDefaults(options, Zcl.Direction.CLIENT_TO_SERVER, cluster.manufacturerCode);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClusterOrRawAttributeKeys,
|
|
3
|
+
ClusterOrRawAttributes,
|
|
4
|
+
ClusterOrRawPayload,
|
|
5
|
+
KeyValue,
|
|
6
|
+
PartialClusterOrRawWriteAttributes,
|
|
7
|
+
TCustomCluster,
|
|
8
|
+
} from "../tstype";
|
|
9
|
+
import Entity from "./entity";
|
|
10
|
+
|
|
11
|
+
export abstract class ZigbeeEntity extends Entity {
|
|
12
|
+
public abstract read<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
13
|
+
clusterKey: Cl,
|
|
14
|
+
attributes: ClusterOrRawAttributeKeys<Cl, Custom>,
|
|
15
|
+
options?: KeyValue,
|
|
16
|
+
): Promise<ClusterOrRawAttributes<Cl, Custom> | undefined>;
|
|
17
|
+
|
|
18
|
+
public abstract write<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
19
|
+
clusterKey: Cl,
|
|
20
|
+
attributes: PartialClusterOrRawWriteAttributes<Cl, Custom>,
|
|
21
|
+
options?: KeyValue,
|
|
22
|
+
): Promise<void>;
|
|
23
|
+
|
|
24
|
+
public abstract command<Cl extends number | string, Co extends number | string, Custom extends TCustomCluster | undefined = undefined>(
|
|
25
|
+
clusterKey: Cl,
|
|
26
|
+
commandKey: Co,
|
|
27
|
+
payload: ClusterOrRawPayload<Cl, Co, Custom>,
|
|
28
|
+
options?: KeyValue,
|
|
29
|
+
): Promise<undefined | KeyValue>;
|
|
30
|
+
}
|