zigbee-herdsman 6.0.0 → 6.0.2

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 (96) hide show
  1. package/.github/workflows/ci.yml +10 -5
  2. package/.github/workflows/typedoc.yaml +2 -2
  3. package/.release-please-manifest.json +1 -1
  4. package/CHANGELOG.md +19 -0
  5. package/biome.json +1 -1
  6. package/dist/adapter/ezsp/driver/uart.js +1 -1
  7. package/dist/adapter/ezsp/driver/uart.js.map +1 -1
  8. package/dist/adapter/z-stack/adapter/zStackAdapter.js +4 -4
  9. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  10. package/dist/adapter/zigate/adapter/zigateAdapter.js +4 -4
  11. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  12. package/dist/controller/controller.d.ts.map +1 -1
  13. package/dist/controller/controller.js +7 -10
  14. package/dist/controller/controller.js.map +1 -1
  15. package/dist/controller/events.d.ts +1 -1
  16. package/dist/controller/events.d.ts.map +1 -1
  17. package/dist/controller/helpers/request.d.ts.map +1 -1
  18. package/dist/controller/helpers/request.js +2 -1
  19. package/dist/controller/helpers/request.js.map +1 -1
  20. package/dist/controller/helpers/zclFrameConverter.d.ts +2 -4
  21. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  22. package/dist/controller/helpers/zclFrameConverter.js +2 -0
  23. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  24. package/dist/controller/model/device.d.ts +13 -24
  25. package/dist/controller/model/device.d.ts.map +1 -1
  26. package/dist/controller/model/device.js +89 -129
  27. package/dist/controller/model/device.js.map +1 -1
  28. package/dist/controller/model/endpoint.d.ts +17 -16
  29. package/dist/controller/model/endpoint.d.ts.map +1 -1
  30. package/dist/controller/model/endpoint.js +31 -16
  31. package/dist/controller/model/endpoint.js.map +1 -1
  32. package/dist/controller/model/group.d.ts +6 -6
  33. package/dist/controller/model/group.d.ts.map +1 -1
  34. package/dist/controller/model/group.js +5 -3
  35. package/dist/controller/model/group.js.map +1 -1
  36. package/dist/controller/model/index.d.ts +1 -0
  37. package/dist/controller/model/index.d.ts.map +1 -1
  38. package/dist/controller/model/index.js +3 -1
  39. package/dist/controller/model/index.js.map +1 -1
  40. package/dist/controller/model/zigbeeEntity.d.ts +8 -0
  41. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  42. package/dist/controller/model/zigbeeEntity.js +11 -0
  43. package/dist/controller/model/zigbeeEntity.js.map +1 -0
  44. package/dist/controller/tstype.d.ts +22 -0
  45. package/dist/controller/tstype.d.ts.map +1 -1
  46. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  47. package/dist/zspec/zcl/definition/cluster.js +2 -9
  48. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  49. package/dist/zspec/zcl/definition/clusters-typegen.js +61 -13
  50. package/dist/zspec/zcl/definition/clusters-typegen.js.map +1 -1
  51. package/dist/zspec/zcl/definition/clusters-types.d.ts +173 -140
  52. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -1
  53. package/dist/zspec/zcl/definition/enums.d.ts +10 -0
  54. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  55. package/dist/zspec/zcl/definition/enums.js +12 -1
  56. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  57. package/dist/zspec/zcl/definition/tstype.d.ts +1 -1
  58. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  59. package/dist/zspec/zcl/index.d.ts +1 -0
  60. package/dist/zspec/zcl/index.d.ts.map +1 -1
  61. package/dist/zspec/zcl/index.js.map +1 -1
  62. package/dist/zspec/zcl/utils.d.ts +1 -1
  63. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  64. package/dist/zspec/zcl/utils.js +1 -1
  65. package/dist/zspec/zcl/utils.js.map +1 -1
  66. package/package.json +3 -4
  67. package/src/adapter/ezsp/driver/driver.ts +1 -1
  68. package/src/adapter/ezsp/driver/uart.ts +1 -1
  69. package/src/adapter/z-stack/adapter/zStackAdapter.ts +4 -4
  70. package/src/adapter/zigate/adapter/zigateAdapter.ts +4 -4
  71. package/src/controller/controller.ts +11 -15
  72. package/src/controller/events.ts +1 -1
  73. package/src/controller/helpers/request.ts +3 -1
  74. package/src/controller/helpers/zclFrameConverter.ts +13 -17
  75. package/src/controller/model/device.ts +104 -148
  76. package/src/controller/model/endpoint.ts +112 -64
  77. package/src/controller/model/group.ts +33 -9
  78. package/src/controller/model/index.ts +1 -0
  79. package/src/controller/model/zigbeeEntity.ts +30 -0
  80. package/src/controller/tstype.ts +234 -0
  81. package/src/zspec/zcl/definition/cluster.ts +2 -9
  82. package/src/zspec/zcl/definition/clusters-typegen.ts +96 -19
  83. package/src/zspec/zcl/definition/clusters-types.ts +195 -146
  84. package/src/zspec/zcl/definition/enums.ts +11 -0
  85. package/src/zspec/zcl/definition/tstype.ts +0 -1
  86. package/src/zspec/zcl/index.ts +1 -0
  87. package/src/zspec/zcl/utils.ts +1 -1
  88. package/test/adapter/ezsp/uart.test.ts +2 -2
  89. package/test/adapter/z-stack/znp.test.ts +4 -4
  90. package/test/benchOptions.ts +14 -0
  91. package/test/controller.bench.ts +96 -75
  92. package/test/controller.test.ts +296 -98
  93. package/test/requests.bench.ts +37 -14
  94. package/test/vitest.config.mts +1 -2
  95. package/test/zcl.test.ts +1 -1
  96. 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,30 +766,31 @@ 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": {},
