Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.57k stars 1.63k forks source link

Aqara Smart Curtain Motor C3 #19555

Open hzonz opened 8 months ago

hzonz commented 8 months ago

Link

https://www.aqara.cn/curtain-c3_overview

Database entry

{"id":3,"type":"Router","ieeeAddr":"0x54ef4410009630e0","nwkAddr":59606,"manufId":4447,"manufName":"Aqara","powerSource":"DC Source","modelId":"lumi.curtain.acn04","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":515,"inClusterList":[5,4,3,0,258,64704],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"modelId":"lumi.curtain.acn04","manufacturerName":"Aqara","powerSource":4,"zclVersion":3,"appVersion":9,"stackVersion":27,"hwVersion":1,"dateCode":"20230407"}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":9,"stackVersion":27,"hwVersion":1,"dateCode":"20230407","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1699081757490,"defaultSendRequestWhen":"immediate"}

Comments

Hello, Aqara has recently released the smart curtain motor C3, can this device be supported in the new version.

External converter

No response

Supported color modes

No response

Color temperature range

No response

latel commented 8 months ago

I have this device too

i-93 commented 6 months ago

+1

Please add the support for it. I am ready to help with debugging if needed.

i-93 commented 6 months ago

Tried to follow the guide. Created the file ZNCLDJ01LM.js based on the closest Aqara C2 code.

` const {} = require('zigbee-herdsman-converters/lib/modernExtend');

const fz = require('zigbee-herdsman-converters/converters/fromZigbee'); const tz = require('zigbee-herdsman-converters/converters/toZigbee'); const exposes = require('zigbee-herdsman-converters/lib/exposes'); const ota = require('zigbee-herdsman-converters/lib/ota'); const xiaomi = require('zigbee-herdsman-converters/lib/xiaomi'); const e = exposes.presets; const ea = exposes.access;

const definition = { zigbeeModel: ['lumi.curtain.acn04'], model: 'ZNCLDJ01LM', vendor: 'Xiaomi', description: 'Aqara C3 curtain motor', fromZigbee: [xiaomi.fromZigbee.xiaomi_basic, fz.xiaomi_curtain_position, fz.xiaomi_curtain_position_tilt, fz.xiaomi_curtain_hagl07_status], toZigbee: [tz.xiaomi_curtain_position_state, tz.xiaomi_curtain_options], onEvent: async (type, data, device) => { // The position (genAnalogOutput.presentValue) reported via an attribute contains an invalid value // however when reading it will provide the correct value. if (data.type === 'attributeReport' && data.cluster === 'genAnalogOutput') { await device.endpoints[0].read('genAnalogOutput', ['presentValue']); } }, exposes: [e.cover_position().setAccess('state', ea.ALL), e.binary('running', ea.STATE, true, false) .withDescription('Whether the motor is moving or not'), e.enum('motor_state', ea.STATE, ['closing', 'opening', 'stop']) .withDescription('The current state of the motor.'), e.power_outage_count()], ota: ota.zigbeeOTA, };

module.exports = definition; `

