Logotipo do Zigbee2MQTT em tela de computador com ambiente de casa inteligente ao fundo

Faça o ZBCMR-01 ser reconhecido pelo Zigbee2MQTT

Recentemente comprei um motor de persiana inteligente Nova Digital ZBCMR-01, mas ao tentar adicioná‑lo ao Zigbee2MQTT ele apareceu como “não suportado. O motivo é simples: o modelo que comprei tem um fingerprint diferente do que está cadastrado na base de dados do z2m. Neste post mostro como contornar o problema usando um conversor externo, passo a passo, e ainda como enviar a correção para que todos se beneficiem.

Se você já viu a mensagem de dispositivo não suportado, não desista: a solução está em habilitar os conversores externos e criar um arquivo pequeno que mapeia o fingerprint do seu lote.

Por que o ZBCMR-01 não é reconhecido?

A página do Zigbee2MQTT para o ZBCMR-01 lista o modelo como suportado, mas o fingerprint cadastrado é zigbeeModel: TS0105 + manufacturerName: _TZE600_ogyg1y6b. Seu módulo, de um lote diferente, se identifica como TS0601 + _TZE284_5blghqkp, uma combinação que ainda não está na base de dados, portanto o z2m recusa o dispositivo mesmo sendo “o mesmo produto”.

Passo a passo para adicionar o conversor externo

  1. Verifique a versão do z2m – Acesse Settings > About. Se estiver desatualizado, atualize (geralmente não resolve sozinho, mas vale testar). Fonte principal.
  2. Habilite conversores externos (necessário a partir da v2.11). Em configuration.yaml (do z2m e não do Home Assistant. Acesse portanto o arquivo /root/config/zigbee2mqtt/configuration.yaml) confirme ou adicione:

    advanced:

    enable_external_js: true


    É provável que já exista uma seção chamada “advanced”. Depois reinicie o z2m. Mais detalhes.

  3. Adicione o conversor externo – O jeito mais fácil é pelo frontend: Settings > Dev console > External converters. Crie um arquivo, por exemplo nova-zbcmr01.mjs, com o seguinte conteúdo:

    // Nova Digital ZBCMR-01 - external converter for Zigbee2MQTT
    // Made by bernabauer.com
    // https://www.bernabauer.com/blog/faca-o-zbcmr-01-ser-reconhecido-pelo-zigbee2mqtt/
    // Version: 1.0

    import * as exposes from 'zigbee-herdsman-converters/lib/exposes';
    import * as tuya from 'zigbee-herdsman-converters/lib/tuya';

    const e = exposes.presets;
    const ea = exposes.access;

    // DP1 - Control command. Values: 0=OPEN, 1=STOP, 2=CLOSE, 3=CONTINUE (never observed in practice).
    // Confirmed reliable for both read and write.
    const coverState = {
        to: (v) => {
            const map = {OPEN: new tuya.Enum(0), STOP: new tuya.Enum(1), CLOSE: new tuya.Enum(2), CONTINUE: new tuya.Enum(3)};
            return map[v];
        },
        from: (v) => {
            const map = {0: 'OPEN', 1: 'STOP', 2: 'CLOSE', 3: 'CONTINUE'};
            return map[v];
        },
    };

    // DP3 - Direction of the last movement command. Values: 0=opening, 1=closing.
    // Confirmed via 14+ direct correlations. Does not indicate whether movement
    // is still in progress, and stays unchanged when a STOP command is sent
    // mid-travel (no distinct "stopped" value observed). Read-only.
    const direction = {
        from: (v) => (v === 0 ? 'opening' : 'closing'),
    };

    // DP8 - Real position report (%). Values: 0-100. Confirmed live during real
    // motor movement. Read-only: writing here has no effect (tested - no error,
    // no echo, no movement). Use DP9 to command a target position.
    const coverPositionReport = {
        from: (v, meta, options, publish) => {
            publish({state: v === 0 ? 'CLOSE' : 'OPEN', position_report: v});
            return v;
        },
    };

    // DP9 - Target position command (%). Values: 0-100. Confirmed: writing here
    // moves the motor to the exact percentage, later reflected on DP8. Same
    // set/report split as the sibling "ZSM-01" (Novato) device in the official
    // library. Listed BEFORE DP8 in tuyaDatapoints so writes to 'position' route
    // here (tz.datapoints picks the first array entry matching the property).
    const coverPositionSet = {
        to: (v) => v,
    };

    // DP11 - Inverts the cover position scale. Confirmed live: default (false)
    // leaves the motor as-is; writing 'false' while already false triggered a
    // resync that opened the motor fully. Do not write unless intentionally
    // changing the inversion - it moves the motor to resync.
    const invertCover = {
        to: (v) => v,
        from: (v) => v,
    };

    // DP19 - Favorite position (%). Confirmed live: writing here does not move
    // the motor immediately, just stores the value (motor beeps to confirm the
    // save). Matches "favorite_position" on the sibling ZSM-01 device (same
    // value type: numeric 0-100, write-only). No recall/goto trigger found for
    // it - not even in the official ZSM-01 definition.
    const favoritePosition = {
        to: (v) => v,
        from: (v) => v,
    };

    // DP20 - Click control (step nudge). Values: up=0, down=1. Confirmed live:
    // "down" triggered a brief CLOSE that auto-stopped after ~1s (a small nudge),
    // matching "click_control" on the sibling ZSM-01 device. Exposed as two
    // single-value enums (button entities in HA) instead of one two-value enum
    // (which HA renders as a dropdown) - matches the cover's own open/close buttons.
    const nudgeUp = {
        to: () => new tuya.Enum(0),
    };
    const nudgeDown = {
        to: () => new tuya.Enum(1),
    };

    export default {
        fingerprint: tuya.fingerprint('TS0601', ['_TZE284_5blghqkp']),
        model: 'ZBCMR-01',
        vendor: 'Nova Digital',
        description: 'Roller Blind Motor (TS0601 batch)',
        extend: [tuya.modernExtend.tuyaBase({dp: true, queryOnConfigure: true})],
        exposes: [
            e.cover_position().setAccess('position', ea.STATE_SET),
            e.enum('direction', ea.STATE, ['opening', 'closing']).withDescription(
                'DP3 - Direction of the last movement command (0=opening, 1=closing). '
                + 'Does not indicate whether movement is still in progress.',
            ).withHomeAssistant({icon: 'mdi:arrow-up-down-bold'}),
            e.binary('invert_cover', ea.STATE_SET, true, false).withLabel('Invert Cover Position').withDescription(
                "DP11 - Inverts the cover position, false: open=100,close=0, true: open=0,close=100 "
                + "(default false). The value must be `true` or `false`",
            ).withHomeAssistant({icon: 'mdi:swap-vertical'}),
            e.numeric('favorite_position', ea.STATE_SET).withValueMin(0).withValueMax(100).withDescription(
                'DP19 - Stores a favorite position (%). Does not move the motor immediately.',
            ).withHomeAssistant({entityCategory: 'config'}),
            e.numeric('position_report', ea.STATE).withUnit('%').withDescription(
                'DP8 - Raw position report, mirrors the cover position as a standalone sensor.',
            ).withHomeAssistant({icon: 'mdi:roller-shade'}),
            e.enum('nudge_up', ea.STATE_SET, ['PRESS']).withLabel('Nudge up').withDescription(
                'DP20 - Step nudge up: triggers a brief OPEN movement that auto-stops after ~1s.',
            ).withHomeAssistant({icon: 'mdi:chevron-up'}),
            e.enum('nudge_down', ea.STATE_SET, ['PRESS']).withLabel('Nudge down').withDescription(
                'DP20 - Step nudge down: triggers a brief CLOSE movement that auto-stops after ~1s.',
            ).withHomeAssistant({icon: 'mdi:chevron-down'}),
        ],
        meta: {
            tuyaDatapoints: [
                [1, 'state', coverState],
                [3, 'direction', direction],
                [9, 'position', coverPositionSet],
                [8, 'position', coverPositionReport],
                [11, 'invert_cover', invertCover],
                [19, 'favorite_position', favoritePosition],
                [20, 'nudge_up', nudgeUp],
                [20, 'nudge_down', nudgeDown],
            ],
        },
    };


    Se preferir, salve o arquivo diretamente na pasta external_converters/ (irmã do configuration.yaml).

  4. Reinicie o z2m e repareie – Vá em Settings > Tools > Restart Zigbee2MQTT. Se o dispositivo já estava listado como “unsupported”, remova-o e coloque‑o em modo de pareamento novamente (reset físico do motor + permitir entrada na rede).
  5. Teste abrir/fechar/parar e posição – Use os comandos OPEN, CLOSE, STOP e ajuste a posição com valores de 0 a 100. Caso o comportamento esteja invertido ou a posição não seja reportada, ajuste o conversor (por exemplo, invertendo a posição) ou siga o guia de descoberta de DPs para Tuya (fonte complementar 3).
  6. Depois que funcionar – Abra uma issue “New device support” no repositório do Zigbee2MQTT (exemplo de issue) anexando os dados da interview, para que esse fingerprint seja incorporado oficialmente e você não precise manter o conversor externo para sempre.
