Модуль дискретных входов WS7: детали Zigbee протокола

Описание Zigbee-протокола устройства JetHome WS7 для интеграции с Zigbee-координаторами.

См.также

Содержание

Идентификация устройства

Поле

Значение

Vendor

JetHome

Модель

WS7

genBasic.manufacturerName (0x0004)

JetHome

genBasic.modelIdentifier (0x0005)

WS7

Profile ID

0x0104 (Home Automation)

Device ID (на всех endpoint)

0x00FF (test/generic)

HW Version (0x0003)

2

Stack Version (0x0002)

1

ZCL Version (0x0000)

8 (ZCL r8)

Power Source (0x0007)

0x03 (Battery)

Тип Zigbee-устройства

End Device (sleepy)

Touchlink

не поддерживается

Green Power

не поддерживается

Важно

Сопоставление в драйвере — только по паре manufacturerName + modelIdentifier. Device ID 0x00FF слишком общий, на него не опираться.

Полный набор атрибутов кластера genBasic (0x0000) на EP1

Attr ID

Имя

Тип

Доступ

Значение

0x0000

ZCLVersion

uint8

R

8

0x0001

ApplicationVersion

uint8

R

соответствует текущей OTA CurrentFileVersion

0x0002

StackVersion

uint8

R

1

0x0003

HWVersion

uint8

R

2

0x0004

ManufacturerName

string

R

JetHome

0x0005

ModelIdentifier

string

R

WS7

0x0006

DateCode

string(8)

R

YYYYMMDD

0x0007

PowerSource

enum8

R

0x03

0x0011

PhysicalEnvironment

enum8

R/W

unspecified

0x4000

SWBuildID

uint8

R

соответствует OTA CurrentFileVersion

0xFFFD

ClusterRevision

uint16

R

0x0002

Внимание

По спецификации ZCL SWBuildID (0x4000) — octet string. На WS7 он объявлен как uint8. Декодеру надо учитывать тип uint8 при чтении.

Примечание

Команда сервера Reset to Factory Defaults поддерживается.

Endpoint-карта

WS7 объявляет три endpoint, физически соответствующие трём симметричным дискретным входам.

Endpoint

Псевдоним

Назначение

1

in1

Главный: Basic, PowerCfg, DeviceTempCfg, Groups, MultistateInput для входа №1; OUT — OnOff, OTA

2

in2

MultistateInput для входа №2; OUT — OnOff

3

in3

MultistateInput для входа №3; OUT — OnOff

Примечание

Все три endpoint используют Profile = 0x0104, DeviceID = 0x00FF.

Примечание

На устройстве физически есть и сервисная (служебная) кнопка для commissioning, factory reset и переключения режима работы входов. Эта кнопка не отправляет ZCL-сообщений по сети — координатор не получит от неё событий.

Кластеры

Endpoint 1 (главный)

Server (Input)

Cluster ID

Имя

Назначение

0x0000

Basic

стандартные атрибуты идентификации (см. Модуль дискретных входов WS7: детали Zigbee протокола)

0x0001

Power Configuration

состояние батареи, reportable

0x0002

Device Temperature Configuration

объявлен reportable, но значение всегда 0 — измерение не реализовано

0x0004

Groups

стандартный

0x0012

Multistate Input (Basic)

события входа №1

Client (Output) — устройство отправляет команды наружу

Cluster ID

Имя

Когда

0x0006

On/Off

всегда

0x0019

OTA Upgrade

всегда

Важно

Кластер Identify (0x0003) отсутствует — не объявлен в Simple Descriptor. Не использовать Identify / IdentifyQuery.

Endpoint 2 / Endpoint 3

Симметричны, отличаются только привязкой к физическому входу.

Сторона

Cluster ID

Имя

Server (In)

0x0012

Multistate Input (Basic) — события входа №2 (EP2) / входа №3 (EP3)

Client (Out)

0x0006

On/Off

Сводная схема направлений

Coordinator                                                  WS7
   │                                                          │
   │  ◄── 0x0006 OnOff (Toggle/On/Off) от EP1/EP2/EP3 ────────┤   (только при наличии binding)
   │  ◄── 0x0012 MultistateInput.Report от EP1/EP2/EP3 ───────┤   (всегда unicast на 0x0000:1)
   │  ◄── 0x0001 PowerCfg.Report от EP1 ──────────────────────┤   (раз в час)
   │  ──► 0x0000 Basic Read attrs ────────────────────────────┤
   │  ◄──► 0x0019 OTA Upgrade ───────────────────────────────┤
   ▼                                                          ▼

