Skip to content

Commit 4041baa

Browse files
authored
fix!: Refactor onEvent (#9650)
1 parent 29fdc2e commit 4041baa

70 files changed

Lines changed: 1130 additions & 1941 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ If any of those commands finish with an error your PR won't pass the tests and w
4141

4242
25.0.0
4343

44+
- Changed the `onEvent` api, see https://github.com/Koenkk/zigbee-herdsman-converters/pull/9650 for more info.
4445
- A `device` argument has been added to `postProcessConvertedFromZigbeeMessage` (https://github.com/Koenkk/zigbee-herdsman-converters/pull/9693)
4546

4647
24.0.0

src/converters/fromZigbee.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,20 @@ export const lock_programming_event: Fz.Converter = {
297297
};
298298
},
299299
};
300+
export const lock_programming_event_read_pincode: Fz.Converter = {
301+
cluster: "closuresDoorLock",
302+
type: "commandProgrammingEventNotification",
303+
convert: (model, msg, publish, options, meta) => {
304+
if (
305+
msg.data.userid !== undefined &&
306+
(msg.data.programeventsrc === undefined || constants.lockSourceName[msg.data.programeventsrc] !== "rf")
307+
) {
308+
msg.endpoint
309+
.command("closuresDoorLock", "getPinCode", {userid: msg.data.userid}, {})
310+
.catch((error) => logger.error(`Failed to read pincode of '${msg.device.ieeeAddr}' (${error})`, NS));
311+
}
312+
},
313+
};
300314
export const lock: Fz.Converter = {
301315
cluster: "closuresDoorLock",
302316
type: ["attributeReport", "readResponse"],

src/devices/adeo.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,7 @@ export const definitions: DefinitionWithExtend[] = [
6666
fromZigbee: [fz.command_arm, fz.command_panic],
6767
toZigbee: [],
6868
exposes: [e.action(["panic", "disarm", "arm_partial_zones", "arm_all_zones"])],
69-
onEvent: async (type, data, device) => {
70-
// Since arm command has a response zigbee-herdsman doesn't send a default response.
71-
// This causes the remote to repeat the arm command, so send a default response here.
72-
if (data.type === "commandArm" && data.cluster === "ssIasAce") {
73-
await data.endpoint.defaultResponse(0, 0, 1281, data.meta.zclTransactionSequenceNumber);
74-
}
75-
},
69+
extend: [m.iasArmCommandDefaultResponse()],
7670
},
7771
{
7872
zigbeeModel: ["ZBEK-1"],

src/devices/amina.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import {Zcl} from "zigbee-herdsman";
22

33
import * as constants from "../lib/constants";
44
import * as exposes from "../lib/exposes";
5+
import {logger} from "../lib/logger";
56
import * as m from "../lib/modernExtend";
67
import * as reporting from "../lib/reporting";
78
import type {DefinitionWithExtend, Fz, KeyValue, Tz} from "../lib/types";
89
import * as utils from "../lib/utils";
910

11+
const NS = "zhc:amina";
12+
1013
const e = exposes.presets;
1114
const ea = exposes.access;
1215

@@ -46,12 +49,33 @@ const aminaAlarms = [
4649
];
4750

4851
const fzLocal = {
52+
poll_energy: {
53+
cluster: "haElectricalMeasurement",
54+
type: ["attributeReport"],
55+
convert: (model, msg, publish, options, meta) => {
56+
if (msg.data.totalActivePower != null) {
57+
// Device does not support reporting of energy attributes, so we poll them manually when power is updated
58+
msg.endpoint.read("aminaControlCluster", ["totalActiveEnergy"]).catch((error) => {
59+
logger.error(`Failed to poll energy of '${msg.device.ieeeAddr}' (${error})`, NS);
60+
});
61+
}
62+
},
63+
} satisfies Fz.Converter,
4964
ev_status: {
5065
cluster: "aminaControlCluster",
5166
type: ["attributeReport", "readResponse"],
5267
convert: (model, msg, publish, options, meta) => {
5368
const result: KeyValue = {};
5469

70+
if (msg.type === "attributeReport") {
71+
// Device does not support reporting of energy attributes, so we poll them manually when charging is stopped
72+
if ((msg.data.evStatus & (1 << 2)) === 0) {
73+
msg.endpoint.read("aminaControlCluster", ["totalActiveEnergy", "lastSessionEnergy"]).catch((error) => {
74+
logger.error(`Failed to poll energy of '${msg.device.ieeeAddr}' (${error})`, NS);
75+
});
76+
}
77+
}
78+
5579
if (msg.data.evStatus !== undefined) {
5680
let statusText = "Not Connected";
5781
const evStatus = msg.data.evStatus;
@@ -73,7 +97,6 @@ const fzLocal = {
7397
}
7498
},
7599
} satisfies Fz.Converter,
76-
77100
alarms: {
78101
cluster: "aminaControlCluster",
79102
type: ["attributeReport", "readResponse"],
@@ -106,14 +129,12 @@ const tzLocal = {
106129
await entity.read("genLevelCtrl", ["currentLevel"]);
107130
},
108131
} satisfies Tz.Converter,
109-
110132
ev_status: {
111133
key: ["ev_status"],
112134
convertGet: async (entity, key, meta) => {
113135
await entity.read("aminaControlCluster", ["evStatus"], manufacturerOptions);
114136
},
115137
} satisfies Tz.Converter,
116-
117138
alarms: {
118139
key: ["alarms"],
119140
convertGet: async (entity, key, meta) => {
@@ -308,11 +329,9 @@ export const definitions: DefinitionWithExtend[] = [
308329
entityCategory: "config",
309330
}),
310331
],
311-
312332
endpoint: (device) => {
313333
return {default: 10};
314334
},
315-
316335
configure: async (device, coordinatorEndpoint) => {
317336
const endpoint = device.getEndpoint(10);
318337

@@ -350,19 +369,5 @@ export const definitions: DefinitionWithExtend[] = [
350369
"lastSessionEnergy",
351370
]);
352371
},
353-
354-
onEvent: async (type, data, device) => {
355-
if (type === "message" && data.type === "attributeReport" && data.cluster === "haElectricalMeasurement" && data.data.totalActivePower) {
356-
// Device does not support reporting of energy attributes, so we poll them manually when power is updated
357-
await data.endpoint.read("aminaControlCluster", ["totalActiveEnergy"]);
358-
}
359-
360-
if (type === "message" && data.type === "attributeReport" && data.cluster === "aminaControlCluster" && data.data.evStatus) {
361-
// Device does not support reporting of energy attributes, so we poll them manually when charging is stopped
362-
if ((data.data.evStatus & (1 << 2)) === 0) {
363-
await data.endpoint.read("aminaControlCluster", ["totalActiveEnergy", "lastSessionEnergy"]);
364-
}
365-
}
366-
},
367372
},
368373
];

src/devices/aurora_lighting.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ const batteryRotaryDimmer = (...endpointsIds: number[]) => ({
6262
await disableBatteryRotaryDimmerReporting(endpoint);
6363
}
6464
}) satisfies Configure,
65-
onEvent: (async (type, data, device) => {
65+
onEvent: (async (event) => {
6666
// The rotary dimmer devices appear to lose the configured reportings when they
6767
// re-announce themselves which they do roughly every 6 hours.
68-
if (type === "deviceAnnounce") {
69-
for (const endpoint of device.endpoints) {
68+
if (event.type === "deviceAnnounce") {
69+
for (const endpoint of event.data.device.endpoints) {
7070
// First disable the default reportings (for the dimmer endpoints only)
7171
if ([1, 2].includes(endpoint.ID)) {
7272
await disableBatteryRotaryDimmerReporting(endpoint);
@@ -84,7 +84,7 @@ const batteryRotaryDimmer = (...endpointsIds: number[]) => ({
8484
}
8585
}
8686
}
87-
}) satisfies OnEvent,
87+
}) satisfies OnEvent.Handler,
8888
});
8989

9090
export const definitions: DefinitionWithExtend[] = [

src/devices/avatto.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,7 @@ export const definitions: DefinitionWithExtend[] = [
1313
model: "ZWSH16",
1414
vendor: "AVATTO",
1515
description: "Smart Temperature and Humidity Detector",
16-
fromZigbee: [tuya.fz.datapoints],
17-
toZigbee: [tuya.tz.datapoints],
18-
onEvent: tuya.onEventSetTime,
19-
configure: async (device, coordinatorEndpoint) => {
20-
const endpoint = device.getEndpoint(1);
21-
await tuya.configureMagicPacket(device, coordinatorEndpoint);
22-
await endpoint.command("manuSpecificTuya", "mcuVersionRequest", {seq: 0x0002});
23-
},
16+
extend: [tuya.modernExtend.tuyaBase({dp: true, timeStart: "2000", mcuVersionRequestOnConfigure: true})],
2417
exposes: [e.battery(), e.temperature(), e.humidity(), tuya.exposes.temperatureUnit(), tuya.exposes.batteryState()],
2518
meta: {
2619
tuyaDatapoints: [
@@ -37,11 +30,8 @@ export const definitions: DefinitionWithExtend[] = [
3730
model: "ME168_AVATTO",
3831
vendor: "AVATTO",
3932
description: "Thermostatic radiator valve",
40-
fromZigbee: [tuya.fz.datapoints],
41-
toZigbee: [tuya.tz.datapoints],
33+
extend: [tuya.modernExtend.tuyaBase({dp: true, timeStart: "2000"})],
4234
whiteLabel: [tuya.whitelabel("Girier", "ME168_Girier", "Thermostatic radiator valve", ["_TZE200_cxakecfo", "_TZE200_4aijvczq"])],
43-
onEvent: tuya.onEventSetTime,
44-
configure: tuya.configureMagicPacket,
4535
ota: true,
4636
exposes: [
4737
e.battery(),

src/devices/busch_jaeger.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -119,29 +119,28 @@ export const definitions: DefinitionWithExtend[] = [
119119
fz.command_stop,
120120
fz.command_recall,
121121
],
122-
options: [
123-
e
124-
.numeric("state_poll_interval", ea.SET)
125-
.withValueMin(-1)
126-
.withDescription(
127-
"This device does not support state reporting so it is polled instead. The default poll interval is 60 seconds, set to -1 to disable.",
128-
),
129-
],
130122
toZigbee: [tz.light_onoff_brightness, tz.light_brightness_step, tz.light_brightness_move],
131-
onEvent: (type, data, device, options) => {
132-
const switchEndpoint = device.getEndpoint(0x12);
133-
if (switchEndpoint == null) {
134-
return;
135-
}
136-
// This device doesn't support reporting.
137-
// Therefore we read the on/off state every 60 seconds.
138-
// This is the same way as the Hue bridge does it.
139-
const poll = async () => {
140-
await switchEndpoint.read("genOnOff", ["onOff"]);
141-
await switchEndpoint.read("genLevelCtrl", ["currentLevel"]);
142-
};
143-
144-
utils.onEventPoll(type, data, device, options, "state", 60, poll);
145-
},
123+
extend: [
124+
// This device doesn't support reporting. Therefore we read the on/off state every 60 seconds.
125+
// This is the same was as the Hue bridge does it.
126+
m.poll({
127+
key: "state",
128+
option: e
129+
.numeric("state_poll_interval", ea.SET)
130+
.withValueMin(-1)
131+
.withDescription(
132+
"This device does not support state reporting so it is polled instead. The default poll interval is 60 seconds, set to -1 to disable.",
133+
),
134+
defaultIntervalSeconds: 60,
135+
poll: async (device) => {
136+
const switchEndpoint = device.getEndpoint(0x12);
137+
if (switchEndpoint == null) {
138+
return;
139+
}
140+
await switchEndpoint.read("genOnOff", ["onOff"]);
141+
await switchEndpoint.read("genLevelCtrl", ["currentLevel"]);
142+
},
143+
}),
144+
],
146145
},
147146
];

src/devices/centralite.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as constants from "../lib/constants";
66
import * as exposes from "../lib/exposes";
77
import * as m from "../lib/modernExtend";
88
import * as reporting from "../lib/reporting";
9-
import * as globalStore from "../lib/store";
109
import type {DefinitionWithExtend, Fz} from "../lib/types";
1110

1211
const e = exposes.presets;
@@ -201,22 +200,7 @@ export const definitions: DefinitionWithExtend[] = [
201200
await reporting.temperature(endpoint);
202201
await reporting.batteryVoltage(endpoint);
203202
},
204-
onEvent: async (type, data, device) => {
205-
if (
206-
type === "message" &&
207-
data.type === "commandGetPanelStatus" &&
208-
data.cluster === "ssIasAce" &&
209-
globalStore.hasValue(device.getEndpoint(1), "panelStatus")
210-
) {
211-
const payload = {
212-
panelstatus: globalStore.getValue(device.getEndpoint(1), "panelStatus"),
213-
secondsremain: 0x00,
214-
audiblenotif: 0x00,
215-
alarmstatus: 0x00,
216-
};
217-
await device.getEndpoint(1).commandResponse("ssIasAce", "getPanelStatusRsp", payload, {}, data.meta.zclTransactionSequenceNumber);
218-
}
219-
},
203+
extend: [m.iasGetPanelStatusResponse()],
220204
},
221205
{
222206
zigbeeModel: ["3420"],

src/devices/danfoss.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import * as fz from "../converters/fromZigbee";
44
import * as tz from "../converters/toZigbee";
55
import * as constants from "../lib/constants";
66
import * as exposes from "../lib/exposes";
7+
import * as m from "../lib/modernExtend";
78
import * as reporting from "../lib/reporting";
8-
import * as globalStore from "../lib/store";
99
import type {DefinitionWithExtend, Zh} from "../lib/types";
1010
import * as utils from "../lib/utils";
1111

@@ -345,26 +345,19 @@ export const definitions: DefinitionWithExtend[] = [
345345
// So, we need to write time during configure (same as for HEIMAN devices)
346346
await setTime(device);
347347
},
348-
onEvent: async (type, data, device) => {
349-
if (type === "stop") {
350-
clearInterval(globalStore.getValue(device, "interval"));
351-
globalStore.clearValue(device, "interval");
352-
} else if (["deviceAnnounce", "start"].includes(type)) {
353-
// The device might have lost its time, so reset it. It would be more proper to check if
354-
// the danfossSystemStatusCode has bit 10 of the SW error code attribute (0x4000) in the
355-
// diagnostics cluster (0x0b05) is set to indicate time lost, but setting it once too many
356-
// times shouldn't hurt.
357-
await setTime(device);
358-
359-
if (!globalStore.hasValue(device, "interval")) {
360-
// Set up a timer to refresh the time once a week to mitigate timer drift, as described
361-
// in the Danfoss documentation. Be careful to not bump this timer past the signed 32-bit
362-
// integer limit of setInterval, which is roughly 24.8 days.
363-
const interval = setInterval(async () => await setTime(device), 10080000);
364-
globalStore.putValue(device, "interval", interval);
365-
}
366-
}
367-
},
348+
extend: [
349+
m.poll({
350+
key: "time_sync",
351+
defaultIntervalSeconds: 60 * 60 * 24,
352+
poll: async (device) => {
353+
// The device might have lost its time, so reset it. It would be more proper to check if
354+
// the danfossSystemStatusCode has bit 10 of the SW error code attribute (0x4000) in the
355+
// diagnostics cluster (0x0b05) is set to indicate time lost, but setting it once too many
356+
// times shouldn't hurt.
357+
await setTime(device);
358+
},
359+
}),
360+
],
368361
},
369362
{
370363
fingerprint: [

src/devices/datek.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,16 @@ export const definitions: DefinitionWithExtend[] = [
113113
model: "0402946",
114114
vendor: "Datek",
115115
description: "Zigbee module for ID lock",
116-
fromZigbee: [fz.lock, fz.battery, fz.lock_operation_event, fz.lock_programming_event, fz.idlock, fz.idlock_fw, fz.lock_pin_code_response],
116+
fromZigbee: [
117+
fz.lock,
118+
fz.battery,
119+
fz.lock_operation_event,
120+
fz.lock_programming_event,
121+
fz.idlock,
122+
fz.idlock_fw,
123+
fz.lock_pin_code_response,
124+
fz.lock_programming_event_read_pincode,
125+
],
117126
toZigbee: [
118127
tz.lock,
119128
tz.lock_sound_volume,
@@ -168,18 +177,6 @@ export const definitions: DefinitionWithExtend[] = [
168177
await endpoint.read("closuresDoorLock", [0x4000, 0x4001, 0x4003, 0x4004, 0x4005], options);
169178
await endpoint.read("genBasic", [0x5000], options);
170179
},
171-
onEvent: async (type, data, device) => {
172-
// When we receive a code updated message, lets read the new value
173-
if (
174-
data.type === "commandProgrammingEventNotification" &&
175-
data.cluster === "closuresDoorLock" &&
176-
data.data &&
177-
data.data.userid !== undefined &&
178-
(data.data.programeventsrc === undefined || constants.lockSourceName[data.data.programeventsrc] !== "rf")
179-
) {
180-
await device.endpoints[0].command("closuresDoorLock", "getPinCode", {userid: data.data.userid}, {});
181-
}
182-
},
183180
exposes: [
184181
e.lock(),
185182
e.battery(),

0 commit comments

Comments
 (0)