Skip to content

Commit 5c1f2d5

Browse files
committed
fix: Fix configure failing when configuring too many attributes at once #8129
1 parent d28465b commit 5c1f2d5

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

src/lib/modernExtend.ts

+28-18
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
noOccupancySince,
4949
postfixWithEndpointName,
5050
precisionRound,
51+
splitArrayIntoChunks,
5152
} from './utils';
5253

5354
function getEndpointsWithCluster(device: Zh.Device, cluster: string | number, type: 'input' | 'output') {
@@ -107,28 +108,37 @@ export async function setupAttributes(
107108
`Configure reporting: ${configureReporting}, read: ${read} for ${ieeeAddr}/${endpoint.ID} ${cluster} ${JSON.stringify(config)}`,
108109
'zhc:setupattribute',
109110
);
111+
112+
// Split into chunks of 4 to prevent to message becoming too big.
113+
const chunks = splitArrayIntoChunks(config, 4);
114+
110115
if (configureReporting) {
111116
await endpoint.bind(cluster, coordinatorEndpoint);
112-
await endpoint.configureReporting(
113-
cluster,
114-
config.map((a) => ({
115-
minimumReportInterval: convertReportingConfigTime(a.min),
116-
maximumReportInterval: convertReportingConfigTime(a.max),
117-
reportableChange: a.change,
118-
attribute: a.attribute,
119-
})),
120-
);
121-
}
122-
if (read) {
123-
try {
124-
// Don't fail configuration if reading this attribute fails
125-
// https://github.com/Koenkk/zigbee-herdsman-converters/pull/7074
126-
await endpoint.read(
117+
for (const chunk of chunks) {
118+
await endpoint.configureReporting(
127119
cluster,
128-
config.map((a) => (isString(a) ? a : isObject(a.attribute) ? a.attribute.ID : a.attribute)),
120+
chunk.map((a) => ({
121+
minimumReportInterval: convertReportingConfigTime(a.min),
122+
maximumReportInterval: convertReportingConfigTime(a.max),
123+
reportableChange: a.change,
124+
attribute: a.attribute,
125+
})),
129126
);
130-
} catch (e) {
131-
logger.debug(`Reading attribute failed: ${e}`, 'zhc:setupattribute');
127+
}
128+
}
129+
130+
if (read) {
131+
for (const chunk of chunks) {
132+
try {
133+
// Don't fail configuration if reading this attribute fails
134+
// https://github.com/Koenkk/zigbee-herdsman-converters/pull/7074
135+
await endpoint.read(
136+
cluster,
137+
chunk.map((a) => (isString(a) ? a : isObject(a.attribute) ? a.attribute.ID : a.attribute)),
138+
);
139+
} catch (e) {
140+
logger.debug(`Reading attribute failed: ${e}`, 'zhc:setupattribute');
141+
}
132142
}
133143
}
134144
}

src/lib/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,17 @@ export function isLightExpose(expose: Expose): expose is Light {
726726
return expose?.type === 'light';
727727
}
728728

729+
export function splitArrayIntoChunks<T>(arr: T[], chunkSize: number): T[][] {
730+
const result = [];
731+
732+
for (let i = 0; i < arr.length; i += chunkSize) {
733+
const chunk = arr.slice(i, i + chunkSize);
734+
result.push(chunk);
735+
}
736+
737+
return result;
738+
}
739+
729740
exports.noOccupancySince = noOccupancySince;
730741
exports.getOptions = getOptions;
731742
exports.isLegacyEnabled = isLegacyEnabled;

test/modernExtend.test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fz from '../src/converters/fromZigbee';
22
import {repInterval} from '../src/lib/constants';
33
import {fromZigbee as lumiFz} from '../src/lib/lumi';
4+
import {setupAttributes} from '../src/lib/modernExtend';
45
import {philipsFz} from '../src/lib/philips';
56
import {assertDefintion, mockDevice, reportingItem} from './utils';
67

@@ -429,4 +430,41 @@ describe('ModernExtend', () => {
429430
},
430431
});
431432
});
433+
434+
test('Setup attributes', async () => {
435+
const device = mockDevice({modelID: '', endpoints: [{inputClusters: ['haElectricalMeasurement']}]});
436+
const deviceEp = device.endpoints[0];
437+
const coordinator = mockDevice({modelID: '', endpoints: [{}]});
438+
const coordinatorEp = coordinator.endpoints[0];
439+
const config = {min: 0, max: 10, change: 1};
440+
const expectedConfig = {maximumReportInterval: 10, minimumReportInterval: 0, reportableChange: 1};
441+
442+
await setupAttributes(deviceEp, coordinator.endpoints[0], 'haElectricalMeasurement', [
443+
{attribute: '1', ...config},
444+
{attribute: '2', ...config},
445+
{attribute: '3', ...config},
446+
{attribute: '4', ...config},
447+
{attribute: '5', ...config},
448+
{attribute: '6', ...config},
449+
]);
450+
451+
expect(deviceEp.bind).toHaveBeenCalledTimes(1);
452+
expect(deviceEp.bind).toHaveBeenCalledWith('haElectricalMeasurement', coordinatorEp);
453+
454+
expect(deviceEp.configureReporting).toHaveBeenCalledTimes(2);
455+
expect(deviceEp.configureReporting).toHaveBeenNthCalledWith(1, 'haElectricalMeasurement', [
456+
{attribute: '1', ...expectedConfig},
457+
{attribute: '2', ...expectedConfig},
458+
{attribute: '3', ...expectedConfig},
459+
{attribute: '4', ...expectedConfig},
460+
]);
461+
expect(deviceEp.configureReporting).toHaveBeenNthCalledWith(2, 'haElectricalMeasurement', [
462+
{attribute: '5', ...expectedConfig},
463+
{attribute: '6', ...expectedConfig},
464+
]);
465+
466+
expect(deviceEp.read).toHaveBeenCalledTimes(2);
467+
expect(deviceEp.read).toHaveBeenNthCalledWith(1, 'haElectricalMeasurement', ['1', '2', '3', '4']);
468+
expect(deviceEp.read).toHaveBeenNthCalledWith(2, 'haElectricalMeasurement', ['5', '6']);
469+
});
432470
});

0 commit comments

Comments
 (0)