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.
Files changed (122) hide show
  1. package/.github/dependabot.yml +3 -0
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/typedoc.yaml +1 -1
  4. package/.release-please-manifest.json +1 -1
  5. package/CHANGELOG.md +29 -0
  6. package/biome.json +1 -1
  7. package/dist/adapter/ember/ezsp/buffalo.d.ts +0 -2
  8. package/dist/adapter/ember/ezsp/buffalo.d.ts.map +1 -1
  9. package/dist/adapter/ember/ezsp/buffalo.js +0 -4
  10. package/dist/adapter/ember/ezsp/buffalo.js.map +1 -1
  11. package/dist/adapter/ember/uart/ash.d.ts.map +1 -1
  12. package/dist/adapter/ember/uart/ash.js +0 -2
  13. package/dist/adapter/ember/uart/ash.js.map +1 -1
  14. package/dist/buffalo/buffalo.d.ts +5 -0
  15. package/dist/buffalo/buffalo.d.ts.map +1 -1
  16. package/dist/buffalo/buffalo.js +7 -0
  17. package/dist/buffalo/buffalo.js.map +1 -1
  18. package/dist/controller/controller.d.ts.map +1 -1
  19. package/dist/controller/controller.js +8 -11
  20. package/dist/controller/controller.js.map +1 -1
  21. package/dist/controller/events.d.ts +2 -1
  22. package/dist/controller/events.d.ts.map +1 -1
  23. package/dist/controller/helpers/request.d.ts.map +1 -1
  24. package/dist/controller/helpers/request.js +2 -1
  25. package/dist/controller/helpers/request.js.map +1 -1
  26. package/dist/controller/helpers/zclFrameConverter.d.ts +2 -4
  27. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  28. package/dist/controller/helpers/zclFrameConverter.js +2 -0
  29. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  30. package/dist/controller/model/device.d.ts +13 -24
  31. package/dist/controller/model/device.d.ts.map +1 -1
  32. package/dist/controller/model/device.js +88 -129
  33. package/dist/controller/model/device.js.map +1 -1
  34. package/dist/controller/model/endpoint.d.ts +17 -16
  35. package/dist/controller/model/endpoint.d.ts.map +1 -1
  36. package/dist/controller/model/endpoint.js +31 -16
  37. package/dist/controller/model/endpoint.js.map +1 -1
  38. package/dist/controller/model/group.d.ts +6 -6
  39. package/dist/controller/model/group.d.ts.map +1 -1
  40. package/dist/controller/model/group.js +5 -3
  41. package/dist/controller/model/group.js.map +1 -1
  42. package/dist/controller/model/index.d.ts +1 -0
  43. package/dist/controller/model/index.d.ts.map +1 -1
  44. package/dist/controller/model/index.js +3 -1
  45. package/dist/controller/model/index.js.map +1 -1
  46. package/dist/controller/model/zigbeeEntity.d.ts +8 -0
  47. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  48. package/dist/controller/model/zigbeeEntity.js +11 -0
  49. package/dist/controller/model/zigbeeEntity.js.map +1 -0
  50. package/dist/controller/tstype.d.ts +39 -0
  51. package/dist/controller/tstype.d.ts.map +1 -1
  52. package/dist/zspec/zcl/buffaloZcl.d.ts +32 -17
  53. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  54. package/dist/zspec/zcl/buffaloZcl.js +257 -121
  55. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  56. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  57. package/dist/zspec/zcl/definition/cluster.js +156 -33
  58. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  59. package/dist/zspec/zcl/definition/clusters-typegen.d.ts +2 -0
  60. package/dist/zspec/zcl/definition/clusters-typegen.d.ts.map +1 -0
  61. package/dist/zspec/zcl/definition/clusters-typegen.js +348 -0
  62. package/dist/zspec/zcl/definition/clusters-typegen.js.map +1 -0
  63. package/dist/zspec/zcl/definition/clusters-types.d.ts +7238 -0
  64. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
  65. package/dist/zspec/zcl/definition/clusters-types.js +3 -0
  66. package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
  67. package/dist/zspec/zcl/definition/enums.d.ts +14 -6
  68. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  69. package/dist/zspec/zcl/definition/enums.js +15 -6
  70. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  71. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  72. package/dist/zspec/zcl/definition/foundation.js +43 -15
  73. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  74. package/dist/zspec/zcl/definition/tstype.d.ts +105 -11
  75. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  76. package/dist/zspec/zcl/index.d.ts +1 -0
  77. package/dist/zspec/zcl/index.d.ts.map +1 -1
  78. package/dist/zspec/zcl/index.js.map +1 -1
  79. package/dist/zspec/zcl/utils.d.ts +1 -1
  80. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  81. package/dist/zspec/zcl/utils.js +1 -1
  82. package/dist/zspec/zcl/utils.js.map +1 -1
  83. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  84. package/dist/zspec/zcl/zclFrame.js +32 -20
  85. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  86. package/dist/zspec/zdo/buffaloZdo.d.ts +0 -6
  87. package/dist/zspec/zdo/buffaloZdo.d.ts.map +1 -1
  88. package/dist/zspec/zdo/buffaloZdo.js +0 -8
  89. package/dist/zspec/zdo/buffaloZdo.js.map +1 -1
  90. package/package.json +3 -3
  91. package/src/adapter/ember/ezsp/buffalo.ts +0 -5
  92. package/src/adapter/ember/uart/ash.ts +0 -2
  93. package/src/adapter/ezsp/driver/driver.ts +1 -1
  94. package/src/buffalo/buffalo.ts +8 -0
  95. package/src/controller/controller.ts +13 -16
  96. package/src/controller/events.ts +2 -1
  97. package/src/controller/greenPower.ts +4 -4
  98. package/src/controller/helpers/request.ts +3 -1
  99. package/src/controller/helpers/zclFrameConverter.ts +13 -17
  100. package/src/controller/model/device.ts +103 -148
  101. package/src/controller/model/endpoint.ts +112 -64
  102. package/src/controller/model/group.ts +33 -9
  103. package/src/controller/model/index.ts +1 -0
  104. package/src/controller/model/zigbeeEntity.ts +30 -0
  105. package/src/controller/tstype.ts +251 -16
  106. package/src/zspec/zcl/buffaloZcl.ts +323 -238
  107. package/src/zspec/zcl/definition/cluster.ts +156 -33
  108. package/src/zspec/zcl/definition/clusters-typegen.ts +588 -0
  109. package/src/zspec/zcl/definition/clusters-types.ts +7331 -0
  110. package/src/zspec/zcl/definition/enums.ts +14 -5
  111. package/src/zspec/zcl/definition/foundation.ts +43 -15
  112. package/src/zspec/zcl/definition/tstype.ts +118 -8
  113. package/src/zspec/zcl/index.ts +1 -0
  114. package/src/zspec/zcl/utils.ts +1 -1
  115. package/src/zspec/zcl/zclFrame.ts +37 -19
  116. package/src/zspec/zdo/buffaloZdo.ts +0 -9
  117. package/test/controller.test.ts +356 -896
  118. package/test/greenpower.test.ts +0 -12
  119. package/test/zcl.test.ts +13 -11
  120. package/test/zspec/zcl/buffalo.test.ts +216 -74
  121. package/test/zspec/zcl/frame.test.ts +62 -28
  122. package/test/zspec/zcl/utils.test.ts +4 -4