Логика входов: два режима работы

Режимы CLICK и HOLD

Каждый из трёх входов работает в одном из двух режимов, общем для всех трёх входов:

Режим

По умолчанию

Какие события распознаются

CLICK

да

одиночный и двойной клики

HOLD

фронт нажатия и фронт отпускания

Примечание

Текущий режим сохраняется в энергонезависимой памяти устройства и переживает перезагрузку. После factory reset режим возвращается в CLICK.

Переключение режима

Переключение — двойным кликом по сервисной кнопке устройства. Подтверждение:

  • CLICK → зелёный светодиод мигает 1 раз;

  • HOLD → зелёный светодиод мигает 2 раза.

Внимание

По сети событие переключения режима не отправляется. Координатор не получит уведомления о смене режима. Узнать текущий режим можно только косвенно — по типу первых пришедших событий.

Декодирование genMultistateInput.PresentValue (attr 0x0055)

Атрибут uint16, принимает только четыре значения:

Значение

Семантика

В каком режиме приходит

Триггер

0

release

HOLD

отпускание (размыкание) входа

1

single

CLICK

одиночный клик

2

double

CLICK

двойной клик

4

hold

HOLD

нажатие (замыкание) входа

Канал (вход №1 / №2 / №3) определяется по полю endpoint входящего ZCL-фрейма (1in1, 2in2, 3in3).

Параллельный канал — исходящие OnOff (cluster 0x0006)

Помимо отчёта Multistate Input, для некоторых событий устройство дополнительно отправляет команду по genOnOff. Маршрут адресации — binding groups fallback:

  1. Binding (addrMode = AddrNotPresent) — основной путь. Устройство использует свою таблицу binding-ов.

  2. Groups — для каждой группы, в которой состоит endpoint, дополнительно шлётся та же команда (AddrGroup).

  3. Coordinator (fallback)afAddr16Bit, shortAddr = 0x0000, endpoint = 1. Но в этот fallback идёт только Multistate Input report; OnOff-команда на координатор по fallback-пути НЕ отправляется.

Соответствие событий и команд

Событие

Режим

OnOff-команда (0x0006)

Multistate (0x0012)

single

CLICK

Toggle (cmd 0x02)

presentValue = 1

double

CLICK

(не отправляется)

presentValue = 2

hold (press)

HOLD

On (cmd 0x01)

presentValue = 4

release

HOLD

Off (cmd 0x00)

presentValue = 0

Важно

Чтобы координатор получал OnOff-команды от устройства, обязателен ZDO Bind cluster 0x0006 для каждого из EP1 / EP2 / EP3 на координатор. Без binding-а OnOff-команды до координатора не доходят.

Multistate Input приходит координатору всегда, без binding — устройство шлёт его юникастом на 0x0000:1.

Управление устройством через OnOff невозможно

Устройство игнорирует входящие команды OnOff от координатора (callback пустой). Любая попытка управления через On / Off / Toggle к WS7 — мимо. OnOff на WS7 — sender-only канал.

Тайминги обработки

Параметр

Значение

Обработка дребезга контактов

5 мс

Длинное нажатие (для входов)

2000 мс

Окно двойного клика

200 мс

Длинное нажатие сервисной кнопки

5000 мс

Power Configuration / батарея

Атрибуты на EP1, cluster 0x0001

Attr ID

Имя

Тип

Доступ

Семантика

0x0020

BatteryVoltage

uint8

R + Reportable

десятые доли вольта (30 ≡ 3.0 В); по ZCL — × 100 мВ

0x0021

BatteryPercentageRemaining

uint8

R + Reportable

формат ZCL: 1 = 0.5 %, 200 (0xC8) = 100 %

Примечание

Атрибуты batteryAlarmState (0x003E), batteryAlarmMask, batteryRatedVoltage, batterySize не объявлены — устройство их не присылает и не отвечает на их чтение.

Декодирование на стороне драйвера

voltage_V  = batteryVoltage / 10.0          # uint8 30  → 3.0 V
voltage_mV = batteryVoltage * 100           #          → 3000 mV
percent    = batteryPercentageRemaining / 2.0   # 200 → 100 %