Device is recognized, but all the attempts to access it produce errors, like: zigbee2mqtt | Zigbee2MQTT:error 2024-01-05 07:03:58: Publish 'get' 'state' to '0x54ef441000a483f8' failed: 'Error: Read 0x54ef441000a483f8/1 genAnalogOutput([85], {"sendWhen":"immediate","timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"srcEndpoint":null,"reservedBits":0,"manufacturerCode":null,"transactionSequenceNumber":null,"writeUndiv":false}) failed (Status 'UNSUPPORTED_ATTRIBUTE')' zigbee2mqtt | Zigbee2MQTT:debug 2024-01-05 07:03:58: Error: Read 0x54ef441000a483f8/1 genAnalogOutput([85], {"sendWhen":"immediate","timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"srcEndpoint":null,"reservedBits":0,"manufacturerCode":null,"transactionSequenceNumber":null,"writeUndiv":false}) failed (Status 'UNSUPPORTED_ATTRIBUTE') zigbee2mqtt | Zigbee2MQTT:info 2024-01-05 07:03:58: MQTT publish: topic 'zigbee2mqtt/bridge/log', payload '{"message":"Publish 'get' 'state' to '0x54ef441000a483f8' failed: 'Error: Read 0x54ef441000a483f8/1 genAnalogOutput([85], {\"sendWhen\":\"immediate\",\"timeout\":10000,\"disableResponse\":false,\"disableRecovery\":false,\"disableDefaultResponse\":true,\"direction\":0,\"srcEndpoint\":null,\"reservedBits\":0,\"manufacturerCode\":null,\"transactionSequenceNumber\":null,\"writeUndiv\":false}) failed (Status 'UNSUPPORTED_ATTRIBUTE')'","meta":{"friendly_name":"0x54ef441000a483f8"},"type":"zigbee_publish_error"}'

Please advice. Thanks!

hzonz commented 6 months ago

+1

请添加对它的支持。如果需要,我随时准备帮助调试。

First of all, thank you for your reply.

I used the aqara C2 file before, yes, all access will get an error.
Then I tried to use it through ZHA, which correctly recognized the device, but not the curtain position, and could only control its opening and closing through the curtain service of home assistant, and apparently I was able to use it, although it was not perfect.

What I want to tell you is that all my ZigBee devices are in ZHA, even if ZigBee2mqtt supports it, I can't use it, obviously I can't migrate all my devices to ZigBee2mqtt.

If you just want to help me use it, thanks, I can only use it in ZHA.

If, you want to add a changer for aqara c3, or you want to use it in the future, I'm willing to debug it, please give me time, I need to add ZigBee2mqtt to home assistant.

Thank you!

latel commented 6 months ago

+1 请添加对它的支持。如果需要,我随时准备帮助调试。

First of all, thank you for your reply.

I used the aqara C2 file before, yes, all access will get an error. Then I tried to use it through ZHA, which correctly recognized the device, but not the curtain position, and could only control its opening and closing through the curtain service of home assistant, and apparently I was able to use it, although it was not perfect.

What I want to tell you is that all my ZigBee devices are in ZHA, even if ZigBee2mqtt supports it, I can't use it, obviously I can't migrate all my devices to ZigBee2mqtt.

If you just want to help me use it, thanks, I can only use it in ZHA.

If, you want to add a changer for aqara c3, or you want to use it in the future, I'm willing to debug it, please give me time, I need to add ZigBee2mqtt to home assistant.

Thank you!

so chinglish, 😄

hzonz commented 6 months ago

+1 请添加对它的支持。如果需要,我随时准备帮助调试。

首先,感谢您的回复。 我之前使用过 aqara C2 文件,是的,所有访问都会出错。然后我尝试通过ZHA使用它,它正确识别了设备,但不能识别窗帘位置,只能通过家庭助理的窗帘服务来控制它的开合,显然我能够使用它,尽管它并不完美。 我想告诉你的是,我所有的 ZigBee 设备都在 ZHA 中,即使 ZigBee2mqtt 支持它,我也不能使用它,显然我不能将我所有的设备迁移到 ZigBee2mqtt。 如果你只是想帮我使用它,谢谢,我只能在ZHA使用它。 如果,你想给aqara c3添加一个转换器,或者你想在将来使用它,我愿意调试它,请给我时间,我需要将ZigBee2mqtt添加到家庭助手中。 谢谢!

所以 Chinglish, 😄

微软翻译 😄

sniicker commented 6 months ago

+1

leonshadow15 commented 5 months ago

+1

marcuscps commented 3 months ago

+1

Any updates here? I have a C3 motor and I'm running Zigbee2Mqtt.

I can help with any debugging if needed. I'm also willing to contribute, even though it would be my first contribution to Zigbee2Mqtt.

Thanks a lot!

leonshadow15 commented 3 months ago

I'm interested in buying and using c3 with HA too. Has anyone worked on it so far?

marthubner commented 2 days ago

Here is an external converter for this device for some basic functions that I somehow put together. The device also has other features, but to try add them I will need to sniff Zigbee traffic, I don't have a sniffer yet.

const { identify } = require('zigbee-herdsman-converters/lib/modernExtend');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const lumi = require('zigbee-herdsman-converters/lib/lumi');
const { lumiZigbeeOTA } = require('zigbee-herdsman-converters/lib/lumi');
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const ota = require('zigbee-herdsman-converters/lib/ota');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;
const { isString, getOptions, assertNumber, getFromLookup } = require('zigbee-herdsman-converters/lib/utils');

// Define lumi_curtain_position here
const lumi_curtain_position_state = {
    key: ['state', 'position'],
    options: [exposes.options.invert_cover()],
    convertSet: async (entity, key, value, meta) => {
        if (Array.isArray(meta.mapped)) throw new Error('Not supported for groups');
        if (key === 'state' && typeof value === 'string' && value.toLowerCase() === 'stop') {
            if (['ZNJLBL01LM', 'ZNCLDJ14LM'].includes(meta.mapped.model)) {
                const payload = { 'presentValue': 2 };
                await entity.write('genMultistateOutput', payload);
            } else {
                await entity.command('closuresWindowCovering', 'stop', {}, getOptions(meta.mapped, entity));
            }

            if (!['ZNCLDJ11LM', 'ZNJLBL01LM', 'ZNCLBL01LM', 'ZNCLDJ01LM'].includes(meta.mapped.model)) {
                // Request position update on stop for certain models
                await entity.read('genAnalogOutput', [0x0055]);
            }

            return { state: { state: 'STOP' } };
        } else {
            const lookup = { 'open': 100, 'close': 0, 'on': 100, 'off': 0 };

            value = typeof value === 'string' ? value.toLowerCase() : value;
            if (isString(value)) {
                value = getFromLookup(value, lookup);
            }
            assertNumber(value);
            value = meta.options.invert_cover ? 100 - value : value;

            if (['ZNCLBL01LM', 'ZNCLDJ01LM'].includes(meta.mapped.model)) {
                await entity.command('closuresWindowCovering', 'goToLiftPercentage', { percentageliftvalue: value }, getOptions(meta.mapped, entity));
            } else {
                const payload = { 'presentValue': value };
                await entity.write('genAnalogOutput', payload);
            }
        }
    },
    convertGet: async (entity, key, meta) => {
        if (!Array.isArray(meta.mapped) && ['ZNCLBL01LM', 'ZNCLDJ01LM'].includes(meta.mapped.model)) {
            await entity.read('closuresWindowCovering', ['currentPositionLiftPercentage']);
        } else {
            await entity.read('genAnalogOutput', [0x0055]);
        }
    },
};

const lumidef = {manufacturerCode: 0x115f, disableDefaultResponse: true};

const tz_curtain_hand_open = {
    key: ['hand_open'],
    convertSet: async (entity, key, value, meta) => {
        await entity.write('manuSpecificLumi', {'curtainHandOpen': value}, lumidef);
    },
    convertGet: async (entity, key, meta) => {
        await entity.read('manuSpecificLumi', ['curtainHandOpen'], lumidef);
    },
};

const fz_curtain_hand_open = {
    cluster: 'manuSpecificLumi',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg, publish, options, meta) => {
        const result = {};
        if (msg.data.hasOwnProperty('curtainHandOpen')) {
            result.hand_open = msg.data['curtainHandOpen'] === 1;
        }
        return result;
    },
};

const tz_curtain_reverse = {
    key: ['reverse_direction'],
    convertSet: async (entity, key, value, meta) => {
        await entity.write('closuresWindowCovering', {'windowCoveringMode': value});
    },
    convertGet: async (entity, key, meta) => {
        await entity.read('closuresWindowCovering', ['windowCoveringMode']);
    },
};

const fz_curtain_reverse =  {
    cluster: 'closuresWindowCovering',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg, publish, options, meta) => {
        const result = {};
        if (msg.data.hasOwnProperty('windowCoveringMode')) {
            result.reverse_direction = msg.data['windowCoveringMode'] === 1;
        }
        return result;
    },
};

const definition = {
    zigbeeModel: ['lumi.curtain.acn04'],
    model: 'ZNCLDJ01LM',
    vendor: 'Aqara',
    description: 'Curtain controller C3',
    fromZigbee: [
        lumi.fromZigbee.lumi_basic,
        lumi.fromZigbee.lumi_curtain_position_tilt,
        lumi.fromZigbee.lumi_specific,
        fz_curtain_hand_open,
        fz_curtain_reverse,
    ],
    toZigbee: [
        lumi_curtain_position_state,
        lumi.toZigbee.lumi_curtain_limits_calibration,
        tz_curtain_hand_open,
        tz_curtain_reverse,
    ],
    exposes: [
        e.cover_position().setAccess('state', ea.ALL),
        e.enum('limits_calibration', ea.SET, ['start', 'end', 'reset']).withDescription('Calibrate the position limits'),
        e.binary('hand_open', ea.ALL, true, false).withDescription('Pulling curtains by hand starts the motor'),
        e.binary('reverse_direction', ea.ALL, true, false).withDescription('Whether the curtain direction is inverted'),
    ],
    extend: [identify()],
    meta: {},
};

module.exports = definition;