From ea839c94247415550716b3717434daf0a0f6ec5a Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Fri, 3 Jan 2025 20:15:08 +0100 Subject: [PATCH 1/5] Updated E2BP-UP - Added E2BPA-UP and E4BPX-UP (#10) --- src/devices/yokis.ts | 58 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/devices/yokis.ts b/src/devices/yokis.ts index 8a60399f0e031..128c5d019e7ae 100644 --- a/src/devices/yokis.ts +++ b/src/devices/yokis.ts @@ -2411,8 +2411,8 @@ const definitions: DefinitionWithExtend[] = [ ], }, { - // E2BPA-UP - zigbeeModel: ['E2BPA-UP', 'E2BP-UP'], + // E2BP-UP + zigbeeModel: ['E2BP-UP'], model: 'E2BP-UP', vendor: 'YOKIS', description: 'Flush-mounted independent 2-channel transmitter', @@ -2436,8 +2436,33 @@ const definitions: DefinitionWithExtend[] = [ ], }, { - // E4BPA-UP - zigbeeModel: ['E4BPA-UP', 'E4BP-UP', 'E4BPX-UP'], + // E2BPA-UP + zigbeeModel: ['E2BPA-UP'], + model: 'E2BPA-UP', + vendor: 'YOKIS', + description: 'Flush-mounted independent 2-channel transmitter (main powered)', + extend: [ + deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), + deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), + deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), + deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), + deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation + deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), + deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation + deviceEndpoints({endpoints: {'1': 1, '2': 2}}), + identify(), + commandsOnOff(), + commandsLevelCtrl(), + commandsWindowCovering(), + // ...YokisDeviceExtend, + // ...YokisInputExtend, + // ...YokisChannelExtend, + ...YokisPilotWireExtend, + ], + }, + { + // E4BP-UP + zigbeeModel: ['E4BP-UP'], model: 'E4BP-UP', vendor: 'YOKIS', description: 'Flush-mounted independent 4-channel transmitter', @@ -2460,6 +2485,31 @@ const definitions: DefinitionWithExtend[] = [ ...YokisPilotWireExtend, ], }, + { + // E4BPX-UP + zigbeeModel: ['E4BPX-UP'], + model: 'E4BPX-UP', + vendor: 'YOKIS', + description: 'Flush-mounted independent 4-channel transmitter (with antenna)', + extend: [ + deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), + deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), + deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), + deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), + deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation + deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), + deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation + deviceEndpoints({endpoints: {'1': 1, '2': 2, '3': 3, '4': 4}}), + identify(), + commandsOnOff(), + commandsLevelCtrl(), + commandsWindowCovering(), + // ...YokisDeviceExtend, + // ...YokisInputExtend, + // ...YokisChannelExtend, + ...YokisPilotWireExtend, + ], + }, { // TLC1-UP zigbeeModel: ['TLC1-UP'], From ced077093fee9c52ad84a05176fd9fcbcf8fab66 Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Sat, 1 Feb 2025 14:15:01 +0100 Subject: [PATCH 2/5] Fixing E2BPA-UP definition. Custom onOff definition for Yokis devices. --- src/devices/yokis.ts | 93 +++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/src/devices/yokis.ts b/src/devices/yokis.ts index 128c5d019e7ae..96055a42e85e7 100644 --- a/src/devices/yokis.ts +++ b/src/devices/yokis.ts @@ -1400,6 +1400,39 @@ const yokisCommandsExtend = { }, }; +// Yokis specific definition + +// Yokis does not support the timer OnOff cluster. It uses a custom cluster instead. +// Dereferencing the `on_time` and `off_wait_time` from the keys of the converter. +const yokisTz = { + on_off: { + key: ['state'], + convertSet: async (entity, key, value, meta) => { + const state = utils.isString(meta.message.state) ? meta.message.state.toLowerCase() : null; + utils.validateValue(state, ['toggle', 'off', 'on']); + + await entity.command('genOnOff', state, {}, utils.getOptions(meta.mapped, entity)); + if (state === 'toggle') { + const currentState = meta.state[`state${meta.endpoint_name ? `_${meta.endpoint_name}` : ''}`]; + return currentState ? {state: {state: currentState === 'OFF' ? 'ON' : 'OFF'}} : {}; + } else { + return {state: {state: state.toUpperCase()}}; + } + }, + convertGet: async (entity, key, meta) => { + await entity.read('genOnOff', ['onOff']); + }, + } satisfies Tz.Converter, +}; + +function YokisOnOff(args?: m.OnOffArgs): ModernExtend { + const result: ModernExtend = {...m.onOff(args), toZigbee: [yokisTz.on_off]}; + + logger.debug(`YokisOnOff: ${JSON.stringify(result)}`, NS); + + return result; +} + // Custom cluster exposition const YokisDeviceExtend: ModernExtend[] = [ // ConfigurationChanged => This attribute is used by Yokis-based controller and probably not very useful at the moment, as we don't know which configuration was changed. @@ -2296,7 +2329,7 @@ const definitions: DefinitionWithExtend[] = [ m.deviceAddCustomCluster('manuSpecificYokisLoadManager', YokisClustersDefinition['manuSpecificYokisLoadManager']), // Pending implementation m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), m.deviceAddCustomCluster('manuSpecificYokisStats', YokisClustersDefinition['manuSpecificYokisStats']), // Pending implementation - m.onOff({powerOnBehavior: false}), // StartupOnOff is not supported + YokisOnOff({powerOnBehavior: false}), // StartupOnOff is not supported m.identify(), ...YokisSubSystemExtend, ...yokisLightControlExtend, @@ -2321,7 +2354,7 @@ const definitions: DefinitionWithExtend[] = [ m.deviceAddCustomCluster('manuSpecificYokisLoadManager', YokisClustersDefinition['manuSpecificYokisLoadManager']), // Pending implementation m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), m.deviceAddCustomCluster('manuSpecificYokisStats', YokisClustersDefinition['manuSpecificYokisStats']), // Pending implementation - m.onOff({powerOnBehavior: false}), // StartupOnOff is not supported + YokisOnOff({powerOnBehavior: false}), // StartupOnOff is not supported m.identify(), ...YokisSubSystemExtend, ...yokisLightControlExtend, @@ -2346,7 +2379,7 @@ const definitions: DefinitionWithExtend[] = [ m.deviceAddCustomCluster('manuSpecificYokisLoadManager', YokisClustersDefinition['manuSpecificYokisLoadManager']), // Pending implementation m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), m.deviceAddCustomCluster('manuSpecificYokisStats', YokisClustersDefinition['manuSpecificYokisStats']), // Pending implementation - m.onOff({powerOnBehavior: false}), // StartupOnOff is not supported + YokisOnOff({powerOnBehavior: false}), // StartupOnOff is not supported m.identify(), ...YokisSubSystemExtend, ...yokisLightControlExtend, @@ -2372,7 +2405,11 @@ const definitions: DefinitionWithExtend[] = [ m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), m.deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), m.deviceAddCustomCluster('manuSpecificYokisStats', YokisClustersDefinition['manuSpecificYokisStats']), // Pending implementation - m.light({configureReporting: true, powerOnBehavior: false}), // StartupOnOff is not supported, TODO: review dimmer cluster instead + m.light({ + effect: false, // related to the identify cluster + configureReporting: true, + powerOnBehavior: false, + }), // StartupOnOff is not supported, TODO: review dimmer cluster instead m.identify(), ...yokisLightControlExtend, ...YokisSubSystemExtend, @@ -2442,18 +2479,18 @@ const definitions: DefinitionWithExtend[] = [ vendor: 'YOKIS', description: 'Flush-mounted independent 2-channel transmitter (main powered)', extend: [ - deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), - deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), - deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), - deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), - deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation - deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), - deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation - deviceEndpoints({endpoints: {'1': 1, '2': 2}}), - identify(), - commandsOnOff(), - commandsLevelCtrl(), - commandsWindowCovering(), + m.deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), + m.deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), + m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), + m.deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), + m.deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation + m.deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), + m.deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation + m.deviceEndpoints({endpoints: {'1': 1, '2': 2}}), + m.identify(), + m.commandsOnOff(), + m.commandsLevelCtrl(), + m.commandsWindowCovering(), // ...YokisDeviceExtend, // ...YokisInputExtend, // ...YokisChannelExtend, @@ -2492,18 +2529,18 @@ const definitions: DefinitionWithExtend[] = [ vendor: 'YOKIS', description: 'Flush-mounted independent 4-channel transmitter (with antenna)', extend: [ - deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), - deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), - deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), - deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), - deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation - deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), - deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation - deviceEndpoints({endpoints: {'1': 1, '2': 2, '3': 3, '4': 4}}), - identify(), - commandsOnOff(), - commandsLevelCtrl(), - commandsWindowCovering(), + m.deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), + m.deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']), + m.deviceAddCustomCluster('manuSpecificYokisLightControl', YokisClustersDefinition['manuSpecificYokisLightControl']), + m.deviceAddCustomCluster('manuSpecificYokisDimmer', YokisClustersDefinition['manuSpecificYokisDimmer']), + m.deviceAddCustomCluster('manuSpecificYokisWindowCovering', YokisClustersDefinition['manuSpecificYokisWindowCovering']), // Pending implementation + m.deviceAddCustomCluster('manuSpecificYokisChannel', YokisClustersDefinition['manuSpecificYokisChannel']), + m.deviceAddCustomCluster('manuSpecificYokisPilotWire', YokisClustersDefinition['manuSpecificYokisPilotWire']), // Pending implementation + m.deviceEndpoints({endpoints: {'1': 1, '2': 2, '3': 3, '4': 4}}), + m.identify(), + m.commandsOnOff(), + m.commandsLevelCtrl(), + m.commandsWindowCovering(), // ...YokisDeviceExtend, // ...YokisInputExtend, // ...YokisChannelExtend, From cfe8d63b26558dd105e719d47cb29cdb3ad283ad Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Sat, 1 Feb 2025 14:22:28 +0100 Subject: [PATCH 3/5] Adding MTR1300EB as part of MTR1300E definition (#15) --- src/devices/yokis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/yokis.ts b/src/devices/yokis.ts index 96055a42e85e7..86a229e2f86f5 100644 --- a/src/devices/yokis.ts +++ b/src/devices/yokis.ts @@ -2342,7 +2342,7 @@ const definitions: DefinitionWithExtend[] = [ }, { // MTR1300E-UP - zigbeeModel: ['MTR1300E-UP'], + zigbeeModel: ['MTR1300E-UP', 'MTR1300EB-UP'], model: 'MTR1300E-UP', vendor: 'YOKIS', description: 'Remote power switch with timer 1300W', From 2268377a557cf3ca2c77f5e10f2b110163fed685 Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Sat, 1 Feb 2025 14:30:28 +0100 Subject: [PATCH 4/5] Removing logger --- src/devices/yokis.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/devices/yokis.ts b/src/devices/yokis.ts index 86a229e2f86f5..0cd796271984d 100644 --- a/src/devices/yokis.ts +++ b/src/devices/yokis.ts @@ -1428,8 +1428,6 @@ const yokisTz = { function YokisOnOff(args?: m.OnOffArgs): ModernExtend { const result: ModernExtend = {...m.onOff(args), toZigbee: [yokisTz.on_off]}; - logger.debug(`YokisOnOff: ${JSON.stringify(result)}`, NS); - return result; } From 32c85fd564293f998024394e608aac811657fbcb Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Sun, 2 Feb 2025 16:50:14 +0100 Subject: [PATCH 5/5] Simplified toZigbee converter override --- src/devices/yokis.ts | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/devices/yokis.ts b/src/devices/yokis.ts index 0cd796271984d..c0659b04ee3dd 100644 --- a/src/devices/yokis.ts +++ b/src/devices/yokis.ts @@ -1,6 +1,7 @@ import {Zcl} from 'zigbee-herdsman'; import {ClusterDefinition} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; +import tz from '../converters/toZigbee'; import * as exposes from '../lib/exposes'; import {logger} from '../lib/logger'; import * as m from '../lib/modernExtend'; @@ -1406,28 +1407,13 @@ const yokisCommandsExtend = { // Dereferencing the `on_time` and `off_wait_time` from the keys of the converter. const yokisTz = { on_off: { + ...tz.on_off, key: ['state'], - convertSet: async (entity, key, value, meta) => { - const state = utils.isString(meta.message.state) ? meta.message.state.toLowerCase() : null; - utils.validateValue(state, ['toggle', 'off', 'on']); - - await entity.command('genOnOff', state, {}, utils.getOptions(meta.mapped, entity)); - if (state === 'toggle') { - const currentState = meta.state[`state${meta.endpoint_name ? `_${meta.endpoint_name}` : ''}`]; - return currentState ? {state: {state: currentState === 'OFF' ? 'ON' : 'OFF'}} : {}; - } else { - return {state: {state: state.toUpperCase()}}; - } - }, - convertGet: async (entity, key, meta) => { - await entity.read('genOnOff', ['onOff']); - }, } satisfies Tz.Converter, }; function YokisOnOff(args?: m.OnOffArgs): ModernExtend { const result: ModernExtend = {...m.onOff(args), toZigbee: [yokisTz.on_off]}; - return result; } @@ -2344,6 +2330,14 @@ const definitions: DefinitionWithExtend[] = [ model: 'MTR1300E-UP', vendor: 'YOKIS', description: 'Remote power switch with timer 1300W', + whiteLabel: [ + { + model: 'MTR1300EB-UP', + vendor: 'YOKIS', + description: 'Remote power switch with timer 1300W', + fingerprint: [{modelID: 'MTR1300EB-UP'}], + }, + ], extend: [ m.deviceAddCustomCluster('manuSpecificYokisDevice', YokisClustersDefinition['manuSpecificYokisDevice']), m.deviceAddCustomCluster('manuSpecificYokisInput', YokisClustersDefinition['manuSpecificYokisInput']),