Koenkk / zigbee2mqtt

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

[New device support]: Moes Roller Blind TS0601 _TZE200_icka1clh #21013

Open Blendi opened 7 months ago

Blendi commented 7 months ago

Link

https://www.aliexpress.com/item/1005005801861031.html?spm=a2g0o.order_list.order_list_main.25.50b65e5bNhNdKV&gatewayAdapt=glo2fra

Database entry

{"id":10,"type":"EndDevice","ieeeAddr":"0x6c5cb1fffe98d021","nwkAddr":21894,"manufId":4098,"manufName":"_TZE200_icka1clh","powerSource":"Battery","modelId":"TS0601","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[0,4,5,61184],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"2�B-f2�B-\u00122�B-\u0012","65506":31,"65508":0,"modelId":"TS0601","manufacturerName":"_TZE200_icka1clh","stackVersion":0,"dateCode":"","appVersion":65,"hwVersion":1,"powerSource":3}},"manuSpecificTuya_2":{"attributes":{}},"haDiagnostic":{"attributes":{}},"genIdentify":{"attributes":{}},"genScenes":{"attributes":{"count":0,"currentScene":0}},"genLevelCtrl":{"attributes":{}},"genBinaryInput":{"attributes":{}},"genMultistateInput":{"attributes":{}},"closuresShadeCfg":{"attributes":{}},"closuresDoorLock":{"attributes":{}},"closuresWindowCovering":{"attributes":{}},"manuSpecificTuya_3":{"attributes":{}},"manuSpecificUbisysDeviceSetup":{"attributes":{}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":65,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1706032659886,"defaultSendRequestWhen":"immediate"}

Comments

Hello,

I bough this new device on aliexpress, and before buying I tried to make sure this device is compatible with zigbee2mqtt. From the reviews on the product, looked like it was supported and people could connect the roller blind to homeassistant.

Most probably this product is a new version and they have changed things a bit with zigbee. Unfortunatelly I dont have a tuya gateway otherwise I would have tried to follow the guide on (https://www.zigbee2mqtt.io/advanced/support-new-devices/03_find_tuya_data_points.html#requirements-and-caveats)

I tired below external definition with different exposes and tuyaDatapoints but nothing worked, until at the end I just commented exposes and tuyaDatapoints completely. I copied the 'exposes' and tuyaDatapoints from existing tuya external definitions for roller blinders already known in https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/tuya.ts

I just throw things at the wall and see what sticks.

External defintion

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 extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    // Since a lot of TuYa devices use the same modelID, but use different datapoints
    // it's necessary to provide a fingerprint instead of a zigbeeModel
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE200_icka1clh',
        },
    ],
    model: 'TS0601_roller_blind',
    vendor: 'TuYa',
    description: 'AM43 Roller Blind',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [ 
        // Here you should put all functionality that your device exposes
        //e.text('work_state', ea.STATE),
        //e.cover_position().setAccess('position', ea.STATE_SET),
        //e.battery(),
        //e.enum('opening_mode', ea.STATE_SET, ['tilt', 'lift']).withDescription('Opening mode'),
        //e.enum('motor_direction', ea.STATE_SET, ['left', 'right']).withDescription('Motor side'),
        //e.enum('set_upper_limit', ea.STATE_SET, ['start', 'stop']).withDescription('Learning'),
        //e.enum('factory_reset', ea.STATE_SET, ['SET']).withDescription('Remove limits'),
    ],
    meta: {
        // All datapoints go in here
        tuyaDatapoints: [
            //[1, 'state', tuya.valueConverterBasic.lookup({'CLOSE': tuya.enum(2), 'STOP': tuya.enum(1), 'OPEN': tuya.enum(0)})],
            //[2, 'position', tuya.valueConverter.coverPositionInverted],
            //[3, 'position', tuya.valueConverter.coverPositionInverted],
            //[4, 'opening_mode', tuya.valueConverterBasic.lookup({'tilt': tuya.enum(0), 'lift': tuya.enum(1)})],
            //[7, 'work_state', tuya.valueConverterBasic.lookup({'standby': tuya.enum(0), 'success': tuya.enum(1), 'learning': tuya.enum(2)})],
            //[13, 'battery', tuya.valueConverter.raw],
            //[101, 'motor_direction', tuya.valueConverterBasic.lookup({'left': tuya.enum(0), 'right': tuya.enum(1)})],
            //[102, 'set_upper_limit', tuya.valueConverterBasic.lookup({'start': tuya.enum(1), 'stop': tuya.enum(0)})],
            //[107, 'factory_reset', tuya.valueConverter.setLimit],
        ],
    },
};

module.exports = definition;
Blendi commented 7 months ago

I have no idea what I did but it is now partially working. I just went over different places and copied part of codes. Only issue I have is that buttons orientation is wrong. I press button with arrow facing down and the roller blinds open. I press the opposite button facing up the blind/curtains roll down. There should be somewhere an option to invert the function of the buttons but anyway so far I am happy with the progress. Battery percentage also is not updating. That might be my next goal.

In the meantime I bought a cheap tuya gateway from aliexpress, in order to extract the tuyadatapoints later.

my external js code looks like below.