Это совпадает со стандартным поведением Battery 1 из ZCL r8. WS7 не имеет переопределения «не делить процент на 2» — деление обязательно.

Внутренняя формула процента

Устройство пересчитывает напряжение в процент линейно по диапазону 2.0 V 3.0 V:

если V >= 2.0 V:
    pct = clamp((V - 2.0) * 100 / 1.0, 0, 100)
иначе:
    pct = 0

То есть voltage = 2.0 V0 %, voltage = 3.0 V100 %. По сети передаётся уже pct * 2 в BatteryPercentageRemaining.

Период репорта

Триггер

Период

Регулярный таймер

60 минут (фиксированно)

После старта устройства

один раз сразу после init

Короткий клик сервисной кнопки в сети

по факту нажатия (заодно идёт OTA query)

После rejoin

5 секунд после восстановления связи

Предупреждение

Configure Reporting от координатора не влияет на частоту батарейного репорта. Период (60 минут) задан внутри устройства и не перенастраивается. Команда Configure Reporting принимается стеком без ошибки, но реальный период остаётся тем же. Отправлять Configure Reporting можно (для совместимости со стандартной процедурой interview), но рассчитывать на изменение периода — нельзя.

Примечание

Отчёт всегда содержит оба атрибута (0x0020 + 0x0021) одним фреймом Report Attributes от EP1 на 0x0000:1.

Device Temperature Configuration (cluster 0x0002, EP1)

Attr ID

Имя

Тип

Доступ

Значение

0x0000

CurrentTemperature

int16

R + Reportable

всегда 0

Атрибут декларирован, но измерение чипа не реализовано — значение никогда не меняется.

Совет

Драйверу: можно игнорировать или показывать как «не реализовано». Не вытягивать в e.temperature() — это введёт пользователя в заблуждение.

OTA Upgrade (cluster 0x0019, EP1, client side)

Атрибуты (все R | Cli)

Attr ID

Имя

Тип

Начальное значение

0x0000

UpgradeServerID

IEEE

FF:FF:FF:FF:FF:FF:FF:FF

0x0001

FileOffset

uint32

0xFFFFFFFF

0x0002

CurrentFileVersion

uint32

текущая версия (0x0000000F для актуального релиза = v15)

0x0003

CurrentZigBeeStackVersion

uint16

OTA_STACK_VER_PRO

0x0004

DownloadedFileVersion

uint32

0xFFFFFFFF

0x0005

DownloadedZigBeeStackVersion

uint16

0xFFFF

0x0006

ImageUpgradeStatus

enum8

0

0x0007

ManufacturerID

uint16

0xF123 (61731)

0x0008

ImageTypeID

uint16

0xF001 (61441)

0x0009

MinimumBlockPeriod

uint16

0

0xFFFD

ClusterRevision

uint16

0x0001

Триггеры запроса OTA

Устройство само инициирует OTA-проверку (Query Next Image Request):

  • сразу после успешного pairing-а;

  • при коротком клике сервисной кнопки, если устройство в сети.

Параметры для OTA-сервера координатора

Параметр

Значение

manufacturerCode

0xF123

imageType

0xF001

Минимальная новая fileVersion для апгрейда

> CurrentFileVersion

API информации об обновлениях

fw.jethome.com/api/devices/ws7/info — возвращает JSON со списком доступных версий и URL образов

Зеркало (используется в zigbee-OTA)

github.com/Koenkk/zigbee-OTA

Поток OTA — стандартный ZCL: Query Next Image RequestQuery Next Image ResponseImage Block Request / Image Block ResponseUpgrade End RequestUpgrade End Response. Vendor-расширений нет.

Pairing, factory reset, rejoin

WS7 — End Device, поэтому требует router/coordinator-родителя в радиусе.

Pairing (вход в сеть)

Шаг

Действие пользователя

Поведение устройства

1

Координатор переводится в permit_join

2

Удержать сервисную кнопку ≥ 5 секунд не находясь в сети

Красный светодиод моргает 30 секунд. Запускается NWK Steering.

3

Устройство ищет сеть, делает association, при успехе перестаёт моргать.

4

Стартует периодический замер батареи (60 мин).

Важно

Нет варианта «permit_join → device joins automatically». Pairing требует физического long-press 5 секунд.

