WS7 digital input module: Zigbee protocol details

Zigbee protocol description of the JetHome WS7 device for integration with Zigbee coordinators.

See also

Device identification

Field

Value

Vendor

JetHome

Model

WS7

genBasic.manufacturerName (0x0004)

JetHome

genBasic.modelIdentifier (0x0005)

WS7

Profile ID

0x0104 (Home Automation)

Device ID (on all endpoints)

0x00FF (test/generic)

HW Version (0x0003)

2

Stack Version (0x0002)

1

ZCL Version (0x0000)

8 (ZCL r8)

Power Source (0x0007)

0x03 (Battery)

Zigbee device type

End Device (sleepy)

Touchlink

unsupported

Green Power

unsupported

Important

Matching in the driver is only on the pair manufacturerName + modelIdentifier. Device ID 0x00FF is too generic to rely on.

Full set of cluster attributes genBasic (0x0000) on EP1

Attr ID

Name

Type

Access

Value

0x0000

ZCLVersion

uint8

R

8

0x0001

ApplicationVersion

uint8

R

current 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

consistent with OTA CurrentFileVersion

0xFFFD

ClusterRevision

uint16

R

0x0002

Attention

On the ZCL spec sheet SWBuildID (0x4000) - octet string. On WS7 it is declared as uint8. Decoder needs to consider uint8 type when reading.

Note

The server command Reset to Factory Defaults is supported.

Endpoint map

WS7 declares three endpoints physically corresponding to three symmetric discrete inputs.

Endpoint

Alias

Destination

1

in1

Main: Basic, PowerCfg, DeviceTempCfg, Groups, MultistateInput for input #1; OUT - OnOff, OTA

2

in2

MultistateInput for input #2; OUT - OnOff

3

in3

MultistateInput for input #3; OUT - OnOff

Note

All three endpoints use Profile = 0x0104, DeviceID = 0x00FF.

Note

The device physically has a service button for commissioning, factory reset, and switching the mode of inputs. This button does not send ZCL messages over the network - the coordinator will not receive events from it.

Clusters

Endpoint 1 (main)

Server (Input)

Cluster ID

Name

Destination

0x0000

Basic

standard identification attributes (see WS7 digital input module: Zigbee protocol details)

0x0001

Power Configuration

battery condition, reportable

0x0002

Device Temperature Configuration

reportable is declared, but value is always 0 - measurement is not implemented

0x0004

Groups

standard

0x0012

Multistate Input (Basic)

input event #1

Client (Output) - the device sends commands outward

Cluster ID

Name

When

0x0006

On/Off

always

0x0019

OTA Upgrade

always

Important

Cluster Identify (0x0003) missing - not declared in Simple Descriptor. Do not use Identify / IdentifyQuery.

Endpoint 2 / Endpoint 3

Symmetrical, differing only in the binding to the physical input.

Side

Cluster ID

Name

Server (In)

0x0012

Multistate Input (Basic) - events of input No. 2 (EP2) / input No. 3 (EP3)

Client (Out)

0x0006

On/Off

Summary diagram of directions

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 ───────────────────────────────┤
   ▼                                                          ▼

Input logic: two modes of operation

CLICK and HOLD modes

Each of the three inputs operates in one of the two modes common to all three inputs:

Mode

Default

Which events are recognized

CLICK

yes

single and double click

HOLD

pressing edge and releasing edge

Note

The current mode is stored in the device’s non-volatile memory and survives a factory reset. After factory reset, the mode returns to CLICK.

Mode switching

Switching - double click on the service button of the device. Confirmation:

  • CLICK → green LED flashes 1 time;

  • HOLD → the green LED flashes 2 times.

Attention

** No mode change event is sent over the network.** The coordinator will not be notified of the mode change. The current mode can be recognized only indirectly - by the type of the first events received.

Decoding genMultistateInput.PresentValue (attr 0x0055)

The attribute uint16, takes only four values:

Value

Semantics

In which mode comes

Trigger

0

release

HOLD

releasing (opening) the input

1

single

CLICK

single click

2

double

CLICK

double click

4

hold

HOLD

pressing (shorting) the input