` 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 extend = require('zigbee-herdsman-converters/lib/extend'); const e = exposes.presets; const ea = exposes.access; const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = { // Since a lot of TuYa devices use the same modelID, but use different datapoints // it's necessary to provide a fingerprint instead of a zigbeeModel fingerprint: [ { // The model ID from: Device with modelID 'TS0601' is not supported // You may need to add \u0000 at the end of the name in some cases modelID: 'TS0601', // The manufacturer name from: Device with modelID 'TS0601' is not supported. manufacturerName: '_TZE200_icka1clh', }, ], model: 'AM43-0.45/40-ES-EB', vendor: 'Moes', description: 'Curtain motor/roller blind motor/window pusher/tubular motor', fromZigbee: [tuya.fz.datapoints], //fromZigbee: [fz.tuya_cover, fz.ignore_basic_report], toZigbee: [tuya.tz.datapoints], //toZigbee: [tz.tuya_cover_control, tz.tuya_cover_options], onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime' configure: tuya.configureMagicPacket, exposes: [ e.cover_position().setAccess('position', ea.STATE_SET), exposes.composite('options', 'options') .withFeature(exposes.numeric('motor_speed', ea.STATE_SET) .withValueMin(0) .withValueMax(255) .withDescription('Motor speed')) ], meta: { // All datapoints go in here tuyaDatapoints: [ [1, 'state', tuya.valueConverterBasic.lookup({'CLOSE': tuya.enum(2), 'STOP': tuya.enum(1), 'OPEN': tuya.enum(0)})], [2, 'position', tuya.valueConverter.coverPositionInverted], [3, 'position', tuya.valueConverter.coverPositionInverted], [4, 'opening_mode', tuya.valueConverterBasic.lookup({'tilt': tuya.enum(0), 'lift': tuya.enum(1)})], [7, 'work_state', tuya.valueConverterBasic.lookup({'standby': tuya.enum(0), 'success': tuya.enum(1), 'learning': tuya.enum(2)})], [13, 'battery', tuya.valueConverter.raw], [101, 'motor_direction', tuya.valueConverterBasic.lookup({'left': tuya.enum(0), 'right': tuya.enum(1)})], [102, 'set_upper_limit', tuya.valueConverterBasic.lookup({'start': tuya.enum(1), 'stop': tuya.enum(0)})], [107, 'factory_reset', tuya.valueConverter.setLimit], ], }, };

module.exports = definition; `

woloss commented 7 months ago

Thanks for your code. As for up/down thing, there is Change Direction in instruction, I guess it should flip buttons.

Blendi commented 7 months ago

Hello,

Actually I was wrong. In Homeassistant and Zigbee2mqtt addon, orientation works fine. I press up arrow and the blinds rise. I press stop and they stop. I press down arrow in homeassistant, blind goes down. Also I can control how many percent I want my blind to be opened, 20-30% etc. All is good except the battery percentage, that I cannot see in Homeassistant nor zigbee2mqtt addon/docker.

The orientation is wrong only when I press the physical buttons on the device, but that is a minor issue for me as I don’t plan to control it from the buttons on the device. I will update here in case I find the solution for the physical buttons and change their orientation to match with blind movements.

Silly me, I just checked the paper that came with some instructions and in the corner of the page was the solution. I must have missed it in the past. Looks like the solution os to hold both up and down arrow buttons for 3 second on the device and the blue led in the device blinks 3 times, indicating the change of orientation was successful.

Result: Everything works perfect now apart from battery information that is never shown/updated.

woloss commented 7 months ago

Looks like there was no battery state in previous revision of Roller Blind or it wasn't found. https://github.com/Koenkk/zigbee2mqtt/issues/17363

arrikhan commented 2 months ago

Innocent question, can the code from ESPHome for the bluetooth model help identify where to find the battery state and illuminance (light sensor)? These models look identical (I have both) other than the protocol to communicate. ESPHome integration of AM43 exposes battery level and light brightness.

Code is here --> https://github.com/esphome/esphome/tree/dev/esphome/components/am43

Dengar66 commented 2 months ago

I was working on setting this one up as well and came across your issue already opened. This is what I've got currently. I noticed you were missing motor speed specifically. Have you tested the opening mode or setting limits through Zigbee yet?

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 modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE200_icka1clh',
        },
    ],
    model: 'AM43-0.45/40-ES-EB',
    vendor: 'Moes',
    description: 'Roller blind/shades drive motor',
    options: [exposes.options.invert_cover()],
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime,
    configure: tuya.configureMagicPacket,
    exposes: [
        e.cover_position().setAccess('position', ea.STATE_SET),
        e.battery(), // No data
        exposes.enum('motor_direction', ea.STATE_SET, ['normal', 'reversed']).withDescription('Set the motor direction'),
        exposes.numeric('motor_speed', ea.STATE_SET).withValueMin(0).withValueMax(255).withDescription('Motor speed').withUnit('rpm'),
    ],
    meta: {
        tuyaDatapoints: [
            [1, 'state', tuya.valueConverterBasic.lookup({'CLOSE': tuya.enum(2), 'STOP': tuya.enum(1), 'OPEN': tuya.enum(0)})],
            [2, 'position', tuya.valueConverter.coverPositionInverted],
            [3, 'position', tuya.valueConverter.coverPositionInverted],
            [4, 'opening_mode', tuya.valueConverterBasic.lookup({'tilt': tuya.enum(0), 'lift': tuya.enum(1)})], // Have not tested
            [7, 'work_state', tuya.valueConverterBasic.lookup({'standby': tuya.enum(0), 'success': tuya.enum(1), 'learning': tuya.enum(2)})], // Have not tested
            [13, 'battery', tuya.valueConverter.raw], // Not getting data back for this
            [101, 'motor_direction', tuya.valueConverterBasic.lookup({'normal': tuya.enum(0), 'reversed': tuya.enum(1)})],
            [102, 'set_upper_limit', tuya.valueConverterBasic.lookup({'start': tuya.enum(1), 'stop': tuya.enum(0)})],
            [105, 'motor_speed', tuya.valueConverter.raw],
            [107, 'factory_reset', tuya.valueConverter.setLimit], // Have not tested
        ],
    },
    extend: [
    ],
};

module.exports = definition;