@@ -1,5 +1,4 @@
1
1
  import assert from "node:assert";
2
-
3
2
  import type {Events as AdapterEvents} from "../../adapter";
4
3
  import type {LQINeighbor, RoutingTableEntry} from "../../adapter/tstype";
5
4
  import {wait} from "../../utils";
@@ -8,6 +7,7 @@ import * as ZSpec from "../../zspec";
8
7
  import {BroadcastAddress} from "../../zspec/enums";
9
8
  import type {Eui64} from "../../zspec/tstypes";
10
9
  import * as Zcl from "../../zspec/zcl";
10
+ import type {TPartialClusterAttributes} from "../../zspec/zcl/definition/clusters-types";
11
11
  import type {ClusterDefinition, CustomClusters} from "../../zspec/zcl/definition/tstype";
12
12
  import * as Zdo from "../../zspec/zdo";
13
13
  import type {ControllerEventMap} from "../controller";
@@ -23,6 +23,18 @@ const OneJanuary2000 = new Date("January 01, 2000 00:00:00 UTC+00:00").getTime()
23
23
 
24
24
  const NS = "zh:controller:device";
25
25
 
26
+ const INTERVIEW_GENBASIC_ATTRIBUTES = [
27
+ "modelId",
28
+ "manufacturerName",
29
+ "powerSource",
30
+ "zclVersion",
31
+ "appVersion",
32
+ "stackVersion",
33
+ "hwVersion",
34
+ "dateCode",
35
+ "swBuildId",
36
+ ] as const;
37
+
26
38
  interface Lqi {
27
39
  neighbors: {
28
40
  ieeeAddr: string;
@@ -49,22 +61,14 @@ export enum InterviewState {
49
61
  export class Device extends Entity<ControllerEventMap> {
50
62
  // biome-ignore lint/style/useNamingConvention: cross-repo impact
51
63
  private readonly ID: number;
52
- private _applicationVersion?: number;
53
- private _dateCode?: string;
64
+ #genBasic: TPartialClusterAttributes<"genBasic"> = {};
54
65
  private _endpoints: Endpoint[];
55
- private _hardwareVersion?: number;
56
66
  private _ieeeAddr: string;
57
67
  private _interviewState: InterviewState;
58
68
  private _lastSeen?: number;
59
69
  private _manufacturerID?: number;
60
- private _manufacturerName?: string;
61
- private _modelID?: string;
62
70
  private _networkAddress: number;
63
- private _powerSource?: string;
64
- private _softwareBuildID?: string;
65
- private _stackVersion?: number;
66
71
  private _type: DeviceType;
67
- private _zclVersion?: number;
68
72
  private _linkquality?: number;
69
73
  private _skipDefaultResponse: boolean;
70
74
  private _customReadResponse?: CustomReadResponse;
@@ -82,10 +86,10 @@ export class Device extends Entity<ControllerEventMap> {
82
86
  this._ieeeAddr = ieeeAddr;
83
87
  }
84
88
  get applicationVersion(): number | undefined {
85
- return this._applicationVersion;
89
+ return this.#genBasic.appVersion;
86
90
  }
87
- set applicationVersion(applicationVersion: number) {
88
- this._applicationVersion = applicationVersion;
91
+ set applicationVersion(version: number) {
92
+ this.#genBasic.appVersion = version;
89
93
  }
90
94
  get endpoints(): Endpoint[] {
91
95
  return this._endpoints;
@@ -109,28 +113,28 @@ export class Device extends Entity<ControllerEventMap> {
109
113
  return this._type;
110
114
  }
111
115
  get dateCode(): string | undefined {
112
- return this._dateCode;
116
+ return this.#genBasic.dateCode;
113
117
  }
114
- set dateCode(dateCode: string) {
115
- this._dateCode = dateCode;
118
+ set dateCode(code: string) {
119
+ this.#genBasic.dateCode = code;
116
120
  }
117
- set hardwareVersion(hardwareVersion: number) {
118
- this._hardwareVersion = hardwareVersion;
121
+ set hardwareVersion(version: number) {
122
+ this.#genBasic.hwVersion = version;
119
123
  }
120
124
  get hardwareVersion(): number | undefined {
121
- return this._hardwareVersion;
125
+ return this.#genBasic.hwVersion;
122
126
  }
123
127
  get manufacturerName(): string | undefined {
124
- return this._manufacturerName;
128
+ return this.#genBasic.manufacturerName;
125
129
  }
126
- set manufacturerName(manufacturerName: string | undefined) {
127
- this._manufacturerName = manufacturerName;
130
+ set manufacturerName(name: string | undefined) {
131
+ this.#genBasic.manufacturerName = name;
128
132
  }
129
- set modelID(modelID: string) {
130
- this._modelID = modelID;
133
+ set modelID(id: string) {
134
+ this.#genBasic.modelId = id;
131
135
  }
132
136
  get modelID(): string | undefined {
133
- return this._modelID;
137
+ return this.#genBasic.modelId;
134
138
  }
135
139
  get networkAddress(): number {
136
140
  return this._networkAddress;
@@ -147,28 +151,39 @@ export class Device extends Entity<ControllerEventMap> {
147
151
  }
148
152
  }
149
153
  get powerSource(): string | undefined {
150
- return this._powerSource;
154
+ return this.#genBasic.powerSource ? Zcl.POWER_SOURCES[this.#genBasic.powerSource] : undefined;
151
155
  }
152
- set powerSource(powerSource: string) {
153
- this._powerSource = typeof powerSource === "number" ? Zcl.POWER_SOURCES[powerSource & ~(1 << 7)] : powerSource;
156
+ set powerSource(source: string | number) {
157
+ if (typeof source === "number") {
158
+ this.#genBasic.powerSource = source & ~(1 << 7);
159
+ } else {
160
+ for (const key in Zcl.POWER_SOURCES) {
161
+ const val = Zcl.POWER_SOURCES[key];
162
+
163
+ if (val === source) {
164
+ this.#genBasic.powerSource = Number(key);
165
+ break;
166
+ }
167
+ }
168
+ }
154
169
  }
155
170
  get softwareBuildID(): string | undefined {
156
- return this._softwareBuildID;
171
+ return this.#genBasic.swBuildId;
157
172
  }
158
- set softwareBuildID(softwareBuildID: string) {
159
- this._softwareBuildID = softwareBuildID;
173
+ set softwareBuildID(id: string) {
174
+ this.#genBasic.swBuildId = id;
160
175
  }
161
176
  get stackVersion(): number | undefined {
162
- return this._stackVersion;
177
+ return this.#genBasic.stackVersion;
163
178
  }
164
- set stackVersion(stackVersion: number) {
165
- this._stackVersion = stackVersion;
179
+ set stackVersion(version: number) {
180
+ this.#genBasic.stackVersion = version;
166
181
  }
167
182
  get zclVersion(): number | undefined {
168
- return this._zclVersion;
183
+ return this.#genBasic.zclVersion;
169
184
  }
170
- set zclVersion(zclVersion: number) {
171
- this._zclVersion = zclVersion;
185
+ set zclVersion(version: number) {
186
+ this.#genBasic.zclVersion = version;
172
187
  }
173
188
  get linkquality(): number | undefined {
174
189
  return this._linkquality;
@@ -208,6 +223,9 @@ export class Device extends Entity<ControllerEventMap> {
208
223
  get gpSecurityKey(): number[] | undefined {
209
224
  return this._gpSecurityKey;
210
225
  }
226
+ get genBasic(): TPartialClusterAttributes<"genBasic"> {
227
+ return this.#genBasic;
228
+ }
211
229
 
212
230
  public meta: KeyValue;
213
231
 
@@ -218,77 +236,6 @@ export class Device extends Entity<ControllerEventMap> {
218
236
  private static readonly deletedDevices: Map<string /* IEEE */, Device> = new Map();
219
237
  private static readonly nwkToIeeeCache: Map<number /* nwk addr */, string /* IEEE */> = new Map();
220
238
 
221
- public static readonly REPORTABLE_PROPERTIES_MAPPING: {
222
- [s: string]: {
223
- set: (value: string | number, device: Device) => void;
224
- key:
225
- | "modelID"
226
- | "manufacturerName"
227
- | "applicationVersion"
228
- | "zclVersion"
229
- | "powerSource"
230
- | "stackVersion"
231
- | "dateCode"
232
- | "softwareBuildID"
233
- | "hardwareVersion";
234
- };
235
- } = {
236
- modelId: {
237
- key: "modelID",
238
- set: (v: string | number, d: Device): void => {
239
- d.modelID = v as string;
240
- },
241
- },
242
- manufacturerName: {
243
- key: "manufacturerName",
244
- set: (v: string | number, d: Device): void => {
245
- d.manufacturerName = v as string;
246
- },
247
- },
248
- powerSource: {
249
- key: "powerSource",
250
- set: (v: string | number, d: Device): void => {
251
- d.powerSource = v as string;
252
- },
253
- },
254
- zclVersion: {
255
- key: "zclVersion",
256
- set: (v: string | number, d: Device): void => {
257
- d.zclVersion = v as number;
258
- },
259
- },
260
- appVersion: {
261
- key: "applicationVersion",
262
- set: (v: string | number, d: Device): void => {
263
- d.applicationVersion = v as number;
264
- },
265
- },
266
- stackVersion: {
267
- key: "stackVersion",
268
- set: (v: string | number, d: Device): void => {
269
- d.stackVersion = v as number;
270
- },
271
- },
272
- hwVersion: {
273
- key: "hardwareVersion",
274
- set: (v: string | number, d: Device): void => {
275
- d.hardwareVersion = v as number;
276
- },
277
- },
278
- dateCode: {
279
- key: "dateCode",
280
- set: (v: string | number, d: Device): void => {
281
- d.dateCode = v as string;
282
- },
283
- },
284
- swBuildId: {
285
- key: "softwareBuildID",
286
- set: (v: string | number, d: Device): void => {
287
- d.softwareBuildID = v as string;
288
- },
289
- },
290
- };
291
-
292
239
  private constructor(
293
240
  id: number,
294
241
  type: DeviceType,
@@ -319,15 +266,15 @@ export class Device extends Entity<ControllerEventMap> {
319
266
  this._networkAddress = networkAddress;
320
267
  this._manufacturerID = manufacturerID;
321
268
  this._endpoints = endpoints;
322
- this._manufacturerName = manufacturerName;
323
- this._powerSource = powerSource;
324
- this._modelID = modelID;
325
- this._applicationVersion = applicationVersion;
326
- this._stackVersion = stackVersion;
327
- this._zclVersion = zclVersion;
328
- this._hardwareVersion = hardwareVersion;
329
- this._dateCode = dateCode;
330
- this._softwareBuildID = softwareBuildID;
269
+ this.#genBasic.manufacturerName = manufacturerName;
270
+ this.powerSource = powerSource ?? Zcl.PowerSource.Unknown;
271
+ this.#genBasic.modelId = modelID;
272
+ this.#genBasic.appVersion = applicationVersion;
273
+ this.#genBasic.stackVersion = stackVersion;
274
+ this.#genBasic.zclVersion = zclVersion;
275
+ this.#genBasic.hwVersion = hardwareVersion;
276
+ this.#genBasic.dateCode = dateCode;
277
+ this.#genBasic.swBuildId = softwareBuildID;
331
278
  this._interviewState = interviewState;
332
279
  this._skipDefaultResponse = false;
333
280
  this.meta = meta;
@@ -370,6 +317,10 @@ export class Device extends Entity<ControllerEventMap> {
370
317
  return this.endpoints.find((d): boolean => d.deviceID === deviceID);
371
318
  }
372
319
 
320
+ public updateGenBasic(data: TPartialClusterAttributes<"genBasic">): void {
321
+ Object.assign(this.#genBasic, data);
322
+ }
323
+
373
324
  public implicitCheckin(): void {
374
325
  // No need to do anythign in `catch` as `endpoint.sendRequest` already logs failures.
375
326
  Promise.allSettled(this.endpoints.map((e) => e.sendPendingRequests(false))).catch(() => {});
@@ -439,12 +390,16 @@ export class Device extends Entity<ControllerEventMap> {
439
390
  if (frame.header.isSpecific && frame.isCluster("genPollCtrl") && frame.isCommand("checkin")) {
440
391
  try {
441
392
  if (this.hasPendingRequests() || this._checkinInterval === undefined) {
442
- const payload = {
443
- startFastPolling: true,
444
- fastPollTimeout: 0,
445
- };
446
393
  logger.debug(`check-in from ${this.ieeeAddr}: accepting fast-poll`, NS);
447
- await endpoint.command(frame.cluster.ID, "checkinRsp", payload, {sendPolicy: "immediate"});
394
+ await endpoint.command(
395
+ frame.cluster.name as "genPollCtrl",
396
+ "checkinRsp",
397
+ {
398
+ startFastPolling: 1,
399
+ fastPollTimeout: 0,
400
+ },
401
+ {sendPolicy: "immediate"},
402
+ );
448
403
 
449
404
  // This is a good time to read the checkin interval if we haven't stored it previously
450
405
  if (this._checkinInterval === undefined) {
@@ -458,14 +413,18 @@ export class Device extends Entity<ControllerEventMap> {
458
413
  // We *must* end fast-poll when we're done sending things. Otherwise
459
414
  // we cause undue power-drain.
460
415
  logger.debug(`check-in from ${this.ieeeAddr}: stopping fast-poll`, NS);
461
- await endpoint.command(frame.cluster.ID, "fastPollStop", {}, {sendPolicy: "immediate"});
416
+ await endpoint.command(frame.cluster.name as "genPollCtrl", "fastPollStop", {}, {sendPolicy: "immediate"});
462
417
  } else {
463
- const payload = {
464
- startFastPolling: false,
465
- fastPollTimeout: 0,
466
- };
467
418
  logger.debug(`check-in from ${this.ieeeAddr}: declining fast-poll`, NS);
468
- await endpoint.command(frame.cluster.ID, "checkinRsp", payload, {sendPolicy: "immediate"});
419
+ await endpoint.command(
420
+ frame.cluster.name as "genPollCtrl",
421
+ "checkinRsp",
422
+ {
423
+ startFastPolling: 0,
424
+ fastPollTimeout: 0,
425
+ },
426
+ {sendPolicy: "immediate"},
427
+ );
469
428
  }
470
429
  } catch (error) {
471
430
  logger.error(`Handling of poll check-in from ${this.ieeeAddr} failed (${(error as Error).message})`, NS);
@@ -794,7 +753,7 @@ export class Device extends Entity<ControllerEventMap> {
794
753
  this.manufacturerName === "HOBEIAN" ||
795
754
  (this.modelID?.match("^TS\\d*$") && (this.manufacturerName?.match("^_TZ.*_.*$") || this.manufacturerName?.match("^_TYZB01_.*$")))
796
755
  ) {
797
- this._powerSource = this._powerSource ?? "Battery";
756
+ this.#genBasic.powerSource = this.#genBasic.powerSource || Zcl.PowerSource.Battery;
798
757
  logger.debug("Interview - quirks matched for Tuya end device", NS);
799
758
  return true;
800
759
  }
@@ -807,24 +766,24 @@ export class Device extends Entity<ControllerEventMap> {
807
766
  type?: DeviceType;
808
767
  manufacturerID?: number;
809
768
  manufacturerName?: string;
810
- powerSource?: string;
769
+ powerSource?: Zcl.PowerSource;
811
770
  };
812
771
  } = {
813
772
  "^3R.*?Z": {
814
773
  type: "EndDevice",
815
- powerSource: "Battery",
774
+ powerSource: Zcl.PowerSource.Battery,
816
775
  },
817
776
  "lumi..*": {
818
777
  type: "EndDevice",
819
778
  manufacturerID: 4151,
820
779
  manufacturerName: "LUMI",
821
- powerSource: "Battery",
780
+ powerSource: Zcl.PowerSource.Battery,
822
781
  },
823
782
  "TERNCY-PP01": {
824
783
  type: "EndDevice",
825
784
  manufacturerID: 4648,
826
785
  manufacturerName: "TERNCY",
827
- powerSource: "Battery",
786
+ powerSource: Zcl.PowerSource.Battery,
828
787
  },
829
788
  "3RWS18BZ": {}, // https://github.com/Koenkk/zigbee-herdsman-converters/pull/2710
830
789
  "MULTI-MECI--EA01": {},
@@ -847,8 +806,8 @@ export class Device extends Entity<ControllerEventMap> {
847
806
  logger.debug(`Interview procedure failed but got modelID matching '${match}', assuming interview succeeded`, NS);
848
807
  this._type = this._type === "Unknown" && info.type ? info.type : this._type;
849
808
  this._manufacturerID = this._manufacturerID || info.manufacturerID;
850
- this._manufacturerName = this._manufacturerName || info.manufacturerName;
851
- this._powerSource = this._powerSource || info.powerSource;
809
+ this.#genBasic.manufacturerName = this.#genBasic.manufacturerName || info.manufacturerName;
810
+ this.#genBasic.powerSource = (this.#genBasic.powerSource || info.powerSource) /* v8 ignore next */ ?? Zcl.PowerSource.Unknown;
852
811
  logger.debug(`Interview - quirks matched on '${match}'`, NS);
853
812
  return true;
854
813
  }
@@ -898,9 +857,7 @@ export class Device extends Entity<ControllerEventMap> {
898
857
  const endpoint = Endpoint.create(1, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
899
858
  const result = await endpoint.read("genBasic", ["modelId", "manufacturerName"], {sendPolicy: "immediate"});
900
859
 
901
- for (const key in result) {
902
- Device.REPORTABLE_PROPERTIES_MAPPING[key].set(result[key], this);
903
- }
860
+ this.updateGenBasic(result);
904
861
  } catch (error) {
905
862
  logger.debug(`Interview - Tuya read modelID and manufacturerName failed (${error})`, NS);
906
863
  }
@@ -935,12 +892,10 @@ export class Device extends Entity<ControllerEventMap> {
935
892
  // Read attributes
936
893
  // nice to have but not required for successful pairing as most of the attributes are not mandatory in ZCL specification
937
894
  if (endpoint.supportsInputCluster("genBasic")) {
938
- for (const key in Device.REPORTABLE_PROPERTIES_MAPPING) {
939
- const item = Device.REPORTABLE_PROPERTIES_MAPPING[key];
940
-
941
- if (ignoreCache || !this[item.key]) {
895
+ for (const key of INTERVIEW_GENBASIC_ATTRIBUTES) {
896
+ if (ignoreCache || !this.#genBasic[key]) {
942
897
  try {
943
- let result: KeyValue;
898
+ let result: TPartialClusterAttributes<"genBasic">;
944
899
 
945
900
  try {
946
901
  result = await endpoint.read("genBasic", [key], {sendPolicy: "immediate"});
@@ -949,8 +904,8 @@ export class Device extends Entity<ControllerEventMap> {
949
904
  // while joining like in:
950
905
  // https://github.com/Koenkk/zigbee-herdsman-converters/issues/2485.
951
906
  // The modelID and manufacturerName are crucial for device identification, so retry.
952
- if (item.key === "modelID" || item.key === "manufacturerName") {
953
- logger.debug(`Interview - first ${item.key} retrieval attempt failed, retrying after 10 seconds...`, NS);
907
+ if (key === "modelId" || key === "manufacturerName") {
908
+ logger.debug(`Interview - first ${key} retrieval attempt failed, retrying after 10 seconds...`, NS);
954
909
  await wait(10000);
955
910
  result = await endpoint.read("genBasic", [key], {sendPolicy: "immediate"});
956
911
  } else {
@@ -958,10 +913,10 @@ export class Device extends Entity<ControllerEventMap> {
958
913
  }
959
914
  }
960
915
 
961
- item.set(result[key], this);
962
- logger.debug(`Interview - got '${item.key}' for device '${this.ieeeAddr}'`, NS);
916
+ this.updateGenBasic(result);
917
+ logger.debug(`Interview - got '${key}' for device '${this.ieeeAddr}'`, NS);
963
918
  } catch (error) {
964
- logger.debug(`Interview - failed to read attribute '${item.key}' from endpoint '${endpoint.ID}' (${error})`, NS);
919
+ logger.debug(`Interview - failed to read attribute '${key}' from endpoint '${endpoint.ID}' (${error})`, NS);
965
920
  }
966
921
  }
967
922
  }