The channel (input #1 / #2 / #3) is defined by the endpoint field of the incoming ZCL frame (1in1, 2in2, 3in3).

Parallel channel - outgoing OnOff (cluster 0x0006)

In addition to the Multistate Input report, for some events the device additionally sends a command to genOnOff. The addressing route is binding groups fallback:

  1. Binding (addrMode = AddrNotPresent) is the primary path. The device uses its own binding table.

  2. Groups - the same command (AddrGroup) is additionally sent for each group the endpoint is a member of.

  3. Coordinator (fallback) - afAddr16Bit, shortAddr = 0x0000, endpoint = 1. But this fallback only receives Multistate Input report; OnOff command is NOT sent to the coordinator via fallback path.

Matching events and commands

Event

Mode

OnOff command (0x0006)

Multistate (0x0012)

single

CLICK

Toggle (cmd 0x02)

presentValue = 1

double

CLICK

(not sending)

presentValue = 2

hold (press)

HOLD

On (cmd 0x01)

presentValue = 4

release

HOLD

Off (cmd 0x00)

presentValue = 0

Important

For the coordinator to receive OnOff commands from the device, must be ZDO Bind cluster 0x0006 for each of EP1 / EP2 / EP3 to the coordinator. Without binding, OnOff commands will not reach the coordinator.

Multistate Input comes to the coordinator always, without binding - the device sends it by unicast to 0x0000:1.

It is not possible to control the device via OnOff

The device ignores incoming OnOff commands from the coordinator (callback is empty). Any attempt to control via On / Off / Toggle to WS7 is bypassed. OnOff on WS7 is a sender-only channel.

Processing timings

Parameter

Value

Contact debouncing

5 ms

Long press (for inputs)

2000 ms.

Double-click window

200 ms

Long press of the service button

5000 ms

Power Configuration / Battery

Attributes on EP1, cluster 0x0001

Attr ID

Name

Type

Access

Semantics

0x0020

BatteryVoltage

uint8

R + Reportable

tenths of a volt (30 ≡ 3.0 V); by ZCL - × 100 mV

0x0021

BatteryPercentageRemaining

uint8

R + Reportable

ZCL format: 1 = 0.5 %, 200 (0xC8) = 100 %

Note

The attributes batteryAlarmState (0x003E), batteryAlarmMask, batteryRatedVoltage, batterySize not declared - the device does not send them and does not respond to read them.

Driver-side decoding

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

This matches the standard behavior of Battery 1 from ZCL r8. WS7 doesn’t have a “don’t divide percentage by 2” override - division is mandatory.

The internal formula for the percentage

The device converts the voltage to a percentage linearly over the range 2.0 V 3.0 V:

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

That is voltage = 2.0 V0 %, voltage = 3.0 V100 %. Over the network it is already pct * 2 to BatteryPercentageRemaining.

Report period

Trigger

Period

Regular timer

60 minutes (fixed)

After starting the device

once right after init

Short click of the service button on the network

when pressed (also OTA query)

After rejoin

5 seconds after reconnection

Warning

Configure Reporting from the coordinator does not affect the battery report frequency. The period (60 minutes) is set internally and is not reconfigurable. The command Configure Reporting is received by the stack without error, but the actual period remains the same. It is possible to send Configure Reporting (for compatibility with the standard interview procedure), but it is not possible to expect the period to change.

Note

The report always contains both attributes (0x0020 + 0x0021) one frame Report Attributes from EP1 to 0x0000:1.

Device Temperature Configuration (cluster 0x0002, EP1)

Attr ID

Name

Type

Access

Value

0x0000

CurrentTemperature

int16

R + Reportable

Always 0

The attribute is declared, but the chip measurement is not implemented - the value never changes.

Tip

Driver: can be ignored or shown as “not implemented”. Do not pull in e.temperature() - this will mislead the user.

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

Attributes (all R | Cli)

Attr ID

Name

Type

Initial value

0x0000

UpgradeServerID

IEEE

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

0x0001

FileOffset

uint32

0xFFFFFFFF

0x0002

CurrentFileVersion

uint32

current version (0x0000000F for current release = 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 request triggers

The device itself initiates an OTA check (Query Next Image Request):

  • right after a successful match;

  • when the service button is clicked briefly if the device is online.

Parameters for the coordinator’s OTA server

Parameter

Value

manufacturerCode

0xF123

imageType

0xF001

Minimum new fileVersion for upgrade

> CurrentFileVersion

Update Information API

fw.jethome.com/api/devices/ws7/info - Returns JSON with a list of available versions and image URLs

Mirror (used in zigbee-OTA)

github.com/Koenkk/zigbee-OTA

OTA stream - standard ZCL: Query Next Image RequestQuery Next Image ResponseImage Block Request / Image Block ResponseUpgrade End RequestUpgrade End Response. There are no Vendor extensions.

Pairing, factory reset, rejoin

WS7 is an End Device, so it requires a router/coordinator parent in the radius.

Pairing (logging on to the network)

Step

User Action

Device behavior

1

The coordinator is being transferred to permit_join

2

Hold down the service button for ≥ 5 seconds outside the network

Red LED blinks for 30 seconds. NWK Steering is started.

3

The device searches for a network, makes a connection, and stops blinking if successful.

4

Starts periodic battery metering (60 min).

Important

There is no option “permit_join → device joins automatically”. Pairing requires a physical long-press of 5 seconds.

Factory reset - two ways

  1. Long network hold - service button ≥ 5 seconds: red LED 1 second, clearing NV data, local leave, program reboot.

  2. Power-on hold - if the button is pressed during power-on: red LED ≈ 1 second, clearing NV data.

Note

After factory reset, the input mode is reset to CLICK (default value).

Rejoin

When a parent device is lost, WS7 puts a rejoin event and tries to restore the network. Upon success:

  • 5 seconds later a fresh battery report comes in;

  • pending input events are replayed;

  • battery metering is rescheduled to the standard 60-minute cycle.

What the coordinator driver is supposed to do

Minimum package of actions after the interview:

  1. Read Basic attributes on EP1: 0x0004 (ManufacturerName), 0x0005 (ModelIdentifier), 0x0003 (HWVersion), 0x0001 (ApplicationVersion), 0x4000 (SWBuildID), 0x0006 (DateCode).

  2. Run ZDO Bind for outgoing device clusters so that the user receives all events:

    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. Optionally - send Configure Reporting for genPowerCfg (batteryVoltage, batteryPercentageRemaining). The command will be accepted, but frequency will not change - the real period is set in the device (60 minutes).

  4. Do not try to do Configure Reporting for genMultistateInput or genOnOff - reports go outside the reporting manager.

  5. Do not attempt to control the device via incoming OnOff - commands are ignored.

  6. Do not use Identify (0x0003) - no cluster.

  7. Multistate Input accept as attributeReport / readResponse on cluster 0x0012, attr 0x0055, without bind (comes unicast to 0x0000:1).

Test cases for driver validation

  1. Identification. manufacturerName="JetHome", modelIdentifier="WS7", hwVersion=2, profile 0x0104, deviceID 0x00FF. Matching only on the pair manufacturerName + modelIdentifier.

  2. EP card. Active EP will return [1, 2, 3]. Simple Descriptor EP1 contains in: [0x0000, 0x0001, 0x0002, 0x0004, 0x0012], out: [0x0006, 0x0019]. EP2 / EP3: in [0x0012], out [0x0006].

  3. Identify does not respond. Cluster 0x0003 commands go unanswered (or Default Response: UNSUP_CLUSTER_COMMAND).

  4. Battery init. Immediately after pairing comes Report Attributes from EP1 to cluster 0x0001 with both attributes (0x0020 and 0x0021) in one frame.

  5. Battery period. The next battery report is in ~60 minutes. Configure Reporting temp does not change.

  6. CLICK / single on EP=1. Single click: Report Attributes cluster 0x0012 attr 0x0055 value=1 from EP=1; plus (if bind to 0x0006) Toggle from EP=1. Decoder outputs single_in1.

  7. CLICK / double on EP=2. Double click: presentValue=2 from EP=2; OnOff does not come. Decoder outputs double_in2.

  8. HOLD / press+release on EP=3. Press → presentValue=4 + (bind) On from EP=3 → hold_in3. Release → presentValue=0 + (bind) Off from EP=3 → release_in3.

  9. Triple click is not possible. Three consecutive clicks (with < 200ms interval between the 2nd and 3rd) will give only one event double - the third click is ignored.

  10. Switch mode CLICK ↔ HOLD. Double-click the service button: green LED 1 or 2 times. No event is delivered over the network. After reboot the mode is saved.

  11. Factory reset → CLICK. After long-pressing 5 sec or holding at boot, the mode is reset to CLICK.

  12. Incoming OnOff is ignored. Send On / Off / Toggle from coordinator to EP1 cluster 0x0006. No response from the device.

  13. OTA Query. On a short click of the service button, the device sends Query Next Image Request from manufacturerCode=0xF123, imageType=0xF001.

  14. Pairing. Long-press 5 sec off-net → NWK Steering → join (if coordinator in permit_join).

  15. Rejoin. After loss of parent: device reports rejoin, 5 sec after recovery comes fresh battery report + pending input events.

Z2m converter template

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 template

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

In the MultistateInputCluster.attribute_updated handler, decode presentValue ( {0, 1, 2, 4} only ) to zha_event (button_<ep>_<action>).

References

  • Manufacturer: jethome.com

  • Update Information API (JSON): fw.jethome.com/api/devices/ws7/info

  • OTA Index (Z2M): github.com/Koenkk/zigbee-OTA

  • ZCL Standard: ZigBee Cluster Library Specification, ZCL r8 - Clusters 0x0000 Basic, 0x0001 Power Configuration, 0x0002 Device Temperature Configuration, 0x0004 Groups, 0x0006 On/Off, 0x0012 Multistate Input (Basic), 0x0019 OTA Upgrade.