-
Notifications
You must be signed in to change notification settings - Fork 4k
Add support for OWON AC221 IR controller #11106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dba937b
a583437
88dc1c9
57123c4
ec572dc
02082e4
dabcc99
dc128aa
d65c60e
da9b467
03fb93e
e346983
5e8c965
b09db91
3b6911e
1af151b
a6834e2
7bfa9aa
4adf2c7
cc83b99
e75535f
786917d
46a7f95
4eef5b7
a236075
92a7181
4e59d8c
77fe737
5dd0cf1
6afcafe
b2810ca
5199563
8337f87
505858e
bcea52c
b83b151
b535d16
0c6c9e9
97a153a
eeb999a
e3e850e
c253c95
ca9d073
70c935a
b8a012d
eeb5025
970bbad
5a3a416
fd4c8c8
61fcd65
1f9a2f1
e1fcc0e
6a3373e
443c2a2
21404d9
7f2074e
b49da6c
392920f
c6cbce7
5b51f51
9ff38c2
a7cb9b4
0b9685c
7247761
2baf941
1d55f75
3545ba2
2f0540b
beff44a
ecf6276
36e1f2c
24632e5
df322e6
59fa4cc
6f1c809
d9fb5b3
72602d9
847dac8
dfe8337
70f0e7c
ebaf456
b2a0523
995e90a
685b916
19bf166
b652c2f
b183346
7d481ec
7c66d00
1af59fe
203c833
d740bd4
a49739d
37f69a8
9eeebe6
03a1809
a5c0e12
5c7d5a8
4fca83b
1a93759
8e53a20
39b3f61
af6428e
6c82895
4b5470d
204f9b5
7913a15
8af886e
06e65fa
6d144e0
8d72013
04e0b23
00babed
ea19c1b
6cf5798
3fcaad0
7deab07
69ccddd
413ecdb
897525d
3ae6944
456558d
4c07485
c45e371
2fdfc90
580e80f
444d586
6c0bf75
f62bc72
67b2fc0
b2d6641
575be3b
cfa4c41
a2d9016
a7941ac
6aec6d1
3319f72
f0c3255
64e0b65
69a43b7
3b20763
901fae0
af528f7
b1363f6
f68244e
0244796
d813d20
6019c81
9ed79e3
8eede2c
a1e1240
a1f04b2
7db45f8
ec79460
b697329
4d3c668
53f4613
7970ae5
3b41fca
51e509d
c7e556c
bff7060
1f3f8dc
e3f9c60
2924283
628b8f7
f5e49e0
fa1ecd1
9e61a78
4fa1989
6fd39e4
f2f5fe0
3789d15
a4b9ad5
6ff025c
85dff2d
b420af0
e616278
daba9ca
28a53b0
65bc011
e24e4b3
e576814
aa6e017
7589490
b01b2ee
6454360
e9230c5
230462a
09a0bdb
df35fa0
d649e0c
c7dfeac
a507ad7
c826c40
2d0bf78
d4a587e
1637eda
fc38911
b5f8056
0263b80
3c65461
a86c7a0
19489aa
369366c
f28ad12
856f63a
7e7af46
6a4b449
168f9bc
671f322
1b86983
02fa467
989fe52
3044908
aa10eb3
474cda7
6f5cbdb
ee061c1
33ff0f8
637d806
8a592cf
5ab97eb
1bc8671
30f10fa
c10d98f
dda4c91
d42bccc
f7f4300
df1d31e
cce8cfe
ef9a87c
5219645
db94a15
a2b1ec0
eff2928
8e9205f
d269d5e
d7a4585
584f045
1bf68a6
7fa9c15
abd143e
454f87b
e2c1b0f
2b6cd0d
c34a69b
b8fab5e
65334f7
a0eee76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||
| import {Zcl} from "zigbee-herdsman"; | ||||
| import type {ClusterDefinition} from "zigbee-herdsman/dist/zspec/zcl/definition/tstype"; | ||||
| import * as fz from "../converters/fromZigbee"; | ||||
| import * as tz from "../converters/toZigbee"; | ||||
| import * as exposes from "../lib/exposes"; | ||||
|
|
@@ -9,6 +10,99 @@ import type {DefinitionWithExtend, Fz, KeyValue, Tz} from "../lib/types"; | |||
| const e = exposes.presets; | ||||
| const ea = exposes.access; | ||||
|
|
||||
| interface OwonAcControl { | ||||
| attributes: Record<string, never>; | ||||
| commands: { | ||||
| oneKeyPairingRequest: {oneKeyPairingStart: number}; // 0x00 end, 0x01 start | ||||
| writePairingCode: {pairingCode: number}; | ||||
| readPairingCodeRequest: Record<string, never>; | ||||
| }; | ||||
| commandResponses: never; | ||||
| } | ||||
|
|
||||
| const owonExtendChecks = { | ||||
| parseOneKeyPairingInput: (input: unknown) => { | ||||
| const action = String(input).toLowerCase().trim(); | ||||
|
|
||||
| let startParam: number; | ||||
| if (action === "start" || action === "on" || action === "true") { | ||||
| startParam = 0x01; | ||||
| } else if (action === "end" || action === "off" || action === "false") { | ||||
| startParam = 0x00; | ||||
| } else { | ||||
| throw new Error(`Invalid value for one_key_pairing: expected "start"/"on"/"true" or "end"/"off"/"false", got "${input}"`); | ||||
| } | ||||
|
|
||||
| return { | ||||
| payload: { | ||||
| oneKeyPairingStart: startParam, | ||||
| }, | ||||
| }; | ||||
| }, | ||||
|
|
||||
| parsePairingCodeInput: (input: unknown) => { | ||||
| if (input === undefined || input === null) { | ||||
| throw new Error("pairing_code is required"); | ||||
| } | ||||
|
|
||||
| const codeStr = typeof input === "number" ? Math.trunc(input).toString() : String(input).trim(); | ||||
|
|
||||
| if (!/^\d+$/.test(codeStr)) { | ||||
| throw new Error(`Invalid pairing_code "${codeStr}", must be decimal number`); | ||||
| } | ||||
|
|
||||
| const codeNum = Number(codeStr); | ||||
|
|
||||
| if (codeNum < 0 || codeNum > 65535) { | ||||
| throw new Error(`Invalid pairing_code "${codeStr}", must be between 0 and 65535`); | ||||
| } | ||||
|
|
||||
| return { | ||||
| payload: { | ||||
| pairingCode: codeNum, | ||||
| }, | ||||
| }; | ||||
| }, | ||||
| }; | ||||
|
|
||||
| const OwonClustersDefinition: {[s: string]: ClusterDefinition} = { | ||||
| manuSpecificOwonAc: { | ||||
| ID: 0xffac, | ||||
| manufacturerCode: Zcl.ManufacturerCode.OWON_TECHNOLOGY_INC, | ||||
| attributes: {}, | ||||
| commands: { | ||||
| oneKeyPairingRequest: { | ||||
| ID: 0x52, | ||||
| parameters: [ | ||||
| {name: "oneKeyPairingStart", type: Zcl.DataType.UINT8}, // 0x00 end, 0x01 start | ||||
| ], | ||||
| }, | ||||
| writePairingCode: { | ||||
| ID: 0x20, | ||||
| parameters: [{name: "pairingCode", type: Zcl.DataType.UINT16}], | ||||
| }, | ||||
| readPairingCodeRequest: { | ||||
| ID: 0x00, | ||||
| parameters: [], | ||||
| }, | ||||
| }, | ||||
| commandsResponse: { | ||||
| oneKeyPairingResponse: { | ||||
| ID: 0x52, | ||||
| parameters: [{name: "receiveStatus", type: Zcl.DataType.UINT8}], | ||||
| }, | ||||
| oneKeyPairingResultUpdate: { | ||||
| ID: 0x80, | ||||
| parameters: [], | ||||
| }, | ||||
| readPairingCodeResponse: { | ||||
| ID: 0x00, | ||||
| parameters: [{name: "pairingCode", type: Zcl.DataType.UINT16}], | ||||
| }, | ||||
| }, | ||||
| }, | ||||
| }; | ||||
|
|
||||
| interface OwonFallDetection { | ||||
| attributes: { | ||||
| status: number; | ||||
|
|
@@ -185,6 +279,78 @@ const fzLocal = { | |||
| return result; | ||||
| }, | ||||
| } satisfies Fz.Converter<"fallDetectionOwon", OwonFallDetection, ["attributeReport", "readResponse"]>, | ||||
|
|
||||
| owonAcOneKeyPairingResponse: { | ||||
| cluster: "manuSpecificOwonAc", | ||||
| type: ["commandOneKeyPairingResponse", "commandOneKeyPairingResultUpdate"], | ||||
| convert: (model, msg, publish, options, meta) => { | ||||
| if (msg.meta?.manufacturerCode !== Zcl.ManufacturerCode.OWON_TECHNOLOGY_INC) { | ||||
| return {}; | ||||
| } | ||||
|
|
||||
| const payload: KeyValue = {}; | ||||
|
|
||||
| if (msg.type === "commandOneKeyPairingResponse") { | ||||
| const status = msg.data?.receiveStatus; | ||||
| if (status !== undefined) { | ||||
| payload.one_key_pairing_status = status === 0x00 ? "SUCCESS" : "FAILURE"; | ||||
| } | ||||
| } | ||||
|
|
||||
| if (msg.type === "commandOneKeyPairingResultUpdate") { | ||||
| let buffer: Buffer | undefined; | ||||
|
|
||||
| if (Buffer.isBuffer(msg.meta?.rawData)) { | ||||
| buffer = msg.meta.rawData; | ||||
| } | ||||
|
|
||||
| if (!buffer || buffer.length < 6) { | ||||
| payload.one_key_pairing_result = { | ||||
| count: 0, | ||||
| codes_found: [], | ||||
| }; | ||||
| return payload; | ||||
| } | ||||
|
|
||||
| const payloadOffset = 5; | ||||
| const count = buffer.readUInt8(payloadOffset); | ||||
| const codes: number[] = []; | ||||
|
|
||||
| let offset = payloadOffset + 1; | ||||
| for (let i = 0; i < count; i++) { | ||||
| if (offset + 1 >= buffer.length) break; | ||||
| codes.push(buffer.readUInt16LE(offset)); | ||||
| offset += 2; | ||||
| } | ||||
|
|
||||
| payload.one_key_pairing_result = { | ||||
| count, | ||||
| codes_found: codes, | ||||
| }; | ||||
| } | ||||
|
|
||||
| return payload; | ||||
| }, | ||||
| // biome-ignore lint/suspicious/noExplicitAny: third-party converter signature requires any | ||||
| } satisfies Fz.Converter<"manuSpecificOwonAc", undefined, any>, | ||||
|
|
||||
| owonAcReadPairingCodeResponse: { | ||||
| cluster: "manuSpecificOwonAc", | ||||
| type: ["commandReadPairingCodeResponse"], | ||||
| convert: (model, msg, publish, options, meta) => { | ||||
| if (msg.data?.pairingCode !== undefined) { | ||||
| const code = msg.data.pairingCode; | ||||
| const displayCode = code === 0xffff ? null : code; | ||||
|
|
||||
| return { | ||||
| pairing_code_current: displayCode, | ||||
| }; | ||||
| } | ||||
|
|
||||
| return {}; | ||||
| }, | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adjusted to satisfies Fz.Converter for the manufacturer-specific cluster; command types are kept untyped as they are not part of ZCL definitions. |
||||
| // biome-ignore lint/suspicious/noExplicitAny: third-party converter signature requires any | ||||
| } satisfies Fz.Converter<"manuSpecificOwonAc", undefined, any>, | ||||
| }; | ||||
|
|
||||
| const tzLocal = { | ||||
|
|
@@ -242,6 +408,50 @@ const tzLocal = { | |||
| ); | ||||
| }, | ||||
| } satisfies Tz.Converter, | ||||
|
|
||||
| owonAcOneKeyPairing: { | ||||
| key: ["one_key_pairing"], | ||||
| convertSet: async (entity, key, value, meta) => { | ||||
| meta.state.one_key_pairing_status = null; | ||||
| meta.state.one_key_pairing_result = null; | ||||
| const commandWrapper = owonExtendChecks.parseOneKeyPairingInput(value); | ||||
|
|
||||
| await entity.command<"manuSpecificOwonAc", "oneKeyPairingRequest", OwonAcControl>( | ||||
| "manuSpecificOwonAc", | ||||
| "oneKeyPairingRequest", | ||||
| commandWrapper.payload, | ||||
| {disableDefaultResponse: true}, | ||||
| ); | ||||
| }, | ||||
| } satisfies Tz.Converter, | ||||
|
|
||||
| owonAcWritePairingCode: { | ||||
| key: ["pairing_code"], | ||||
| convertSet: async (entity, key, value, meta) => { | ||||
| const commandWrapper = owonExtendChecks.parsePairingCodeInput(value); | ||||
|
|
||||
| meta.state.pairing_code = String(value); | ||||
|
|
||||
| await entity.command<"manuSpecificOwonAc", "writePairingCode", OwonAcControl>( | ||||
| "manuSpecificOwonAc", | ||||
| "writePairingCode", | ||||
| commandWrapper.payload, | ||||
| {disableDefaultResponse: true}, | ||||
| ); | ||||
| }, | ||||
| } satisfies Tz.Converter, | ||||
|
|
||||
| owonAcReadPairingCode: { | ||||
| key: ["pairing_code_current"], | ||||
| convertGet: async (entity, key, meta) => { | ||||
| await entity.command<"manuSpecificOwonAc", "readPairingCodeRequest", OwonAcControl>( | ||||
| "manuSpecificOwonAc", | ||||
| "readPairingCodeRequest", | ||||
| {}, | ||||
| {disableDefaultResponse: true}, | ||||
| ); | ||||
| }, | ||||
| } satisfies Tz.Converter, | ||||
| }; | ||||
|
|
||||
| export const definitions: DefinitionWithExtend[] = [ | ||||
|
|
@@ -250,15 +460,29 @@ export const definitions: DefinitionWithExtend[] = [ | |||
| model: "WSP402", | ||||
| vendor: "OWON", | ||||
| description: "Smart plug", | ||||
| extend: [m.onOff(), m.electricityMeter({cluster: "metering"})], | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The device is now migrated to modernExtend. A minimal configure() is kept only to customize metering reporting parameters, |
||||
| extend: [ | ||||
| m.onOff({powerOnBehavior: false}), | ||||
| m.electricityMeter({ | ||||
| cluster: "metering", | ||||
| }), | ||||
|
|
||||
| m.forcePowerSource({powerSource: "Mains (single phase)"}), | ||||
| ], | ||||
| }, | ||||
| { | ||||
| zigbeeModel: ["WSP403-E"], | ||||
| model: "WSP403", | ||||
| vendor: "OWON", | ||||
| whiteLabel: [{vendor: "Oz Smart Things", model: "WSP403"}], | ||||
| description: "Smart plug", | ||||
| extend: [m.onOff(), m.electricityMeter({cluster: "metering"}), m.forcePowerSource({powerSource: "Mains (single phase)"})], | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use this instead of the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The device is now migrated to modernExtend. A minimal configure() is kept only to customize metering reporting parameters,
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
To have a consistent experience, all devices should have the same reporting parameters unless there is a really good reason not to, is there one in this case?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The custom reporting parameters are added because WSP403 reports In practice this results in delayed or missing power updates, The adjusted reporting improves usability without increasing
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand, it should report with the same frequency as all other plugs right? The default can be found here:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, thanks for pointing this out. After reviewing modernExtend.electricityMeter(), it already configures |
||||
| extend: [ | ||||
| m.onOff({powerOnBehavior: false}), | ||||
| m.electricityMeter({ | ||||
| cluster: "metering", | ||||
| }), | ||||
|
|
||||
| m.forcePowerSource({powerSource: "Mains (single phase)"}), | ||||
| ], | ||||
| }, | ||||
| { | ||||
| zigbeeModel: ["WSP404"], | ||||
|
|
@@ -331,6 +555,60 @@ export const definitions: DefinitionWithExtend[] = [ | |||
| await reporting.thermostatAcLouverPosition(endpoint); | ||||
| }, | ||||
| }, | ||||
| { | ||||
| zigbeeModel: ["AC221"], | ||||
| model: "AC221", | ||||
| vendor: "OWON", | ||||
| description: "AC controller / IR blaster", | ||||
| extend: [m.deviceAddCustomCluster("manuSpecificOwonAc", OwonClustersDefinition.manuSpecificOwonAc)], | ||||
| fromZigbee: [fz.fan, fz.thermostat, fzLocal.owonAcOneKeyPairingResponse, fzLocal.owonAcReadPairingCodeResponse], | ||||
| toZigbee: [ | ||||
| tz.fan_mode, | ||||
| tz.thermostat_system_mode, | ||||
| tz.thermostat_occupied_heating_setpoint, | ||||
| tz.thermostat_occupied_cooling_setpoint, | ||||
| tz.thermostat_ac_louver_position, | ||||
| tz.thermostat_local_temperature, | ||||
| tzLocal.owonAcOneKeyPairing, | ||||
| tzLocal.owonAcWritePairingCode, | ||||
| tzLocal.owonAcReadPairingCode, | ||||
| ], | ||||
|
|
||||
| exposes: [ | ||||
| // --- One Key Pairing Exposes --- | ||||
| e.enum("one_key_pairing", ea.SET, ["start", "end"]), | ||||
| e.text("one_key_pairing_status", ea.STATE).withDescription("Status of the last one key pairing request command."), | ||||
| e.text("one_key_pairing_result", ea.STATE).withDescription("Final result of one key pairing process (JSON string, device reported)."), | ||||
| e | ||||
| .numeric("pairing_code_current", ea.STATE_GET) | ||||
| .withDescription("Currently set pairing code on the device (null if invalid)") | ||||
| .withUnit("") | ||||
| .withValueMin(0) | ||||
| .withValueMax(65535), | ||||
| e.text("pairing_code", ea.SET).withDescription("Manually write pairing code to device (decimal digits only, e.g. 123456)."), | ||||
| e | ||||
| .climate() | ||||
| .withSystemMode(["off", "heat", "cool", "auto", "dry", "fan_only"]) | ||||
| .withSetpoint("occupied_heating_setpoint", 8, 30, 1) | ||||
| .withSetpoint("occupied_cooling_setpoint", 8, 30, 1) | ||||
| .withLocalTemperature(), | ||||
| e.fan().withModes(["low", "medium", "high", "on", "auto"]), | ||||
| ], | ||||
|
|
||||
| configure: async (device, coordinatorEndpoint) => { | ||||
| const endpoint = device.getEndpoint(1); | ||||
| const binds = ["genBasic", "genIdentify", "genTime", "hvacThermostat", "hvacFanCtrl"]; | ||||
|
|
||||
| await reporting.bind(endpoint, coordinatorEndpoint, binds); | ||||
|
|
||||
| await reporting.thermostatTemperature(endpoint, {min: 60, max: 600, change: 0.1}); | ||||
| await reporting.thermostatOccupiedHeatingSetpoint(endpoint); | ||||
| await reporting.thermostatOccupiedCoolingSetpoint(endpoint); | ||||
| await reporting.thermostatSystemMode(endpoint); | ||||
|
|
||||
| await reporting.fanMode(endpoint); | ||||
| }, | ||||
| }, | ||||
| { | ||||
| zigbeeModel: ["THS317"], | ||||
| model: "THS317", | ||||
|
|
@@ -604,19 +882,19 @@ export const definitions: DefinitionWithExtend[] = [ | |||
| ID: 0xfd00, | ||||
| manufacturerCode: Zcl.ManufacturerCode.OWON_TECHNOLOGY_INC, | ||||
| attributes: { | ||||
| status: {ID: 0x0000, type: Zcl.DataType.ENUM8, write: true, max: 0xff}, | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this removed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This attribute is now defined via deviceAddCustomCluster. In this typed definition |
||||
| breathing_rate: {ID: 0x0002, type: Zcl.DataType.UINT8, write: true, max: 0xff}, | ||||
| location_x: {ID: 0x0003, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| location_y: {ID: 0x0004, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| bedUpperLeftX: {ID: 0x0100, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| bedUpperLeftY: {ID: 0x0101, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| bedLowerRightX: {ID: 0x0102, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| bedLowerRightY: {ID: 0x0103, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| doorCenterX: {ID: 0x0108, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| doorCenterY: {ID: 0x0109, type: Zcl.DataType.INT16, write: true, min: -32768}, | ||||
| leftFallDetectionRange: {ID: 0x010c, type: Zcl.DataType.UINT16, write: true, max: 0xffff}, | ||||
| rightFallDetectionRange: {ID: 0x010d, type: Zcl.DataType.UINT16, write: true, max: 0xffff}, | ||||
| frontFallDetectionRange: {ID: 0x010e, type: Zcl.DataType.UINT16, write: true, max: 0xffff}, | ||||
| status: {ID: 0x0000, type: Zcl.DataType.ENUM8}, | ||||
| breathing_rate: {ID: 0x0002, type: Zcl.DataType.UINT8}, | ||||
| location_x: {ID: 0x0003, type: Zcl.DataType.INT16}, | ||||
| location_y: {ID: 0x0004, type: Zcl.DataType.INT16}, | ||||
| bedUpperLeftX: {ID: 0x0100, type: Zcl.DataType.INT16}, | ||||
| bedUpperLeftY: {ID: 0x0101, type: Zcl.DataType.INT16}, | ||||
| bedLowerRightX: {ID: 0x0102, type: Zcl.DataType.INT16}, | ||||
| bedLowerRightY: {ID: 0x0103, type: Zcl.DataType.INT16}, | ||||
| doorCenterX: {ID: 0x0108, type: Zcl.DataType.INT16}, | ||||
| doorCenterY: {ID: 0x0109, type: Zcl.DataType.INT16}, | ||||
| leftFallDetectionRange: {ID: 0x010c, type: Zcl.DataType.UINT16}, | ||||
| rightFallDetectionRange: {ID: 0x010d, type: Zcl.DataType.UINT16}, | ||||
| frontFallDetectionRange: {ID: 0x010e, type: Zcl.DataType.UINT16}, | ||||
| }, | ||||
| commands: {}, | ||||
| commandsResponse: {}, | ||||
|
|
@@ -648,13 +926,19 @@ export const definitions: DefinitionWithExtend[] = [ | |||
| vendor: "OWON", | ||||
| description: "Smart plug with doorbell press indicator", | ||||
| extend: [ | ||||
| m.onOff({endpointNames: ["l1", "l2", "l3"]}), | ||||
| m.deviceEndpoints({ | ||||
| endpoints: { | ||||
| l1: 1, | ||||
| l2: 2, | ||||
| l3: 3, | ||||
| }, | ||||
| }), | ||||
| m.onOff({endpointNames: ["l1", "l2", "l3"], powerOnBehavior: false}), | ||||
| m.iasZoneAlarm({ | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using m.deviceEndpoints handles multi-endpoint, so meta.multiEndpoint is not needed. |
||||
| zoneType: "contact", | ||||
| zoneAttributes: ["alarm_2"], | ||||
| }), | ||||
| ], | ||||
| endpoint: (device) => ({l1: 1, l2: 2, l3: 3}), | ||||
| configure: async (device, coordinatorEndpoint) => { | ||||
| const ep2 = device.getEndpoint(2); | ||||
| if (ep2) { | ||||
|
|
||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
satisfies Fz.Converter<with the custom cluster definition hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted to satisfies Fz.Converter for the manufacturer-specific cluster; command types are kept untyped as they are not part of ZCL definitions.