From 972707ad21aba9563ff8fe2fe7d41aa82ad0b96b Mon Sep 17 00:00:00 2001 From: niracler Date: Tue, 3 Dec 2024 17:20:44 +0800 Subject: [PATCH] fix: add SR-ZG9002KR12-Pro SR-ZG9002KR12-Pro configure and use extend instead only fromZigbee --- src/devices/sunricher.ts | 268 ++++++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 113 deletions(-) diff --git a/src/devices/sunricher.ts b/src/devices/sunricher.ts index 213219bb159fb..6f2f2c0cd22d2 100644 --- a/src/devices/sunricher.ts +++ b/src/devices/sunricher.ts @@ -170,6 +170,159 @@ function sunricherMinimumPWM(): ModernExtend { }; } +function sunricherSRZG9002KR12Pro(): ModernExtend { + const cluster = 0xff03; + + const fromZigbee: Fz.Converter[] = [ + { + cluster: 0xff03, + type: ['raw'], + convert: (model, msg, publish, options, meta) => { + const bytes = [...msg.data]; + const messageType = bytes[3]; + let action = 'unknown'; + + if (messageType === 0x01) { + const pressTypeMask: number = bytes[6]; + const pressTypeLookup: {[key: number]: string} = { + 0x01: 'short_press', + 0x02: 'double_press', + 0x03: 'hold', + 0x04: 'hold_released', + }; + action = pressTypeLookup[pressTypeMask] || 'unknown'; + + const buttonMask = (bytes[4] << 8) | bytes[5]; + const specialButtonMap: {[key: number]: string} = { + 9: 'knob', + 11: 'k9', + 12: 'k10', + 15: 'k11', + 16: 'k12', + }; + + const actionButtons: string[] = []; + for (let i = 0; i < 16; i++) { + if ((buttonMask >> i) & 1) { + const button = i + 1; + actionButtons.push(specialButtonMap[button] ?? `k${button}`); + } + } + return {action, action_buttons: actionButtons}; + } else if (messageType === 0x03) { + const directionMask = bytes[4]; + const actionSpeed = bytes[6]; + + const directionMap: {[key: number]: string} = { + 0x01: 'clockwise', + 0x02: 'anti_clockwise', + }; + const direction = directionMap[directionMask] || 'unknown'; + + action = `${direction}_rotation`; + return {action, action_speed: actionSpeed}; + } + + return {action}; + }, + }, + ]; + + const exposes: Expose[] = [e.action(['short_press', 'double_press', 'hold', 'hold_released', 'clockwise_rotation', 'anti_clockwise_rotation'])]; + + const configure: [Configure] = [ + async (device, coordinatorEndpoint, definition) => { + const endpoint = device.getEndpoint(1); + await endpoint.bind(cluster, coordinatorEndpoint); + }, + ]; + + return { + fromZigbee, + exposes, + configure, + isModernExtend: true, + }; +} + +function sunricherSRZG2836D5Pro(): ModernExtend { + const cluster = 0xff03; + + const fromZigbee: Fz.Converter[] = [ + { + cluster: 0xff03, + type: ['raw'], + convert: (model, msg, publish, options, meta) => { + const bytes = [...msg.data]; + const messageType = bytes[3]; + let action = 'unknown'; + + if (messageType === 0x01) { + const pressTypeMask: number = bytes[6]; + const pressTypeLookup: {[key: number]: string} = { + 0x01: 'short_press', + 0x02: 'double_press', + 0x03: 'hold', + 0x04: 'hold_released', + }; + action = pressTypeLookup[pressTypeMask] || 'unknown'; + + const buttonMask = bytes[5]; + const specialButtonLookup: {[key: number]: string} = { + 0x01: 'top_left', + 0x02: 'top_right', + 0x03: 'bottom_left', + 0x04: 'bottom_right', + 0x05: 'center', + }; + + const actionButtons: string[] = []; + for (let i = 0; i < 5; i++) { + if ((buttonMask >> i) & 1) { + const button = i + 1; + actionButtons.push(specialButtonLookup[button] || `unknown_${button}`); + } + } + return {action, action_buttons: actionButtons}; + } else if (messageType === 0x03) { + const directionMask = bytes[4]; + const actionSpeed = bytes[6]; + const isStop = bytes[5] === 0x02; + + const directionMap: {[key: number]: string} = { + 0x01: 'clockwise', + 0x02: 'anti_clockwise', + }; + const direction = isStop ? 'stop' : directionMap[directionMask] || 'unknown'; + + action = `${direction}_rotation`; + return {action, action_speed: actionSpeed}; + } + + return {action}; + }, + }, + ]; + + const exposes: Expose[] = [ + e.action(['short_press', 'double_press', 'hold', 'hold_released', 'clockwise_rotation', 'anti_clockwise_rotation', 'stop_rotation']), + ]; + + const configure: [Configure] = [ + async (device, coordinatorEndpoint, definition) => { + const endpoint = device.getEndpoint(1); + await endpoint.bind(cluster, coordinatorEndpoint); + }, + ]; + + return { + fromZigbee, + exposes, + configure, + isModernExtend: true, + }; +} + const fzLocal = { sunricher_SRZGP2801K45C: { cluster: 'greenPower', @@ -196,111 +349,6 @@ const fzLocal = { return {action: utils.getFromLookup(commandID, lookup)}; }, } satisfies Fz.Converter, - sunricher_SRZG9002KR12Pro: { - cluster: 0xff03, - type: ['raw'], - convert: (model, msg, publish, options, meta) => { - const bytes = [...msg.data]; - const messageType = bytes[3]; - let action = 'unknown'; - - if (messageType === 0x01) { - const pressTypeMask: number = bytes[6]; - const pressTypeLookup: {[key: number]: string} = { - 0x01: 'short_press', - 0x02: 'double_press', - 0x03: 'hold', - 0x04: 'hold_released', - }; - action = pressTypeLookup[pressTypeMask] || 'unknown'; - - const buttonMask = (bytes[4] << 8) | bytes[5]; - const specialButtonMap: {[key: number]: string} = { - 9: 'knob', - 11: 'k9', - 12: 'k10', - 15: 'k11', - 16: 'k12', - }; - - const actionButtons: string[] = []; - for (let i = 0; i < 16; i++) { - if ((buttonMask >> i) & 1) { - const button = i + 1; - actionButtons.push(specialButtonMap[button] ?? `k${button}`); - } - } - return {action, action_buttons: actionButtons}; - } else if (messageType === 0x03) { - const directionMask = bytes[4]; - const actionSpeed = bytes[6]; - - const directionMap: {[key: number]: string} = { - 0x01: 'clockwise', - 0x02: 'anti_clockwise', - }; - const direction = directionMap[directionMask] || 'unknown'; - - action = `${direction}_rotation`; - return {action, action_speed: actionSpeed}; - } - - return {action}; - }, - } satisfies Fz.Converter, - sunricher_SRZG2836D5Pro: { - cluster: 0xff03, - type: ['raw'], - convert: (model, msg, publish, options, meta) => { - const bytes = [...msg.data]; - const messageType = bytes[3]; - let action = 'unknown'; - - if (messageType === 0x01) { - const pressTypeMask: number = bytes[6]; - const pressTypeLookup: {[key: number]: string} = { - 0x01: 'short_press', - 0x02: 'double_press', - 0x03: 'hold', - 0x04: 'hold_released', - }; - action = pressTypeLookup[pressTypeMask] || 'unknown'; - - const buttonMask = bytes[5]; - const specialButtonLookup: {[key: number]: string} = { - 0x01: 'top_left', - 0x02: 'top_right', - 0x03: 'bottom_left', - 0x04: 'bottom_right', - 0x05: 'center', - }; - - const actionButtons: string[] = []; - for (let i = 0; i < 5; i++) { - if ((buttonMask >> i) & 1) { - const button = i + 1; - actionButtons.push(specialButtonLookup[button] || `unknown_${button}`); - } - } - return {action, action_buttons: actionButtons}; - } else if (messageType === 0x03) { - const directionMask = bytes[4]; - const actionSpeed = bytes[6]; - const isStop = bytes[5] === 0x02; - - const directionMap: {[key: number]: string} = { - 0x01: 'clockwise', - 0x02: 'anti_clockwise', - }; - const direction = isStop ? 'stop' : directionMap[directionMask] || 'unknown'; - - action = `${direction}_rotation`; - return {action, action_speed: actionSpeed}; - } - - return {action}; - }, - } satisfies Fz.Converter, }; async function syncTime(endpoint: Zh.Endpoint) { @@ -319,20 +367,14 @@ const definitions: DefinitionWithExtend[] = [ model: 'SR-ZG2836D5-Pro', vendor: 'Sunricher', description: 'Zigbee smart remote', - extend: [battery()], - fromZigbee: [fzLocal.sunricher_SRZG2836D5Pro], - exposes: [ - e.action(['short_press', 'double_press', 'hold', 'hold_released', 'clockwise_rotation', 'anti_clockwise_rotation', 'stop_rotation']), - ], + extend: [battery(), sunricherSRZG2836D5Pro()], }, { zigbeeModel: ['HK-ZRC-K12&RS-E'], model: 'SR-ZG9002KR12-Pro', vendor: 'Sunricher', description: 'Zigbee smart wall panel remote', - extend: [battery()], - fromZigbee: [fzLocal.sunricher_SRZG9002KR12Pro], - exposes: [e.action(['short_press', 'double_press', 'hold', 'hold_released', 'clockwise_rotation', 'anti_clockwise_rotation'])], + extend: [battery(), sunricherSRZG9002KR12Pro()], }, { zigbeeModel: ['ZV9380A', 'ZG9380A'],