From feaf578fa776a03e09d96149b3c00d09a390b83a Mon Sep 17 00:00:00 2001 From: kbullet Date: Wed, 25 Dec 2024 02:53:34 +0700 Subject: [PATCH 1/9] feat(add): Z111PL0H-1JX (#8503) Co-authored-by: Koen Kanters --- src/devices/sonoff.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/devices/sonoff.ts b/src/devices/sonoff.ts index 3cb81c7da007d..fd1bcc6bbbb29 100644 --- a/src/devices/sonoff.ts +++ b/src/devices/sonoff.ts @@ -950,13 +950,21 @@ const definitions: DefinitionWithExtend[] = [ exposes: [e.cover_position(), e.battery()], }, { - zigbeeModel: ['Z111PL0H-1JX', 'SA-029-1', 'SA-028-1'], + zigbeeModel: ['SA-029-1', 'SA-028-1'], model: 'SA-028/SA-029', vendor: 'SONOFF', whiteLabel: [{vendor: 'Woolley', model: 'SA-029-1'}], description: 'Smart Plug', extend: [onOff(), forcePowerSource({powerSource: 'Mains (single phase)'})], }, + { + zigbeeModel: ['Z111PL0H-1JX'], + model: 'Z111PL0H-1JX', + vendor: 'SONOFF', + whiteLabel: [{vendor: 'Woolley', model: 'SA-028-1'}], + description: 'Smart Plug', + extend: [onOff({powerOnBehavior: false}), forcePowerSource({powerSource: 'Mains (single phase)'})], + }, { zigbeeModel: ['SNZB-01P'], model: 'SNZB-01P', From ead17e18f26e824305face85c8b5338de11a7937 Mon Sep 17 00:00:00 2001 From: Cosik Date: Tue, 24 Dec 2024 20:53:55 +0100 Subject: [PATCH 2/9] fix: Fix mode enum values for TRV603 (#8512) --- src/devices/tuya.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/tuya.ts b/src/devices/tuya.ts index c39cd28830151..424517de6b068 100644 --- a/src/devices/tuya.ts +++ b/src/devices/tuya.ts @@ -5381,7 +5381,7 @@ const definitions: DefinitionWithExtend[] = [ 'mode', tuya.valueConverterBasic.lookup({ auto: tuya.enum(0), - manual: tuya.enum(1), + manual: tuya.enum(2), }), ], [4, 'current_heating_setpoint', tuya.valueConverter.divideBy10], From 0a714e0a2172222a67c616c2b3799334717d04f8 Mon Sep 17 00:00:00 2001 From: Niracler Li Date: Wed, 25 Dec 2024 03:58:00 +0800 Subject: [PATCH 3/9] feat(add): SR-ZG9030A-MW (#8509) --- src/devices/sunricher.ts | 196 +++++++++++++++++++++++++++++++++++++++ src/lib/sunricher.ts | 13 +++ test/index.test.js | 4 + 3 files changed, 213 insertions(+) create mode 100644 src/lib/sunricher.ts diff --git a/src/devices/sunricher.ts b/src/devices/sunricher.ts index 701a795d11053..66b32fa5e284c 100644 --- a/src/devices/sunricher.ts +++ b/src/devices/sunricher.ts @@ -13,17 +13,20 @@ import { commandsScenes, deviceEndpoints, electricityMeter, + enumLookup, humidity, iasZoneAlarm, identify, illuminance, light, + numeric, occupancy, onOff, temperature, } from '../lib/modernExtend'; import * as reporting from '../lib/reporting'; import * as globalStore from '../lib/store'; +import * as sunricher from '../lib/sunricher'; import {Configure, DefinitionWithExtend, Expose, Fz, ModernExtend, Tz, Zh} from '../lib/types'; import * as utils from '../lib/utils'; @@ -362,6 +365,195 @@ async function syncTime(endpoint: Zh.Endpoint) { } const definitions: DefinitionWithExtend[] = [ + { + zigbeeModel: ['ZG9030A-MW'], + model: 'SR-ZG9030A-MW', + vendor: 'Sunricher', + description: 'Zigbee compatible ceiling mount occupancy sensor', + extend: [ + numeric({ + name: 'light_pwm_frequency', + cluster: 'genBasic', + attribute: {ID: 0x9001, type: 0x21}, + valueMin: 0, + valueMax: 65535, + description: 'Light PWM frequency (0-65535, default: 3300)', + access: 'ALL', + }), + enumLookup({ + name: 'brightness_curve', + cluster: 'genBasic', + attribute: {ID: 0x8806, type: 0x20}, + lookup: { + linear: 0, + gamma_logistics_1_5: 0x0f, + gamma_logistics_1_8: 0x12, + }, + description: 'Brightness curve (default: Linear)', + access: 'ALL', + }), + enumLookup({ + name: 'start_up_on_off', + cluster: 'genOnOff', + attribute: {ID: 0x4003, type: 0x30}, + lookup: { + last_state: 0xff, + on: 1, + off: 0, + }, + description: 'Start up on/off (default: last_state)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_light_duration', + cluster: 'genBasic', + attribute: {ID: 0x8902, type: 0x21}, + valueMin: 0, + valueMax: 65535, + unit: 's', + description: 'Motion sensor light duration (0s-65535s, default: 5s)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_light_sensitivity', + cluster: 'genBasic', + attribute: {ID: 0x8903, type: 0x21}, + valueMin: 0, + valueMax: 255, + description: 'Motion sensor light sensitivity (0-255, default: 0)', + access: 'ALL', + }), + enumLookup({ + name: 'motion_sensor_working_mode', + cluster: 'genBasic', + attribute: {ID: 0x8904, type: 0x20}, + lookup: { + automatic: 0, + manual: 1, + }, + description: 'Motion sensor working mode (default: Automatic)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_sensing_distance', + cluster: 'genBasic', + attribute: {ID: 0x8905, type: 0x20}, + valueMin: 0, + valueMax: 15, + description: 'Motion sensor sensing distance (0-15, default: 1)', + access: 'ALL', + }), + enumLookup({ + name: 'motion_sensor_microwave_switch', + cluster: 'genBasic', + attribute: {ID: 0x8906, type: 0x20}, + lookup: { + on: 1, + off: 0, + }, + description: 'Motion sensor microwave switch (default: On)', + access: 'ALL', + }), + enumLookup({ + name: 'motion_sensor_onoff_broadcast', + cluster: 'genBasic', + attribute: {ID: 0x8907, type: 0x20}, + lookup: { + on: 1, + off: 0, + }, + description: 'Motion sensor on/off broadcast (default: On)', + access: 'ALL', + }), + enumLookup({ + name: 'motion_sensor_light_state', + cluster: 'genBasic', + attribute: {ID: 0x890c, type: 0x20}, + lookup: { + on: 1, + off: 0, + }, + description: 'Motion sensor light state (default: On)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_in_pwm_brightness', + cluster: 'genBasic', + attribute: {ID: 0x8908, type: 0x21}, + valueMin: 0, + valueMax: 1000, + unit: 'lux', + description: 'Motion sensor IN PWM brightness (0-1000 lux, default: 0)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_in_pwm_output', + cluster: 'genBasic', + attribute: {ID: 0x8909, type: 0x20}, + valueMin: 0, + valueMax: 254, + description: 'Motion sensor IN PWM output (0-254, default: 254)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_leave_pwm_output', + cluster: 'genBasic', + attribute: {ID: 0x890a, type: 0x20}, + valueMin: 0, + valueMax: 100, + unit: '%', + description: 'Motion sensor LEAVE PWM output (0%-100%, default: 0%)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_leave_delay', + cluster: 'genBasic', + attribute: {ID: 0x8901, type: 0x21}, + valueMin: 0, + valueMax: 65535, + unit: 's', + description: 'Motion sensor LEAVE delay (0s-65535s, default: 0s)', + access: 'ALL', + }), + numeric({ + name: 'motion_sensor_pwm_output_after_delay', + cluster: 'genBasic', + attribute: {ID: 0x890b, type: 0x20}, + valueMin: 0, + valueMax: 100, + unit: '%', + description: 'Motion sensor PWM output after delay (0%-100%, default: 0%)', + access: 'ALL', + }), + numeric({ + name: 'linear_error_ratio_coefficient_of_lux_measurement', + cluster: 'genBasic', + attribute: {ID: 0x890d, type: 0x21}, + valueMin: 100, + valueMax: 10000, + description: 'Linear error ratio coefficient of LUX measurement (100‰-10000‰, default: 1000‰)', + access: 'ALL', + }), + numeric({ + name: 'fixed_deviation_of_lux_measurement', + cluster: 'genBasic', + attribute: {ID: 0x890e, type: 0x29}, + valueMin: -100, + valueMax: 100, + description: 'Fixed deviation of LUX measurement (-100~100, default: 0)', + access: 'ALL', + }), + deviceEndpoints({endpoints: {'1': 1, '2': 2, '3': 3}}), + light(), + occupancy(), + illuminance({endpointNames: ['3']}), + commandsOnOff(), + commandsLevelCtrl(), + ], + meta: {multiEndpoint: true}, + toZigbee: [sunricher.tz.setModel], + exposes: [e.enum('model', ea.SET, ['HK-DIM', 'ZG9030A-MW']).withDescription('Model of the device')], + }, { zigbeeModel: ['HK-ZRC-K5&RS-E'], model: 'SR-ZG2836D5-Pro', @@ -708,6 +900,10 @@ const definitions: DefinitionWithExtend[] = [ description: 'LED dimmable driver', extend: [light()], whiteLabel: [{vendor: 'Yphix', model: '50208702'}], + toZigbee: [sunricher.tz.setModel], + // Some ZG9030A-MW devices were mistakenly set with the modelId HK-DIM during manufacturing. + // This allows users to update the modelId from HK-DIM to ZG9030A-MW to ensure proper device functionality. + exposes: [e.enum('model', ea.SET, ['HK-DIM', 'ZG9030A-MW']).withDescription('Model of the device')], }, { zigbeeModel: ['SR-ZG9040A-S'], diff --git a/src/lib/sunricher.ts b/src/lib/sunricher.ts new file mode 100644 index 0000000000000..5644a76269c1a --- /dev/null +++ b/src/lib/sunricher.ts @@ -0,0 +1,13 @@ +import {Tz} from './types'; + +const tz = { + setModel: { + key: ['model'], + convertSet: async (entity, key, value, meta) => { + await entity.write('genBasic', {modelId: value}); + return {state: {model: value}}; + }, + } satisfies Tz.Converter, +}; + +export {tz}; diff --git a/test/index.test.js b/test/index.test.js index 7a00fa1fd4e2e..87d054402db0b 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -13,6 +13,7 @@ import { removeExternalDefinitions, } from '../src/index'; import {access as _access, enum as _enum, list as _list, composite, numeric, presets} from '../src/lib/exposes'; +import * as sunricher from '../src/lib/sunricher'; import {tz} from '../src/lib/tuya'; import {getFromLookup, toNumber} from '../src/lib/utils'; import {COLORTEMP_RANGE_MISSING_ALLOWED} from './colortemp_range_missing_allowed'; @@ -421,6 +422,9 @@ describe('index.js', () => { // tuya.tz.datapoints is generic, keys cannot be used to determine expose access if (device.toZigbee.includes(tz.datapoints)) return; + // sunricher.tz.setModel is used to switch modelId for devices with conflicting modelId, skip expose access check + if (device.toZigbee.includes(sunricher.tz.setModel)) return; + const toCheck = []; const expss = typeof device.exposes == 'function' ? device.exposes() : device.exposes; for (const expose of expss) { From c146ce80802eb0aa6fe2f3292279acd73aa36c70 Mon Sep 17 00:00:00 2001 From: CubeZ2mDeveloper Date: Wed, 25 Dec 2024 03:59:00 +0800 Subject: [PATCH 4/9] feat(add): CK-MG22-JLDJ-01(7015), MYRX25Z-1, AM25B-1-25-ES-E-Z (#8510) --- src/devices/ewelink.ts | 151 ++++++++- src/lib/ewelink.ts | 678 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 824 insertions(+), 5 deletions(-) diff --git a/src/devices/ewelink.ts b/src/devices/ewelink.ts index a98afb338affb..cbeec15656c10 100644 --- a/src/devices/ewelink.ts +++ b/src/devices/ewelink.ts @@ -1,7 +1,20 @@ +import {Zcl} from 'zigbee-herdsman'; + import fz from '../converters/fromZigbee'; +import {modernExtend as ewelinkModernExtend} from '../lib/ewelink'; import * as exposes from '../lib/exposes'; import {logger} from '../lib/logger'; -import {battery, deviceEndpoints, iasZoneAlarm, onOff} from '../lib/modernExtend'; +import { + battery, + deviceAddCustomCluster, + deviceEndpoints, + forcePowerSource, + iasZoneAlarm, + onOff, + setupAttributes, + windowCovering, +} from '../lib/modernExtend'; +import * as reporting from '../lib/reporting'; import {DefinitionWithExtend, Fz} from '../lib/types'; const e = exposes.presets; @@ -163,6 +176,142 @@ const definitions: DefinitionWithExtend[] = [ description: 'Zigbee water sensor', extend: [battery(), iasZoneAlarm({zoneType: 'water_leak', zoneAttributes: ['alarm_1', 'battery_low']})], }, + { + zigbeeModel: ['CK-MG22-JLDJ-01(7015)', 'CK-MG22-Z310EE07DOOYA-01(7015)', 'MYDY25Z-1', 'Grandekor Smart Curtain Grandekor'], + model: 'CK-MG22-JLDJ-01(7015)', + vendor: 'eWeLink', + whiteLabel: [ + {fingerprint: [{modelID: 'CK-MG22-JLDJ-01(7015)'}], vendor: 'eWeLink', model: 'CK-MG22-JLDJ-01(7015)'}, + {fingerprint: [{modelID: 'CK-MG22-Z310EE07DOOYA-01(7015)'}], vendor: 'eWeLink', model: 'CK-MG22-Z310EE07DOOYA-01(7015)'}, + {fingerprint: [{modelID: 'MYDY25Z-1'}], vendor: 'eWeLink', model: 'MYDY25Z-1'}, + {fingerprint: [{modelID: 'Grandekor Smart Curtain Grandekor'}], vendor: 'eWeLink', model: 'Grandekor Smart Curtain Grandekor'}, + ], + description: 'Dooya Curtain', + extend: [ + deviceAddCustomCluster('customClusterEwelink', { + ID: 0xef00, + attributes: {}, + commands: { + protocolData: { + ID: 0, + parameters: [{name: 'data', type: Zcl.BuffaloZclDataType.LIST_UINT8}], + }, + }, + commandsResponse: {}, + }), + forcePowerSource({powerSource: 'Battery'}), + ewelinkModernExtend.ewelinkBattery(), + windowCovering({ + controls: ['lift'], + configureReporting: false, + coverMode: false, + coverInverted: true, + }), + ewelinkModernExtend.ewelinkMotorReverse(), + ewelinkModernExtend.ewelinkMotorMode('customClusterEwelink', 'protocolData'), + ewelinkModernExtend.ewelinkMotorClbByPosition('customClusterEwelink', 'protocolData'), + ewelinkModernExtend.ewelinkReportMotorInfo('customClusterEwelink'), + ewelinkModernExtend.ewelinkMotorSpeed('customClusterEwelink', 'protocolData', 0x00, 0x0e), + ], + configure: async (device, coordinatorEndpoint) => { + const windowCoveringAttributes = [{attribute: 'currentPositionLiftPercentage', min: 0, max: 3600, change: 10}]; + await setupAttributes(device, coordinatorEndpoint, 'closuresWindowCovering', windowCoveringAttributes); + }, + onEvent: async (type, data, device, settings, state) => { + if (type === 'deviceInterview') { + const endpoint = device.getEndpoint(1); + const payloadValue = []; + payloadValue[0] = 0x02; + payloadValue[1] = 0x0e; + payloadValue[2] = 0x00; + // Query the maximum level supported for speed adjustment. + await endpoint.command('customClusterEwelink', 'protocolData', { + data: payloadValue, + }); + } + }, + ota: true, + }, + { + zigbeeModel: ['MYRX25Z-1'], + model: 'MYRX25Z-1', + vendor: 'eWeLink', + whiteLabel: [{fingerprint: [{modelID: 'MYRX25Z-1'}], vendor: 'eWeLink', model: 'MYRX25Z-1'}], + description: 'Reax Curtain', + extend: [ + deviceAddCustomCluster('customClusterEwelink', { + ID: 0xef00, + attributes: {}, + commands: { + protocolData: { + ID: 0, + parameters: [{name: 'data', type: Zcl.BuffaloZclDataType.LIST_UINT8}], + }, + }, + commandsResponse: {}, + }), + forcePowerSource({powerSource: 'Battery'}), + ewelinkModernExtend.ewelinkBattery(), + windowCovering({ + controls: ['lift'], + configureReporting: false, + coverMode: false, + coverInverted: true, + }), + ewelinkModernExtend.ewelinkMotorMode('customClusterEwelink', 'protocolData'), + ewelinkModernExtend.ewelinkMotorClbByPosition('customClusterEwelink', 'protocolData'), + ewelinkModernExtend.ewelinkReportMotorInfo('customClusterEwelink'), + ewelinkModernExtend.ewelinkMotorSpeed('customClusterEwelink', 'protocolData', 0x01, 0x03), + ], + configure: async (device, coordinatorEndpoint) => { + const endpoint = device.getEndpoint(1); + await reporting.bind(endpoint, coordinatorEndpoint, ['customClusterEwelink']); + + const windowCoveringAttributes = [{attribute: 'currentPositionLiftPercentage', min: 0, max: 3600, change: 10}]; + await setupAttributes(device, coordinatorEndpoint, 'closuresWindowCovering', windowCoveringAttributes); + }, + ota: true, + }, + { + zigbeeModel: ['AM25B-1-25-ES-E-Z', 'ZM25-EAZ', 'AM25C-1-25-ES-E-Z'], + model: 'AM25B-1-25-ES-E-Z', + vendor: 'eWeLink', + whiteLabel: [ + {fingerprint: [{modelID: 'AM25B-1-25-ES-E-Z'}], vendor: 'eWeLink', model: 'AM25B-1-25-ES-E-Z'}, + {fingerprint: [{modelID: 'ZM25-EAZ'}], vendor: 'eWeLink', model: 'ZM25-EAZ'}, + {fingerprint: [{modelID: 'AM25C-1-25-ES-E-Z'}], vendor: 'eWeLink', model: 'AM25C-1-25-ES-E-Z'}, + ], + description: 'AK Curtain', + extend: [ + deviceAddCustomCluster('customClusterEwelink', { + ID: 0xef00, + attributes: {}, + commands: { + protocolData: { + ID: 0, + parameters: [{name: 'data', type: Zcl.BuffaloZclDataType.LIST_UINT8}], + }, + }, + commandsResponse: {}, + }), + forcePowerSource({powerSource: 'Battery'}), + ewelinkModernExtend.ewelinkBattery(), + windowCovering({ + controls: ['lift'], + configureReporting: false, + coverMode: false, + coverInverted: true, + }), + ewelinkModernExtend.ewelinkMotorReverse(), + ewelinkModernExtend.ewelinkMotorMode('customClusterEwelink', 'protocolData'), + ewelinkModernExtend.ewelinkMotorClbByPosition('customClusterEwelink', 'protocolData'), + ], + configure: async (device, coordinatorEndpoint) => { + const windowCoveringAttributes = [{attribute: 'currentPositionLiftPercentage', min: 0, max: 3600, change: 10}]; + await setupAttributes(device, coordinatorEndpoint, 'closuresWindowCovering', windowCoveringAttributes); + }, + ota: true, + }, ]; export default definitions; diff --git a/src/lib/ewelink.ts b/src/lib/ewelink.ts index b6b275280ba7e..eb6eee7581de5 100644 --- a/src/lib/ewelink.ts +++ b/src/lib/ewelink.ts @@ -1,6 +1,652 @@ -import {presets} from './exposes'; +import {access, options, presets} from './exposes'; import {battery, setupConfigureForBinding} from './modernExtend'; -import {Configure, Expose, Fz, KeyValueAny, ModernExtend} from './types'; +import {Configure, Expose, Fz, KeyValueAny, ModernExtend, Tz, Zh} from './types'; +import * as utils from './utils'; + +const e = presets; +const ea = access; + +// ====================== Type Or Interface ============================== + +// ======================= Utils ========================================= +const findKeyByValue = (object: object, value: number | string) => { + const entry = Object.entries(object).find(([key, val]) => val === value); + return entry ? entry[0] : undefined; +}; +// ======================= Custom TZ ===================================== +export const ewelinkToZigbee = { + motor_direction: { + key: ['motor_direction'], + convertSet: async (entity, key, value, meta) => { + let windowCoveringMode; + if (value == 'forward') { + windowCoveringMode = 0x00; + } else if (value == 'reverse') { + windowCoveringMode = 0x01; + } + await entity.write('closuresWindowCovering', {windowCoveringMode}, utils.getOptions(meta.mapped, entity)); + return {state: {motor_direction: value}}; + }, + } satisfies Tz.Converter, +}; + +// ======================= Custom FZ ===================================== +export const ewelinkFromZigbee = { + motor_direction: { + cluster: 'closuresWindowCovering', + type: ['attributeReport', 'readResponse'], + options: [options.invert_cover()], + convert: (model, msg, publish, options, meta) => { + const result: KeyValueAny = {}; + if (typeof msg.data === 'object' && Object.prototype.hasOwnProperty.call(msg.data, 'windowCoveringMode')) { + result['motor_direction'] = (msg.data.windowCoveringMode & (1 << 0)) > 0 == true ? 'reverse' : 'forward'; + } + return result; + }, + } satisfies Fz.Converter, +}; + +// ======================= Custom Extend ================================= +function privateMotorClbByPosition(clusterName: string, writeCommand: string): ModernExtend { + const protocol = { + dooya: { + supportModel: ['CK-MG22-Z310EE07DOOYA-01(7015)', 'MYDY25Z-1', 'CK-MG22-JLDJ-01(7015)'], + mapping: { + open: 0x01, + close: 0x02, + other: 0x03, + clear: 0x10, + }, + updateMotorClbCommand: { + privateCmd: 0x01, + subCmd: 0x09, + }, + updatedMotorClbCommand: { + privateCmd: 0x01, + subCmd: 0x09, + }, + deleteMotorClbCommand: { + privateCmd: 0x01, + subCmd: 0x0b, + }, + deletedMotorClbCommand: { + privateCmd: 0x01, + subCmd: 0x0b, + }, + }, + raex: { + supportModel: ['MYRX25Z-1'], + mapping: { + open: 0x01, + close: 0x02, + clear: 0x01, + }, + updateMotorClbCommand: { + privateCmd: 0x22, + dataLength: 0x02, + subCmd: 0x71, + }, + deleteMotorClbCommand: { + privateCmd: 0x22, + dataLength: 0x02, + subCmd: 0x76, + }, + }, + ak: { + supportModel: ['AM25B-1-25-ES-E-Z', 'ZM25-EAZ', 'AM25C-1-25-ES-E-Z'], + mapping: { + open: 0x00, + close: 0x01, + clear: 0x02, + }, + // AK Protocol setting the limit and clearing the limit use the same command. + updateMotorClbCommand: { + cmdType: 0x00, + privateCmd: 0x68, + dataType: 0x04, + dataLength: [0x00, 0x01], + }, + updatedMotorClbCommand: { + cmdType: 0x01, + privateCmd: 0x68, + dataType: 0x04, + }, + }, + }; + const exposes = []; + exposes.push(e.enum('motor_clb_position', ea.SET, ['open', 'close', 'other', 'clear']).withDescription('Motor Calibration By Position')); + + exposes.push(e.text('motor_clb_position_result', ea.STATE).withDescription('Motor Calibration Result')); + + const fromZigbee: Fz.Converter[] = [ + { + cluster: clusterName, + type: ['raw'], + convert: (model, msg, publish, otions, meta) => { + if (msg.type === 'raw' && msg.data instanceof Buffer) { + // Raex Protocol,updated Report only through 'motor_info'. + if (protocol.dooya.supportModel.includes(model.model)) { + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); // The first five bytes belong to the ZCL frame header, which are not of interest here; only the payload is extracted. + const payload = bufferObj.data; + const {privateCmd: updatedPrivateCmd, subCmd: updatedPrivateSubCmd} = protocol.dooya.updatedMotorClbCommand; + const {privateCmd: deletedPrivateCmd, subCmd: deletedPrivateSubCmd} = protocol.dooya.deletedMotorClbCommand; + + if (payload[0] === updatedPrivateCmd && payload[1] === updatedPrivateSubCmd) { + const entities = Object.entries(protocol.dooya.mapping); + const motor_clb_position_result: {[key: string]: string} = {}; + for (const entity of entities) { + if (entity[1] === payload[2]) { + motor_clb_position_result[entity[0] as string] = 'calibrated'; + break; + } + } + + return { + motor_clb_position_result, + }; + } else if (payload[0] === deletedPrivateCmd && payload[1] === deletedPrivateSubCmd) { + if (payload[2] === protocol.dooya.mapping.clear) { + const motor_clb_position_result = { + open: 'uncalibrated', + close: 'uncalibrated', + other: 'uncalibrated', + }; + return { + motor_clb_position_result, + }; + } + } + } else if (protocol.ak.supportModel.includes(model.model)) { + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); // The first five bytes belong to the ZCL frame header, which are not of interest here; only the payload is extracted. + const payload = bufferObj.data; + const {cmdType, privateCmd, dataType} = protocol.ak.updatedMotorClbCommand; + if (payload[0] === cmdType && payload[2] === privateCmd && payload[3] === dataType) { + if (payload[6] === protocol.ak.mapping.clear) { + const motor_clb_position_result = { + open: 'uncalibrated', + close: 'uncalibrated', + other: 'uncalibrated', + }; + return { + motor_clb_position_result, + }; + } else { + const entities = Object.entries(protocol.ak.mapping); + const motor_clb_position_result: {[key: string]: string} = {}; + for (const entity of entities) { + if (entity[1] === payload[6]) { + motor_clb_position_result[entity[0] as string] = 'calibrated'; + break; + } + } + return { + motor_clb_position_result, + }; + } + } + } + } + }, + }, + ]; + + const toZigbee: Tz.Converter[] = [ + { + key: ['motor_clb_position'], + convertSet: async (entity: Zh.Endpoint, key, value, meta) => { + const device: Zh.Device = entity.getDevice(); + const modelID = device.modelID; + + if (protocol.dooya.supportModel.includes(modelID)) { + const {deleteMotorClbCommand, updateMotorClbCommand, mapping} = protocol.dooya; + // Dooya Protocol + const payloadValue = []; + if (value === 'clear') { + // Clear limit postion + payloadValue[0] = deleteMotorClbCommand.privateCmd; + payloadValue[1] = deleteMotorClbCommand.subCmd; + payloadValue[2] = mapping[value as keyof typeof mapping]; + } else if (['open', 'close', 'other'].includes(value as string)) { + // Set limit postion + payloadValue[0] = updateMotorClbCommand.privateCmd; + payloadValue[1] = updateMotorClbCommand.subCmd; + payloadValue[2] = mapping[value as keyof typeof mapping]; + } + + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } else if (protocol.raex.supportModel.includes(modelID)) { + const {deleteMotorClbCommand, updateMotorClbCommand, mapping} = protocol.raex; + // Raex Protocol + const payloadValue = []; + if (value === 'clear') { + // Clear limit postion + payloadValue[0] = deleteMotorClbCommand.privateCmd; + payloadValue[1] = deleteMotorClbCommand.dataLength; + payloadValue[2] = deleteMotorClbCommand.subCmd; + payloadValue[3] = mapping[value as keyof typeof mapping]; + } else if (['open', 'close', 'other'].includes(value as string)) { + // Set limit postion + payloadValue[0] = updateMotorClbCommand.privateCmd; + payloadValue[1] = updateMotorClbCommand.dataLength; + payloadValue[2] = updateMotorClbCommand.subCmd; + payloadValue[3] = mapping[value as keyof typeof mapping]; + } + + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } else if (protocol.ak.supportModel.includes(modelID)) { + // AK Protocol + const {updateMotorClbCommand, mapping} = protocol.ak; + const payloadValue = []; + payloadValue[0] = updateMotorClbCommand.cmdType; + payloadValue[1] = 0x00; + payloadValue[2] = updateMotorClbCommand.privateCmd; + payloadValue[3] = updateMotorClbCommand.dataType; + payloadValue.push(...updateMotorClbCommand.dataLength); + payloadValue[6] = mapping[value as keyof typeof mapping]; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } + return {state: {[key]: value}}; + }, + }, + ]; + + return {exposes, toZigbee, fromZigbee, isModernExtend: true}; +} + +function privateMotorMode(clusterName: string, writeCommand: string): ModernExtend { + const mode = ['inching', 'continuou']; + const protocol = { + dooya: { + supportModel: ['CK-MG22-Z310EE07DOOYA-01(7015)', 'MYDY25Z-1', 'CK-MG22-JLDJ-01(7015)'], + mapping: { + continuou: 0x20, + inching: 0x30, + }, + updateMotorModeCommand: { + privateCmd: 0x01, + subCmd: 0x10, + }, + updatedMotorModeCommand: { + privateCmd: 0x01, + subCmd: 0x10, + }, + }, + raex: { + supportModel: ['MYRX25Z-1'], + mapping: { + continuou: 0x01, + inching: 0x02, + }, + updateMotorModeCommand: { + privateCmd: 0x11, + dataLength: 0x02, + subCmd: 0x54, + }, + }, + ak: { + supportModel: ['AM25B-1-25-ES-E-Z', 'ZM25-EAZ', 'AM25C-1-25-ES-E-Z'], + mapping: { + continuou: 0x00, + inching: 0x01, + }, + updateMotorModeCommand: { + cmdType: 0x00, + privateCmd: 0x67, + dataType: 0x04, + dataLength: [0x00, 0x01], + }, + updatedMotorModeCommand: { + cmdType: 0x01, + privateCmd: 0x67, + dataType: 0x04, + }, + }, + }; + const expose = e.enum('motor_mode', ea.STATE_SET, mode).withDescription('Motor Mode'); + const fromZigbee: Fz.Converter[] = [ + { + cluster: clusterName, + type: ['raw'], + convert: (model, msg, publish, otions, meta) => { + if (msg.type === 'raw' && msg.data instanceof Buffer) { + if (protocol.dooya.supportModel.includes(model.model)) { + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); + const payload = bufferObj.data; + const {privateCmd, subCmd} = protocol.dooya.updatedMotorModeCommand; + + if (payload[0] === privateCmd && payload[1] === subCmd) { + const entities = Object.entries(protocol.dooya.mapping); + let motor_mode; + for (const entity of entities) { + if (entity[1] === payload[2]) { + motor_mode = entity[0]; + break; + } + } + return { + motor_mode, + }; + } + } else if (protocol.ak.supportModel.includes(model.model)) { + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); + const payload = bufferObj.data; + const {cmdType, privateCmd, dataType} = protocol.ak.updatedMotorModeCommand; + if (payload[0] === cmdType && payload[2] === privateCmd && payload[3] === dataType) { + const entities = Object.entries(protocol.ak.mapping); + let motor_mode; + for (const entity of entities) { + if (entity[1] === payload[6]) { + motor_mode = entity[0]; + break; + } + } + return { + motor_mode, + }; + } + } + } + }, + }, + ]; + + const toZigbee: Tz.Converter[] = [ + { + key: ['motor_mode'], + convertSet: async (entity: Zh.Endpoint, key, value, meta) => { + const device: Zh.Device = entity.getDevice(); + const modelID = device.modelID; + + if (protocol.dooya.supportModel.includes(modelID)) { + // Dooya Protocol + const payloadValue = []; + const {updateMotorModeCommand, mapping} = protocol.dooya; + payloadValue[0] = updateMotorModeCommand.privateCmd; + payloadValue[1] = updateMotorModeCommand.subCmd; + payloadValue[2] = mapping[value as keyof typeof mapping]; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } else if (protocol.raex.supportModel.includes(modelID)) { + // Raex Protocol + const payloadValue = []; + const {updateMotorModeCommand, mapping} = protocol.raex; + payloadValue[0] = updateMotorModeCommand.privateCmd; + payloadValue[1] = updateMotorModeCommand.dataLength; + payloadValue[2] = updateMotorModeCommand.subCmd; + payloadValue[3] = mapping[value as keyof typeof mapping]; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } else if (protocol.ak.supportModel.includes(modelID)) { + // AK Protocol + const payloadValue = []; + const {updateMotorModeCommand, mapping} = protocol.ak; + payloadValue[0] = updateMotorModeCommand.cmdType; + payloadValue[1] = 0x00; + payloadValue[2] = updateMotorModeCommand.privateCmd; + payloadValue[3] = updateMotorModeCommand.dataType; + payloadValue.push(...updateMotorModeCommand.dataLength); + payloadValue[6] = mapping[value as keyof typeof mapping]; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } + + return {state: {[key]: value}}; + }, + }, + ]; + + return {exposes: [expose], toZigbee, fromZigbee, isModernExtend: true}; +} + +function privateReportMotorInfo(clusterName: string): ModernExtend { + const protocol = { + dooya: { + supportModel: ['CK-MG22-Z310EE07DOOYA-01(7015)', 'MYDY25Z-1', 'CK-MG22-JLDJ-01(7015)', 'Grandekor Smart Curtain Grandekor'], + mapping: { + status: { + open: 0x01, + close: 0x02, + stop: 0x03, + }, + itinerary: { + none: 0x00, + all: 0x01, + hasOpen: 0x02, + hasClose: 0x03, + hasThird: 0x04, + }, + speed: { + none: 0x00, + T1: 0x01, + T2: 0x02, + T3: 0x03, + T4: 0x04, + T5: 0x05, + T6: 0x06, + T7: 0x07, + T8: 0x08, + T9: 0x09, + T10: 0x0a, + T11: 0x0b, + T12: 0x0c, + T13: 0x0d, + T14: 0x0e, + }, + motorDirection: { + forward: 0x01, + reverse: 0x02, + }, + motorMode: { + continuou: 0x01, + inching: 0x02, + }, + }, + updatedMotorInfoCommand: { + privateCmd: 0x03, + subCmd: 0x01, + }, + }, + raex: { + supportModel: ['MYRX25Z-1'], + mapping: { + status: { + open: '01', + close: '10', + stop: '00', + }, + itinerary: { + none: 0x00, + hasOpen: 0x01, + hasClose: 0x02, + all: 0x03, + }, + speed: { + none: 0x00, + T1: 0x01, + T2: 0x02, + T3: 0x03, + }, + motorDirection: { + forward: '0', + reverse: '1', + }, + motorMode: { + continuou: '0', + inching: '1', + }, + }, + updatedMotorInfoCommand: { + privateCmd: 0xa1, + subCmd: 0x0c, + }, + }, + }; + + const expose = e.text('motor_info', ea.STATE).withDescription('Motor Updated Info'); + const fromZigbee: Fz.Converter[] = [ + { + cluster: clusterName, + type: ['raw'], + convert: (model, msg, publish, otions, meta) => { + if (msg.type === 'raw' && msg.data instanceof Buffer) { + if (protocol.dooya.supportModel.includes(model.model)) { + // Dooya Protocol + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); + const payload = bufferObj.data; + const dooyaProtocol = protocol.dooya; + const {privateCmd, subCmd} = protocol.dooya.updatedMotorInfoCommand; + + if (payload[0] === privateCmd && payload[1] === subCmd) { + const motor_status = findKeyByValue(dooyaProtocol.mapping.status, payload[2]); + const motor_percentage = payload[3]; + const motor_angle = payload[4]; + const motor_itinerary = findKeyByValue(dooyaProtocol.mapping.itinerary, payload[5]); + const motor_speed = payload[6]; + const motor_direction = findKeyByValue(dooyaProtocol.mapping.motorDirection, payload[7]); + const motor_mode = findKeyByValue(dooyaProtocol.mapping.motorMode, payload[8]); + const battery = payload[9]; + return { + [expose.property]: { + motor_status, + motor_percentage, + motor_angle, + motor_itinerary, + motor_speed, + motor_direction, + motor_mode, + battery, + }, + }; + } + } else if (protocol.raex.supportModel.includes(model.model)) { + // Raex Protocol + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); + const payload = bufferObj.data; + const raexProtocol = protocol.raex; + const {privateCmd, subCmd} = raexProtocol.updatedMotorInfoCommand; + + if (payload[0] === privateCmd && payload[1] === subCmd) { + const motor_status_binary = payload[2].toString(2).padStart(8, '0').slice(6, 8); + const motor_status = findKeyByValue(raexProtocol.mapping.status, motor_status_binary); + const motor_percentage = payload[3]; // 255 indicates that the motor cannot find the percentage. + const motor_angle = payload[4]; // 255 indicates that the motor cannot find the angle + const battery = payload[5]; + const motor_itinerary = findKeyByValue(raexProtocol.mapping.itinerary, payload[11]); + const motor_speed = payload[9]; + const motor_direction_binary = payload[8].toString(2).padStart(8, '0').slice(6, 7); + const motor_direction = findKeyByValue(raexProtocol.mapping.motorDirection, motor_direction_binary); + const motor_mode_binary = payload[8].toString(2).padStart(8, '0').slice(5, 6); + const motor_mode = findKeyByValue(raexProtocol.mapping.motorMode, motor_mode_binary); + return { + [expose.property]: { + motor_status, + motor_percentage, + motor_angle, + motor_itinerary, + motor_speed, + motor_direction, + motor_mode, + battery, + }, + }; + } + } + } + }, + }, + ]; + return {exposes: [expose], fromZigbee, isModernExtend: true}; +} + +function privateMotorSpeed(clusterName: string, writeCommand: string, minSpeed: number, maxSpeed: number): ModernExtend { + const protocol = { + dooya: { + supportModel: ['CK-MG22-Z310EE07DOOYA-01(7015)', 'MYDY25Z-1', 'CK-MG22-JLDJ-01(7015)', 'Grandekor Smart Curtain Grandekor'], + updateMotorSpeedCommand: { + privateCmd: 0x01, + subCmd: 0xd1, + }, + updatedMotorSpeedCommand: { + privateCmd: 0x01, + subCmd: 0xd1, + }, + updatedMaxMotorSpeedCommand: { + privateCmd: 0x02, + subCmd: 0x0e, + }, + }, + raex: { + supportModel: ['MYRX25Z-1'], + updateMotorSpeedCommand: { + privateCmd: 0x11, + dataLength: 0x02, + subCmd: 0x53, + }, + }, + }; + + const exposes = []; + exposes.push(e.numeric('motor_speed', ea.STATE_SET).withDescription('Set the motor speed').withValueMin(minSpeed).withValueMax(maxSpeed)); + exposes.push(e.numeric('supported_max_motor_speed', ea.STATE).withDescription('Supported max motor speed')); + + const fromZigbee: Fz.Converter[] = [ + { + cluster: clusterName, + type: ['raw'], + convert: (model, msg, publish, otions, meta) => { + if (msg.type === 'raw' && msg.data instanceof Buffer) { + if (protocol.dooya.supportModel.includes(model.model)) { + const bufferObj = msg.data.subarray(3, msg.data.length).toJSON(); + const payload = bufferObj.data; + const {updatedMotorSpeedCommand, updatedMaxMotorSpeedCommand} = protocol.dooya; + + if (payload[0] === updatedMotorSpeedCommand.privateCmd && payload[1] === updatedMotorSpeedCommand.subCmd) { + return { + motor_speed: payload[2], // If the gear position is 255, it means the device does not support speed adjustment. + }; + } else if (payload[0] === updatedMaxMotorSpeedCommand.privateCmd && payload[1] === updatedMaxMotorSpeedCommand.subCmd) { + const supportedMax = payload[2]; + if (supportedMax === 0 || supportedMax === undefined) { + return { + supported_max_motor_speed: 0, + }; + } + return {supported_max_motor_speed: supportedMax}; + } + } + } + }, + }, + ]; + + const toZigbee: Tz.Converter[] = [ + { + key: ['motor_speed'], + convertSet: async (entity: Zh.Endpoint, key, value, meta) => { + const device: Zh.Device = entity.getDevice(); + const modelID = device.modelID; + + if (protocol.dooya.supportModel.includes(modelID)) { + const payloadValue = []; + const {updateMotorSpeedCommand} = protocol.dooya; + payloadValue[0] = updateMotorSpeedCommand.privateCmd; + payloadValue[1] = updateMotorSpeedCommand.subCmd; + payloadValue[2] = value; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } else if (protocol.raex.supportModel.includes(modelID)) { + const payloadValue = []; + const {updateMotorSpeedCommand} = protocol.raex; + payloadValue[0] = updateMotorSpeedCommand.privateCmd; + payloadValue[1] = updateMotorSpeedCommand.dataLength; + payloadValue[2] = updateMotorSpeedCommand.subCmd; + payloadValue[3] = value; + await entity.command(clusterName, writeCommand, {data: payloadValue}); + } + + return {state: {[key]: value}}; + }, + }, + ]; + + return {exposes, toZigbee, fromZigbee, isModernExtend: true}; +} export const ewelinkModernExtend = { ewelinkAction: (): ModernExtend => { @@ -27,10 +673,34 @@ export const ewelinkModernExtend = { return battery({ voltage: true, voltageReporting: true, - percentageReportingConfig: {min: 3600, max: 7200, change: 10}, - voltageReportingConfig: {min: 3600, max: 7200, change: 10}, + percentageReportingConfig: {min: 3600, max: 7200, change: 2}, + voltageReportingConfig: {min: 3600, max: 7200, change: 100}, }); }, + ewelinkMotorReverse: (): ModernExtend => { + const exposes = [e.enum('motor_direction', ea.STATE_SET, ['forward', 'reverse']).withDescription('Set the motor direction')]; + const toZigbee: Tz.Converter[] = [ewelinkToZigbee.motor_direction]; + const fromZigbee: Fz.Converter[] = [ewelinkFromZigbee.motor_direction]; + + return { + exposes, + fromZigbee, + toZigbee, + isModernExtend: true, + }; + }, + ewelinkMotorClbByPosition: (clusterName: string, writeCommand: string): ModernExtend => { + return privateMotorClbByPosition(clusterName, writeCommand); + }, + ewelinkMotorMode: (clusterName: string, writeCommand: string): ModernExtend => { + return privateMotorMode(clusterName, writeCommand); + }, + ewelinkReportMotorInfo: (clusterName: string): ModernExtend => { + return privateReportMotorInfo(clusterName); + }, + ewelinkMotorSpeed: (clusterName: string, writeCommand: string, min: number, max: number): ModernExtend => { + return privateMotorSpeed(clusterName, writeCommand, min, max); + }, }; export {ewelinkModernExtend as modernExtend}; From 0f85431313291d5fe3cf07819ce211bd79fc893a Mon Sep 17 00:00:00 2001 From: Nopraz <12595433+Nopraz@users.noreply.github.com> Date: Tue, 24 Dec 2024 21:00:09 +0100 Subject: [PATCH 5/9] fix: Fix MAZDA MZV-T1Z `system_mode` (#8487) --- src/devices/mazda.ts | 60 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/devices/mazda.ts b/src/devices/mazda.ts index 5dab5dc3cc238..a40ed86c0c367 100644 --- a/src/devices/mazda.ts +++ b/src/devices/mazda.ts @@ -1,9 +1,13 @@ import * as exposes from '../lib/exposes'; import * as tuya from '../lib/tuya'; -import {DefinitionWithExtend} from '../lib/types'; +import {DefinitionWithExtend, KeyValueNumberString, Tz} from '../lib/types'; +import * as utils from '../lib/utils'; const e = exposes.presets; const ea = exposes.access; +interface KeyValueStringEnum { + [s: string]: tuya.Enum; +} const definitions: DefinitionWithExtend[] = [ { @@ -56,25 +60,55 @@ const definitions: DefinitionWithExtend[] = [ [ 2, 'preset', - tuya.valueConverterBasic.lookup({ - manual: tuya.enum(0), - schedule: tuya.enum(1), - eco: tuya.enum(2), - comfort: tuya.enum(3), - frost_protection: tuya.enum(4), - holiday: tuya.enum(5), - off: tuya.enum(6), - }), + { + from: (v: string) => { + utils.assertNumber(v, 'system_mode'); + const presetLookup: KeyValueNumberString = { + 0: 'manual', + 1: 'schedule', + 2: 'eco', + 3: 'comfort', + 4: 'frost_protection', + 5: 'holiday', + 6: 'off', + }; + return presetLookup[v]; + }, + to: (v: string, meta: Tz.Meta) => { + const lookup: KeyValueStringEnum = { + manual: tuya.enum(0), + schedule: tuya.enum(1), + eco: tuya.enum(2), + comfort: tuya.enum(3), + frost_protection: tuya.enum(4), + holiday: tuya.enum(5), + }; + // Update system_mode when preset changes + if (meta) { + meta.state['system_mode'] = v === 'off' ? 'off' : 'heat'; + } + return utils.getFromLookup(v, lookup); + }, + }, ], [ 2, 'system_mode', { - from: (v) => { + from: (v: tuya.Enum) => { return v === tuya.enum(6) ? 'off' : 'heat'; }, - to: (v) => { - // By default switching to "heat" will activate schedule mode on Homeassistant + to: (v: string, meta: Tz.Meta) => { + if (meta) { + const currentPreset = meta.state['preset']; + if (v === 'heat' && currentPreset === 'off') { + meta.state['preset'] = 'manual'; + return tuya.enum(0); + } else if (v === 'off') { + meta.state['preset'] = 'off'; + return tuya.enum(6); + } + } return v === 'off' ? tuya.enum(6) : tuya.enum(1); }, }, From 223fad439e83893e0956688c822dda83d46210cf Mon Sep 17 00:00:00 2001 From: support <117250319+3reality-support@users.noreply.github.com> Date: Wed, 25 Dec 2024 04:01:26 +0800 Subject: [PATCH 6/9] feat(add): 3RDP01072Z (#8494) Co-authored-by: jintj Co-authored-by: Koen Kanters --- src/devices/third_reality.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/devices/third_reality.ts b/src/devices/third_reality.ts index a8920f99b020b..0620bf709d38c 100644 --- a/src/devices/third_reality.ts +++ b/src/devices/third_reality.ts @@ -3,7 +3,17 @@ import {Zcl} from 'zigbee-herdsman'; import fz from '../converters/fromZigbee'; import tz from '../converters/toZigbee'; import * as exposes from '../lib/exposes'; -import {battery, deviceAddCustomCluster, humidity, iasZoneAlarm, light, onOff, temperature} from '../lib/modernExtend'; +import { + battery, + deviceAddCustomCluster, + deviceEndpoints, + electricityMeter, + humidity, + iasZoneAlarm, + light, + onOff, + temperature, +} from '../lib/modernExtend'; import * as reporting from '../lib/reporting'; import {DefinitionWithExtend, Fz, KeyValue} from '../lib/types'; @@ -406,6 +416,18 @@ const definitions: DefinitionWithExtend[] = [ }), ], }, + { + zigbeeModel: ['3RDP01072Z'], + model: '3RDP01072Z', + vendor: 'Third Reality', + description: 'Zigbee / BLE dual plug with power', + ota: true, + extend: [ + deviceEndpoints({endpoints: {left: 1, right: 2}}), + onOff({endpointNames: ['left', 'right']}), + electricityMeter({acFrequency: true, powerFactor: true, endpointNames: ['left', 'right']}), + ], + }, { zigbeeModel: ['3RVS01031Z'], model: '3RVS01031Z', From 9713bfc592ede1babdcb0e46ec570d9860a34806 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Tue, 24 Dec 2024 21:04:02 +0100 Subject: [PATCH 7/9] fix: Fix Tuya TS011F_2_gang_power gangs not controlled individually https://github.com/Koenkk/zigbee2mqtt/issues/23402 --- src/devices/tuya.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/devices/tuya.ts b/src/devices/tuya.ts index 424517de6b068..f125490946a95 100644 --- a/src/devices/tuya.ts +++ b/src/devices/tuya.ts @@ -3190,6 +3190,7 @@ const definitions: DefinitionWithExtend[] = [ vendor: 'Tuya', description: '2 gang socket with power monitoring and USB', extend: [ + tuyaMagicPacket(), deviceEndpoints({endpoints: {left: 1, right: 2}, multiEndpointSkip: ['current', 'voltage', 'power', 'energy']}), onOff({powerOnBehavior: false, endpointNames: ['left', 'right']}), identify(), From 5210be7d2c45c4d781ee42f4c6cc34b671341ca4 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Tue, 24 Dec 2024 21:07:11 +0100 Subject: [PATCH 8/9] feat(add): 929003823101, 929003822701 https://github.com/Koenkk/zigbee2mqtt/issues/25305 --- src/devices/philips.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/devices/philips.ts b/src/devices/philips.ts index adb724919d7c1..bb5a9af5f53f6 100644 --- a/src/devices/philips.ts +++ b/src/devices/philips.ts @@ -440,6 +440,20 @@ const definitions: DefinitionWithExtend[] = [ description: 'Hue white G125 B22 LED bulb filament giant globe', extend: [philipsLight()], }, + { + zigbeeModel: ['929003823101'], + model: '929003823101', + vendor: 'Philips', + description: 'Hue Tento White 42.1 cm', + extend: [philipsLight({colorTemp: {range: [153, 438]}})], + }, + { + zigbeeModel: ['929003822701'], + model: '929003822701', + vendor: 'Philips', + description: 'Hue Tento White 29.1 cm', + extend: [philipsLight()], + }, { zigbeeModel: ['LTD011'], model: '5110131H5', From dab236f5d8eb8a4f5bfcc3a67f2c3b1cb94cd460 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Tue, 24 Dec 2024 21:08:40 +0100 Subject: [PATCH 9/9] chore(master): release 21.9.0 (#8511) --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ package.json | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 60419e2fab17f..72f825bbe990f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "21.8.0" + ".": "21.9.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 1238d85817514..698c6a17e0506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [21.9.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v21.8.0...v21.9.0) (2024-12-24) + + +### Features + +* **add:** _TZE200_i48qyn9s ([#8505](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8505)) ([3fccb35](https://github.com/Koenkk/zigbee-herdsman-converters/commit/3fccb35678736750b8ee1511fbf200d4afc4cf2b)) +* **add:** 3RDP01072Z ([#8494](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8494)) ([223fad4](https://github.com/Koenkk/zigbee-herdsman-converters/commit/223fad439e83893e0956688c822dda83d46210cf)) +* **add:** 929003823101, 929003822701 https://github.com/Koenkk/zigbee2mqtt/issues/25305 ([5210be7](https://github.com/Koenkk/zigbee-herdsman-converters/commit/5210be7d2c45c4d781ee42f4c6cc34b671341ca4)) +* **add:** CK-MG22-JLDJ-01(7015), MYRX25Z-1, AM25B-1-25-ES-E-Z ([#8510](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8510)) ([c146ce8](https://github.com/Koenkk/zigbee-herdsman-converters/commit/c146ce80802eb0aa6fe2f3292279acd73aa36c70)) +* **add:** SR-ZG9030A-MW ([#8509](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8509)) ([0a714e0](https://github.com/Koenkk/zigbee-herdsman-converters/commit/0a714e0a2172222a67c616c2b3799334717d04f8)) +* **add:** Z111PL0H-1JX ([#8503](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8503)) ([feaf578](https://github.com/Koenkk/zigbee-herdsman-converters/commit/feaf578fa776a03e09d96149b3c00d09a390b83a)) +* Use Vitest for testing ([#8507](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8507)) ([6d740d7](https://github.com/Koenkk/zigbee-herdsman-converters/commit/6d740d7e1b15cdb5a881ef9b69914fe2650e4117)) + + +### Bug Fixes + +* Fix MAZDA MZV-T1Z `system_mode` ([#8487](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8487)) ([0f85431](https://github.com/Koenkk/zigbee-herdsman-converters/commit/0f85431313291d5fe3cf07819ce211bd79fc893a)) +* Fix mode enum values for TRV603 ([#8512](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8512)) ([ead17e1](https://github.com/Koenkk/zigbee-herdsman-converters/commit/ead17e18f26e824305face85c8b5338de11a7937)) +* Fix Tuya TS011F_2_gang_power gangs not controlled individually https://github.com/Koenkk/zigbee2mqtt/issues/23402 ([9713bfc](https://github.com/Koenkk/zigbee-herdsman-converters/commit/9713bfc592ede1babdcb0e46ec570d9860a34806)) +* **ignore:** Nedis thermostat model number typo correction ([#8508](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8508)) ([019479b](https://github.com/Koenkk/zigbee-herdsman-converters/commit/019479b677485cfb5fcadf261e8adcd4b7f33a18)) +* Improve TRV602Z integration ([#8502](https://github.com/Koenkk/zigbee-herdsman-converters/issues/8502)) ([9bb99f0](https://github.com/Koenkk/zigbee-herdsman-converters/commit/9bb99f0c972c5a339c846ac966bf2739d591fe42)) + ## [21.8.0](https://github.com/Koenkk/zigbee-herdsman-converters/compare/v21.7.0...v21.8.0) (2024-12-22) diff --git a/package.json b/package.json index b419550a1f1f3..0c528e8a27566 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zigbee-herdsman-converters", - "version": "21.8.0", + "version": "21.9.0", "description": "Collection of device converters to be used with zigbee-herdsman", "main": "index.js", "types": "index.d.ts",