Factory reset — два пути

  1. Длинное удержание в сети — сервисная кнопка ≥ 5 секунд: красный светодиод 1 секунду, очистка NV-данных, local leave, программная перезагрузка.

  2. Удержание при подаче питания — если в момент power-on кнопка нажата: красный светодиод ≈ 1 секунду, очистка NV-данных.

Примечание

После factory reset режим входов сбрасывается в CLICK (значение по умолчанию).

Rejoin

При потере parent-устройства WS7 ставит событие rejoin и пытается восстановить сеть. После успеха:

  • через 5 секунд приходит свежий батарейный отчёт;

  • отложенные input-события воспроизводятся;

  • замер батареи перепланируется на штатный 60-минутный цикл.

Что должен делать драйвер координатора

Минимальный пакет действий после interview:

  1. Прочитать атрибуты Basic на EP1: 0x0004 (ManufacturerName), 0x0005 (ModelIdentifier), 0x0003 (HWVersion), 0x0001 (ApplicationVersion), 0x4000 (SWBuildID), 0x0006 (DateCode).

  2. Выполнить ZDO Bind для исходящих кластеров устройства, чтобы пользователь получал все события:

    src=device, srcEP=1, cluster=0x0006, dst=coord, dstEP=1
    src=device, srcEP=2, cluster=0x0006, dst=coord, dstEP=1
    src=device, srcEP=3, cluster=0x0006, dst=coord, dstEP=1
    src=device, srcEP=1, cluster=0x0001, dst=coord, dstEP=1
    
  3. Опционально — отправить Configure Reporting для genPowerCfg (batteryVoltage, batteryPercentageRemaining). Команда будет принята, но частоту это не изменит — реальный период задан в устройстве (60 минут).

  4. Не пытаться делать Configure Reporting для genMultistateInput или genOnOff — отчёты идут вне reporting manager-а.

  5. Не пытаться управлять устройством через входящие OnOff — команды игнорируются.

  6. Не использовать Identify (0x0003) — кластер отсутствует.

  7. Multistate Input принимать как attributeReport / readResponse на cluster 0x0012, attr 0x0055, без bind (приходит unicast на 0x0000:1).

Тест-кейсы для валидации драйвера

  1. Идентификация. manufacturerName="JetHome", modelIdentifier="WS7", hwVersion=2, profile 0x0104, deviceID 0x00FF. Матчинг только по паре manufacturerName + modelIdentifier.

  2. EP-карта. Active EP вернёт [1, 2, 3]. Simple Descriptor EP1 содержит in: [0x0000, 0x0001, 0x0002, 0x0004, 0x0012], out: [0x0006, 0x0019]. EP2 / EP3: in [0x0012], out [0x0006].

  3. Identify не отвечает. Команды cluster 0x0003 идут без ответа (или Default Response: UNSUP_CLUSTER_COMMAND).

  4. Battery init. Сразу после pairing-а приходит Report Attributes от EP1 на cluster 0x0001 с обоими атрибутами (0x0020 и 0x0021) одним фреймом.

  5. Battery period. Следующий батарейный отчёт — через ~60 минут. Configure Reporting темп не меняет.

  6. CLICK / single на EP=1. Одиночный клик: Report Attributes cluster 0x0012 attr 0x0055 value=1 от EP=1; плюс (если bind на 0x0006) Toggle от EP=1. Декодер выдаёт single_in1.

  7. CLICK / double на EP=2. Двойной клик: presentValue=2 от EP=2; OnOff не приходит. Декодер выдаёт double_in2.

  8. HOLD / press+release на EP=3. Нажатие → presentValue=4 + (bind) On от EP=3 → hold_in3. Отпускание → presentValue=0 + (bind) Off от EP=3 → release_in3.

  9. Triple click невозможен. Три клика подряд (с интервалом < 200 мс между 2-м и 3-м) дадут только одно событие double — третий клик игнорируется.

  10. Switch mode CLICK ↔ HOLD. Двойной клик сервисной кнопки: зелёный светодиод 1 или 2 раза. По сети событие не доставляется. После reboot режим сохраняется.

  11. Factory reset → CLICK. После long-press 5 сек или удержания при boot режим сбрасывается в CLICK.

  12. Incoming OnOff игнорируется. Отправить On / Off / Toggle от координатора на EP1 cluster 0x0006. Реакции от устройства нет.

  13. OTA Query. На короткий клик сервисной кнопки устройство шлёт Query Next Image Request с manufacturerCode=0xF123, imageType=0xF001.

  14. Pairing. Long-press 5 сек не в сети → NWK Steering → join (если координатор в permit_join).

  15. Rejoin. После потери parent: устройство сообщает rejoin, через 5 сек после восстановления приходит свежий батарейный отчёт + отложенные input-события.

Шаблон z2m-конвертера

import * as fz from "../converters/fromZigbee";
import * as exposes from "../lib/exposes";
import * as reporting from "../lib/reporting";
import type {DefinitionWithExtend, Fz} from "../lib/types";
import * as utils from "../lib/utils";

const e = exposes.presets;

const ws7 = {
    fz: {
        action: {
            cluster: "genMultistateInput",
            type: ["attributeReport", "readResponse"],
            convert: (model, msg, publish, options, meta) => {
                const map: Record<number, string> = {
                    0: "release", 1: "single", 2: "double", 4: "hold",
                };
                const action = map[msg.data.presentValue];
                if (!action) return;
                return {action: utils.postfixWithEndpointName(action, msg, model, meta)};
            },
        } satisfies Fz.Converter<"genMultistateInput", undefined, ["attributeReport", "readResponse"]>,
    },
};

export const definitions: DefinitionWithExtend[] = [
    {
        fingerprint: [{modelID: "WS7", manufacturerName: "JetHome"}],
        model: "WS7",
        vendor: "JetHome",
        description: "3-channel battery discrete input module (CLICK / HOLD modes)",
        fromZigbee: [fz.battery, fz.command_toggle, fz.command_on, fz.command_off, ws7.fz.action],
        toZigbee: [],
        ota: true,
        meta: {multiEndpoint: true},
        endpoint: () => ({in1: 1, in2: 2, in3: 3}),
        exposes: [
            e.battery(),
            e.battery_voltage(),
            e.action([
                "release_in1", "single_in1", "double_in1", "hold_in1",
                "release_in2", "single_in2", "double_in2", "hold_in2",
                "release_in3", "single_in3", "double_in3", "hold_in3",
            ]),
        ],
        configure: async (device, coordinatorEndpoint) => {
            const ep1 = device.getEndpoint(1);
            await reporting.bind(ep1, coordinatorEndpoint, ["genPowerCfg", "genOnOff"]);
            await reporting.batteryVoltage(ep1);
            await reporting.batteryPercentageRemaining(ep1);

            const ep2 = device.getEndpoint(2);
            await reporting.bind(ep2, coordinatorEndpoint, ["genOnOff"]);

            const ep3 = device.getEndpoint(3);
            await reporting.bind(ep3, coordinatorEndpoint, ["genOnOff"]);
        },
    },
];

Шаблон ZHA quirk

class JetHomeWS7(CustomDevice):
    signature = {
        MODELS_INFO: [("JetHome", "WS7")],
        ENDPOINTS: {
            1: {
                PROFILE_ID:  0x0104,
                DEVICE_TYPE: 0x00FF,
                INPUT_CLUSTERS:  [0x0000, 0x0001, 0x0002, 0x0004, 0x0012],
                OUTPUT_CLUSTERS: [0x0006, 0x0019],
            },
            2: {
                PROFILE_ID:  0x0104,
                DEVICE_TYPE: 0x00FF,
                INPUT_CLUSTERS:  [0x0012],
                OUTPUT_CLUSTERS: [0x0006],
            },
            3: {
                PROFILE_ID:  0x0104,
                DEVICE_TYPE: 0x00FF,
                INPUT_CLUSTERS:  [0x0012],
                OUTPUT_CLUSTERS: [0x0006],
            },
        },
    }
    replacement = signature

В обработчике MultistateInputCluster.attribute_updated декодировать presentValue (только {0, 1, 2, 4}) в zha_event (button_<ep>_<action>).

Ссылки

  • Производитель: jethome.ru

  • API информации об обновлениях (JSON): fw.jethome.com/api/devices/ws7/info

  • OTA-индекс (Z2M): github.com/Koenkk/zigbee-OTA

  • Стандарт ZCL: ZigBee Cluster Library Specification, ZCL r8 — кластеры 0x0000 Basic, 0x0001 Power Configuration, 0x0002 Device Temperature Configuration, 0x0004 Groups, 0x0006 On/Off, 0x0012 Multistate Input (Basic), 0x0019 OTA Upgrade.