831
790
  MOT003: {}, // https://github.com/Koenkk/zigbee2mqtt/issues/12471
832
791
  "C-ZB-SEDC": {}, //candeo device that doesn't follow IAS enrollment process correctly and therefore fails to complete interview
833
792
  "C-ZB-SEMO": {}, //candeo device that doesn't follow IAS enrollment process correctly and therefore fails to complete interview
793
+ "CS-T9C-A0-BG": {}, // iAS enroll fails: https://github.com/Koenkk/zigbee2mqtt/issues/27822
834
794
  };
835
795
 
836
796
  let match: string | undefined;
@@ -847,8 +807,8 @@ export class Device extends Entity<ControllerEventMap> {
847
807
  logger.debug(`Interview procedure failed but got modelID matching '${match}', assuming interview succeeded`, NS);
848
808
  this._type = this._type === "Unknown" && info.type ? info.type : this._type;
849
809
  this._manufacturerID = this._manufacturerID || info.manufacturerID;
850
- this._manufacturerName = this._manufacturerName || info.manufacturerName;
851
- this._powerSource = this._powerSource || info.powerSource;
810
+ this.#genBasic.manufacturerName = this.#genBasic.manufacturerName || info.manufacturerName;
811
+ this.#genBasic.powerSource = (this.#genBasic.powerSource || info.powerSource) /* v8 ignore next */ ?? Zcl.PowerSource.Unknown;
852
812
  logger.debug(`Interview - quirks matched on '${match}'`, NS);
853
813
  return true;
854
814
  }
@@ -898,9 +858,7 @@ export class Device extends Entity<ControllerEventMap> {
898
858
  const endpoint = Endpoint.create(1, undefined, undefined, [], [], this.networkAddress, this.ieeeAddr);
899
859
  const result = await endpoint.read("genBasic", ["modelId", "manufacturerName"], {sendPolicy: "immediate"});
900
860
 
901
- for (const key in result) {
902
- Device.REPORTABLE_PROPERTIES_MAPPING[key].set(result[key], this);
903
- }
861
+ this.updateGenBasic(result);
904
862
  } catch (error) {
905
863
  logger.debug(`Interview - Tuya read modelID and manufacturerName failed (${error})`, NS);
906
864
  }
@@ -935,12 +893,10 @@ export class Device extends Entity<ControllerEventMap> {
935
893
  // Read attributes
936
894
  // nice to have but not required for successful pairing as most of the attributes are not mandatory in ZCL specification
937
895
  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]) {
896
+ for (const key of INTERVIEW_GENBASIC_ATTRIBUTES) {
897
+ if (ignoreCache || !this.#genBasic[key]) {
942
898
  try {
943
- let result: KeyValue;
899
+ let result: TPartialClusterAttributes<"genBasic">;
944
900
 
945
901
  try {
946
902
  result = await endpoint.read("genBasic", [key], {sendPolicy: "immediate"});
@@ -949,8 +905,8 @@ export class Device extends Entity<ControllerEventMap> {
949
905
  // while joining like in:
950
906
  // https://github.com/Koenkk/zigbee-herdsman-converters/issues/2485.
951
907
  // 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);
908
+ if (key === "modelId" || key === "manufacturerName") {
909
+ logger.debug(`Interview - first ${key} retrieval attempt failed, retrying after 10 seconds...`, NS);
954
910
  await wait(10000);
955
911
  result = await endpoint.read("genBasic", [key], {sendPolicy: "immediate"});
956
912
  } else {
@@ -958,10 +914,10 @@ export class Device extends Entity<ControllerEventMap> {
958
914
  }
959
915
  }
960
916
 
961
- item.set(result[key], this);
962
- logger.debug(`Interview - got '${item.key}' for device '${this.ieeeAddr}'`, NS);
917
+ this.updateGenBasic(result);
918
+ logger.debug(`Interview - got '${key}' for device '${this.ieeeAddr}'`, NS);
963
919
  } catch (error) {
964
- logger.debug(`Interview - failed to read attribute '${item.key}' from endpoint '${endpoint.ID}' (${error})`, NS);
920
+ logger.debug(`Interview - failed to read attribute '${key}' from endpoint '${endpoint.ID}' (${error})`, NS);
965
921
  }
966
922
  }
967
923
  }