Interface do Zigbee2MQTT mostrando o motor de persiana ZBCMR-01 recém-adicionado com controles de abrir/fechar
Foto: Markus Winkler / Pexels

Após seguir esses passos, seu motor deve aparecer na lista de dispositivos com as funcionalidades de abertura, fechamento, parada e controle de posição. Teste cada comando e confirme que os relatórios de estado estão corretos.

Testando e ajustando

Como os Datapoints (DPs) da Tuya podem variar entre lotes, vale observar o log do z2m enquanto aciona o motor. Se notar que a posição está invertida, basta mudar a linha exposes: [e.cover_position().setAccess(‘position’, ea.STATE_SET)] para usar exposes: [e.cover_position().setAccess(‘position’, ea.STATE_SET)] com a opção invert_cover: true na configuração do dispositivo, ou ajustar o conversor para mapear o DP correto. Se precisar de ajuda com o mapeamento de DPs, consulte o guia detalhado de suporte a novos dispositivos Tuya (fonte complementar 3).

Contribuindo para a base oficial

Quando tudo estiver funcionando, considere devolver à comunidade. Abra uma pull request no repositório zigbee-herdsman-converters ou uma issue no zigbee2mqtt com o fingerprint e a descrição do seu lote. Assim, o suporte será integrado nas próximas versões e você poderá remover o conversor externo.

Posts Similares

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *