Модуль дискретных входов WS7: детали Zigbee протокола
Описание Zigbee-протокола устройства JetHome WS7 для интеграции с Zigbee-координаторами.
См.также
Трехканальный Zigbee Модуль дискретных входов JetHome WS7 — описание устройства, монтаж и эксплуатация.
Совместимость Zigbee устройств JetHome с различными экосистемами — таблица совместимости с Zigbee-стеками.
Содержание
Идентификация устройства
Поле |
Значение |
|---|---|
Vendor |
|
Модель |
|
|
|
|
|
Profile ID |
|
Device ID (на всех endpoint) |
|
HW Version ( |
|
Stack Version ( |
|
ZCL Version ( |
|
Power Source ( |
|
Тип Zigbee-устройства |
End Device (sleepy) |
Touchlink |
не поддерживается |
Green Power |
не поддерживается |
Важно
Сопоставление в драйвере — только по паре manufacturerName + modelIdentifier.
Device ID 0x00FF слишком общий, на него не опираться.
Полный набор атрибутов кластера genBasic (0x0000) на EP1
Attr ID |
Имя |
Тип |
Доступ |
Значение |
|---|---|---|---|---|
|
ZCLVersion |
|
R |
|
|
ApplicationVersion |
|
R |
соответствует текущей |
|
StackVersion |
|
R |
|
|
HWVersion |
|
R |
|
|
ManufacturerName |
|
R |
|
|
ModelIdentifier |
|
R |
|
|
DateCode |
|
R |
|
|
PowerSource |
|
R |
|
|
PhysicalEnvironment |
|
R/W |
unspecified |
|
SWBuildID |
|
R |
соответствует |
|
ClusterRevision |
|
R |
|
Внимание
По спецификации ZCL SWBuildID (0x4000) — octet string.
На WS7 он объявлен как uint8. Декодеру надо учитывать тип uint8 при чтении.
Примечание
Команда сервера Reset to Factory Defaults поддерживается.
Endpoint-карта
WS7 объявляет три endpoint, физически соответствующие трём симметричным дискретным входам.
Endpoint |
Псевдоним |
Назначение |
|---|---|---|
|
|
Главный: Basic, PowerCfg, DeviceTempCfg, Groups, MultistateInput для входа №1; OUT — OnOff, OTA |
|
|
MultistateInput для входа №2; OUT — OnOff |
|
|
MultistateInput для входа №3; OUT — OnOff |
Примечание
Все три endpoint используют Profile = 0x0104, DeviceID = 0x00FF.
Примечание
На устройстве физически есть и сервисная (служебная) кнопка для commissioning, factory reset и переключения режима работы входов. Эта кнопка не отправляет ZCL-сообщений по сети — координатор не получит от неё событий.
Кластеры
Endpoint 1 (главный)
Server (Input)
Cluster ID |
Имя |
Назначение |
|---|---|---|
|
Basic |
стандартные атрибуты идентификации (см. Модуль дискретных входов WS7: детали Zigbee протокола) |
|
Power Configuration |
состояние батареи, reportable |
|
Device Temperature Configuration |
объявлен reportable, но значение всегда 0 — измерение не реализовано |
|
Groups |
стандартный |
|
Multistate Input (Basic) |
события входа №1 |
Client (Output) — устройство отправляет команды наружу
Cluster ID |
Имя |
Когда |
|---|---|---|
|
On/Off |
всегда |
|
OTA Upgrade |
всегда |
Важно
Кластер Identify (0x0003) отсутствует — не объявлен в Simple Descriptor.
Не использовать Identify / IdentifyQuery.
Endpoint 2 / Endpoint 3
Симметричны, отличаются только привязкой к физическому входу.
Сторона |
Cluster ID |
Имя |
|---|---|---|
Server (In) |
|
Multistate Input (Basic) — события входа №2 (EP2) / входа №3 (EP3) |
Client (Out) |
|
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, принимает только четыре значения:
Значение |
Семантика |
В каком режиме приходит |
Триггер |
|---|---|---|---|
|
release |
HOLD |
отпускание (размыкание) входа |
|
single |
CLICK |
одиночный клик |
|
double |
CLICK |
двойной клик |
|
hold |
HOLD |
нажатие (замыкание) входа |
Канал (вход №1 / №2 / №3) определяется по полю endpoint входящего ZCL-фрейма
(1 → in1, 2 → in2, 3 → in3).
Параллельный канал — исходящие OnOff (cluster 0x0006)
Помимо отчёта Multistate Input, для некоторых событий устройство дополнительно
отправляет команду по genOnOff. Маршрут адресации — binding → groups → fallback:
Binding (
addrMode = AddrNotPresent) — основной путь. Устройство использует свою таблицу binding-ов.Groups — для каждой группы, в которой состоит endpoint, дополнительно шлётся та же команда (
AddrGroup).Coordinator (fallback) —
afAddr16Bit,shortAddr = 0x0000,endpoint = 1. Но в этот fallback идёт только Multistate Input report; OnOff-команда на координатор по fallback-пути НЕ отправляется.
Соответствие событий и команд
Событие |
Режим |
OnOff-команда ( |
Multistate ( |
|---|---|---|---|
single |
CLICK |
Toggle (cmd |
|
double |
CLICK |
(не отправляется) |
|
hold (press) |
HOLD |
On (cmd |
|
release |
HOLD |
Off (cmd |
|
Важно
Чтобы координатор получал 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 |
Имя |
Тип |
Доступ |
Семантика |
|---|---|---|---|---|
|
BatteryVoltage |
|
R + Reportable |
десятые доли вольта ( |
|
BatteryPercentageRemaining |
|
R + Reportable |
формат ZCL: 1 = 0.5 %, |
Примечание
Атрибуты 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 V → 0 %, voltage = 3.0 V → 100 %. По сети
передаётся уже 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 |
Имя |
Тип |
Доступ |
Значение |
|---|---|---|---|---|
|
CurrentTemperature |
|
R + Reportable |
всегда 0 |
Атрибут декларирован, но измерение чипа не реализовано — значение никогда не меняется.
Совет
Драйверу: можно игнорировать или показывать как «не реализовано».
Не вытягивать в e.temperature() — это введёт пользователя в заблуждение.
OTA Upgrade (cluster 0x0019, EP1, client side)
Атрибуты (все R | Cli)
Attr ID |
Имя |
Тип |
Начальное значение |
|---|---|---|---|
|
UpgradeServerID |
|
|
|
FileOffset |
|
|
|
CurrentFileVersion |
|
текущая версия ( |
|
CurrentZigBeeStackVersion |
|
|
|
DownloadedFileVersion |
|
|
|
DownloadedZigBeeStackVersion |
|
|
|
ImageUpgradeStatus |
|
|
|
ManufacturerID |
|
|
|
ImageTypeID |
|
|
|
MinimumBlockPeriod |
|
|
|
ClusterRevision |
|
|
Триггеры запроса OTA
Устройство само инициирует OTA-проверку (Query Next Image Request):
сразу после успешного pairing-а;
при коротком клике сервисной кнопки, если устройство в сети.
Параметры для OTA-сервера координатора
Параметр |
Значение |
|---|---|
|
|
|
|
Минимальная новая |
|
API информации об обновлениях |
fw.jethome.com/api/devices/ws7/info — возвращает JSON со списком доступных версий и URL образов |
Зеркало (используется в zigbee-OTA) |
Поток OTA — стандартный ZCL: Query Next Image Request → Query Next Image Response →
Image Block Request / Image Block Response → Upgrade End Request →
Upgrade End Response. Vendor-расширений нет.
Pairing, factory reset, rejoin
WS7 — End Device, поэтому требует router/coordinator-родителя в радиусе.
Pairing (вход в сеть)
Шаг |
Действие пользователя |
Поведение устройства |
|---|---|---|
1 |
Координатор переводится в |
— |
2 |
Удержать сервисную кнопку ≥ 5 секунд не находясь в сети |
Красный светодиод моргает 30 секунд. Запускается NWK Steering. |
3 |
— |
Устройство ищет сеть, делает association, при успехе перестаёт моргать. |
4 |
— |
Стартует периодический замер батареи (60 мин). |
Важно
Нет варианта «permit_join → device joins automatically».
Pairing требует физического long-press 5 секунд.
Factory reset — два пути
Длинное удержание в сети — сервисная кнопка ≥ 5 секунд: красный светодиод 1 секунду, очистка NV-данных, local leave, программная перезагрузка.
Удержание при подаче питания — если в момент power-on кнопка нажата: красный светодиод ≈ 1 секунду, очистка NV-данных.
Примечание
После factory reset режим входов сбрасывается в CLICK (значение по умолчанию).
Rejoin
При потере parent-устройства WS7 ставит событие rejoin и пытается восстановить сеть. После успеха:
через 5 секунд приходит свежий батарейный отчёт;
отложенные input-события воспроизводятся;
замер батареи перепланируется на штатный 60-минутный цикл.
Что должен делать драйвер координатора
Минимальный пакет действий после interview:
Прочитать атрибуты Basic на EP1:
0x0004(ManufacturerName),0x0005(ModelIdentifier),0x0003(HWVersion),0x0001(ApplicationVersion),0x4000(SWBuildID),0x0006(DateCode).Выполнить 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
Опционально — отправить
Configure ReportingдляgenPowerCfg(batteryVoltage,batteryPercentageRemaining). Команда будет принята, но частоту это не изменит — реальный период задан в устройстве (60 минут).Не пытаться делать
Configure ReportingдляgenMultistateInputилиgenOnOff— отчёты идут вне reporting manager-а.Не пытаться управлять устройством через входящие OnOff — команды игнорируются.
Не использовать Identify (
0x0003) — кластер отсутствует.Multistate Input принимать как
attributeReport/readResponseна cluster0x0012, attr0x0055, без bind (приходит unicast на0x0000:1).
Декодирование событий (рекомендуемая модель)
Каноническая таблица
EP |
|
Режим |
OnOff (если bind) |
Имя |
|---|---|---|---|---|
1 |
|
CLICK |
Toggle |
|
1 |
|
CLICK |
— |
|
1 |
|
HOLD |
On |
|
1 |
|
HOLD |
Off |
|
2 |
|
CLICK |
Toggle |
|
2 |
|
CLICK |
— |
|
2 |
|
HOLD |
On |
|
2 |
|
HOLD |
Off |
|
3 |
|
CLICK |
Toggle |
|
3 |
|
CLICK |
— |
|
3 |
|
HOLD |
On |
|
3 |
|
HOLD |
Off |
|
Псевдокод декодера
on attribute_report(cluster, attr, value, endpoint):
if cluster == 0x0001: # genPowerCfg
if attr == 0x0021 and value < 255:
publish("battery", value / 2.0) # %
if attr == 0x0020 and value < 255:
publish("voltage", value * 100) # mV
return
if cluster == 0x0012 and attr == 0x0055: # genMultistateInput
action_map = {0: "release", 1: "single", 2: "double", 4: "hold"}
if value not in action_map:
return # WS7 других значений не шлёт
ep_map = {1: "in1", 2: "in2", 3: "in3"}
publish("action", f"{action_map[value]}_{ep_map[endpoint]}")
return
on command_received(cluster, command, endpoint):
if cluster == 0x0006: # genOnOff (через bind)
ep_map = {1: "in1", 2: "in2", 3: "in3"}
cmd_map = {0x00: "off", 0x01: "on", 0x02: "toggle"}
publish("onoff_command", f"{cmd_map[command]}_{ep_map[endpoint]}")
Тест-кейсы для валидации драйвера
Идентификация.
manufacturerName="JetHome",modelIdentifier="WS7",hwVersion=2, profile0x0104, deviceID0x00FF. Матчинг только по пареmanufacturerName+modelIdentifier.EP-карта. Active EP вернёт
[1, 2, 3]. Simple Descriptor EP1 содержит in:[0x0000, 0x0001, 0x0002, 0x0004, 0x0012], out:[0x0006, 0x0019]. EP2 / EP3: in[0x0012], out[0x0006].Identify не отвечает. Команды cluster
0x0003идут без ответа (илиDefault Response: UNSUP_CLUSTER_COMMAND).Battery init. Сразу после pairing-а приходит
Report Attributesот EP1 на cluster0x0001с обоими атрибутами (0x0020и0x0021) одним фреймом.Battery period. Следующий батарейный отчёт — через ~60 минут.
Configure Reportingтемп не меняет.CLICK / single на EP=1. Одиночный клик:
Report Attributescluster0x0012attr0x0055value=1от EP=1; плюс (если bind на0x0006)Toggleот EP=1. Декодер выдаётsingle_in1.CLICK / double на EP=2. Двойной клик:
presentValue=2от EP=2; OnOff не приходит. Декодер выдаётdouble_in2.HOLD / press+release на EP=3. Нажатие →
presentValue=4+ (bind)Onот EP=3 →hold_in3. Отпускание →presentValue=0+ (bind)Offот EP=3 →release_in3.Triple click невозможен. Три клика подряд (с интервалом < 200 мс между 2-м и 3-м) дадут только одно событие
double— третий клик игнорируется.Switch mode CLICK ↔ HOLD. Двойной клик сервисной кнопки: зелёный светодиод 1 или 2 раза. По сети событие не доставляется. После reboot режим сохраняется.
Factory reset → CLICK. После long-press 5 сек или удержания при boot режим сбрасывается в
CLICK.Incoming OnOff игнорируется. Отправить
On/Off/Toggleот координатора на EP1 cluster0x0006. Реакции от устройства нет.OTA Query. На короткий клик сервисной кнопки устройство шлёт
Query Next Image RequestсmanufacturerCode=0xF123,imageType=0xF001.Pairing. Long-press 5 сек не в сети → NWK Steering → join (если координатор в
permit_join).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 — кластеры
0x0000Basic,0x0001Power Configuration,0x0002Device Temperature Configuration,0x0004Groups,0x0006On/Off,0x0012Multistate Input (Basic),0x0019OTA Upgrade.