Repository: zigpy/zha-device-handlers

Issue:
[Device Support Request] Sonoff SNZB-01 (Zigbee Button)
Hi tried to add a Sonoff SNZB-01 button but it only adds the device and a power entity:

![image](https://user-images.githubusercontent.com/39986400/102706450-f3719180-42cc-11eb-90ef-1e58d238e270.png)

There is no entity for the actual button function. 

So the button is currently out-of-the box for **normal** users unusable 
(except when using "zha_event" events or a custom sensor like e.g. [eventsensor)](https://github.com/azogue/eventsensor)
.

I saw the same issue being closed: [Issue 437](https://github.com/zigpy/zha-device-handlers/issues/437)
but it would be really helpful to have a quirk created for the button to help with automation.

That's why I reopened the issue/created this request.

The Zigbee device signature is as follows:

```
{
  "node_descriptor": "NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=0, maximum_buffer_size=80, maximum_incoming_transfer_size=160, server_mask=0, maximum_outgoing_transfer_size=160, descriptor_capability_field=0)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0000",
      "in_clusters": [
        "0x0000",
        "0x0001",
        "0x0003"
      ],
      "out_clusters": [
        "0x0003",
        "0x0006"
      ]
    }
  },
  "manufacturer": "eWeLink",
  "model": "WB01",
  "class": "zigpy.device.Device"
}
```

Here the log file from adding the device:

```
Device 0x137f (00:12:4b:00:22:14:e7:21) joined the network
[0x137f] Requesting 'Node Descriptor'
Tries remaining: 2
[0x137f] Extending timeout for 0xb4 request
Device 0x137f (00:12:4b:00:22:14:e7:21) joined the network
Skip initialization for existing device 00:12:4b:00:22:14:e7:21
[0x137f:zdo] ZDO request ZDOCmd.Device_annce: [0x137F, 00:12:4b:00:22:14:e7:21, 128]
[0x137f] Node Descriptor: NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=0, maximum_buffer_size=80, maximum_incoming_transfer_size=160, server_mask=0, maximum_outgoing_transfer_size=160, descriptor_capability_field=0)
[0x137f] Discovering endpoints
Tries remaining: 3
[0x137f] Extending timeout for 0xb6 request
[0x137f] Discovered endpoints: [1]
[0x137f:1] Discovering endpoint information
Tries remaining: 3
[0x137f] Extending timeout for 0xb8 request
[0x137f:1] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0, device_version=0, input_clusters=[0, 3, 1], output_clusters=[6, 3])
[0x137f] Extending timeout for 0xba request
[0x137f:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=186 command_id=Command.Read_Attributes_rsp>
[0x137f:1] Manufacturer: eWeLink
[0x137f:1] Model: WB01
Checking quirks for eWeLink WB01 (00:12:4b:00:22:14:e7:21)
Considering <class 'bellows.zigbee.application.EZSPCoordinator'>
Fail because device_type mismatch on at least one endpoint
Considering <class 'zhaquirks.gledopto.soposhgu10.SoposhGU10'>
Fail because endpoint list mismatch: {11, 13} {1}
Considering <class 'zhaquirks.netvox.z308e3ed.Z308E3ED'>
Fail because device_type mismatch on at least one endpoint
Considering <class 'zhaquirks.philips.rom001.PhilipsROM001'>
Fail because device_type mismatch on at least one endpoint
Considering <class 'zhaquirks.philips.rwl020.PhilipsRWL020'>
Fail because endpoint list mismatch: {1, 2} {1}
Considering <class 'zhaquirks.philips.rwl021.PhilipsRWL021'>
Fail because endpoint list mismatch: {1, 2} {1}
Considering <class 'zhaquirks.smartthings.multi.SmartthingsMultiPurposeSensor'>
Fail because device_type mismatch on at least one endpoint
Considering <class 'zhaquirks.smartthings.tag_v4.SmartThingsTagV4'>
Fail because device_type mismatch on at least one endpoint
Considering <class 'zhaquirks.xbee.xbee3_io.XBee3Sensor'>
Fail because endpoint list mismatch: {232, 230} {1}
Considering <class 'zhaquirks.xbee.xbee_io.XBeeSensor'>
Fail because endpoint list mismatch: {232, 230} {1}
device - 0x137F:00:12:4b:00:22:14:e7:21 entering async_device_initialized - is_new_join: True
device - 0x137F:00:12:4b:00:22:14:e7:21 has joined the ZHA zigbee network
[0x137F](WB01): started configuration
[0x137F:ZDO](WB01): 'async_configure' stage succeeded
[0x137f] Extending timeout for 0xbc request
[0x137f] Extending timeout for 0xbe request
[0x137f] Extending timeout for 0xc0 request
[0x137F:1:0x0001]: bound 'power' cluster: Status.SUCCESS
[0x137f] Extending timeout for 0xc2 request
[0x137F:1:0x0000]: bound 'basic' cluster: Status.SUCCESS
[0x137F:1:0x0000]: finished channel configuration
[0x137f] Extending timeout for 0xc4 request
[0x137F:1:0x0006]: bound 'on_off' cluster: Status.SUCCESS
[0x137F:1:0x0006]: finished channel configuration
[0x137f:1:0x0001] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=194 command_id=Command.Configure_Reporting_rsp>
[0x137F:1:0x0001]: reporting 'battery_voltage' attr on 'power' cluster: 3600/10800/1: Result: '[[ConfigureReportingResponseRecord(status=0)]]'
[0x137f] Extending timeout for 0xc6 request
[0x137f:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=196 command_id=Command.Read_Attributes_rsp>
[0x137F:1:0x0000]: initializing channel: from_cache: False
[0x137f:1:0x0001] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=198 command_id=Command.Configure_Reporting_rsp>
[0x137F:1:0x0001]: reporting 'battery_percentage_remaining' attr on 'power' cluster: 3600/10800/1: Result: '[[ConfigureReportingResponseRecord(status=0)]]'
[0x137F:1:0x0001]: finished channel configuration
[0x137F:1:0x0001]: 'async_configure' stage succeeded
[0x137F:1:0x0000]: 'async_configure' stage succeeded
[0x137F:1:0x0006]: 'async_configure' stage succeeded
[0x137F](WB01): completed configuration
[0x137F](WB01): stored in registry: ZhaDeviceEntry(name='eWeLink WB01', ieee='00:12:4b:00:22:14:e7:21', last_seen=1608444221.7844937)
[0x137f] Extending timeout for 0xc8 request
[0x137f:1:0x0003] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=200 command_id=Command.Default_Response>
[0x137F:1:0x0003]: executed 'trigger_effect' command with args: '(2, 0)' kwargs: '{}' result: [64, <Status.UNSUP_CLUSTER_COMMAND: 129>]
[0x137F](WB01): started initialization
[0x137F:ZDO](WB01): 'async_initialize' stage succeeded
[0x137f] Extending timeout for 0xca request
[0x137f] Extending timeout for 0xcc request
[0x137F:1:0x0006]: initializing channel: from_cache: False
[0x137f:1:0x0001] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=202 command_id=Command.Read_Attributes_rsp>
[0x137f:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=204 command_id=Command.Read_Attributes_rsp>
[0x137F:1:0x0000]: initializing channel: from_cache: False
[0x137F:1:0x0001]: 'async_initialize' stage succeeded
[0x137F:1:0x0000]: 'async_initialize' stage succeeded
[0x137F:1:0x0006]: 'async_initialize' stage succeeded
[0x137F](WB01): power source: Battery or Unknown
[0x137F](WB01): completed initialization
```

Thx a lot & Merry Christmas


Discussion:
Buttons fire events. Developer tools -> events -> in listen type in `zha_event` Then hit listen. Press buttons and you should see the event stream for the button.
@dmulcahey 
Thx for the answer and explanation. 

I was just hoping that we could have a kind of sensor with state changes for the button to easily integrate it with automations.

At the moment I use the following (not so pretty) solution:

1. Get the zha_event details. In my case when I single short push the button:
```
{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-20T06:48:27.272033+00:00",
    "context": {
        "id": "fe1bcbfd4df1f47d7f15f71289eda41f",
        "parent_id": null,
        "user_id": null
    }
}
```
The possible "command" values for the Sonoff SNZB-01 button are:
- toggle (Short single push)
- on (Quick double push)
- off (Long single push)

2. Install [eventsensor](https://github.com/azogue/eventsensor) to have a custom sensor entity for your button.

3. Setup eventsensor to have the commands available as states. 

![image](https://user-images.githubusercontent.com/39986400/102714818-49195e80-430c-11eb-9e2e-d5829a5be7b3.png)

4. Setup 3 automations. One for each state (on, off, toggle). This is my 'off' example:

![image](https://user-images.githubusercontent.com/39986400/102714900-cfce3b80-430c-11eb-9d3f-5c9b724246fc.png)

It is important to fire the zha_event with an empty (or other than 'on/off/toggle') command.
Without the zha_event your trigger will not react on the same command twice since the state isn't changing.

I hope I could help others who struggle with the button integration as well.

Feel free to close this issue if there is no other better solution achievable.

Thx a lot.


These are momentary actions so persistent state doesn’t make sense. There should not be sensors for these aside from battery. To make things easier a quirk should be added to add device triggers for HA.
> It is important to fire the zha_event with an empty (or other than 'on/off/toggle') command.
> Without the zha_event your trigger will not react on the same command twice since the state isn't changing.

This is not true at all. Your automation is probably still running and you have `mode: single` set on the automation in your HA screenshot. 

Get me the full event capture for each action the button supports and we’ll add device triggers for it. 
> Get me the full event capture for each action the button supports and we’ll add device triggers for it.

Thx a million. That's even better 👍 

**Single short push:**
```
{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:12:03.503320+00:00",
    "context": {
        "id": "5693bd3eea9f992b70f9abfd8929a2e2",
        "parent_id": null,
        "user_id": null
    }
}
```

**Single long push:**
```
{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "off",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:13:05.028515+00:00",
    "context": {
        "id": "18ed708fb53de9e92105d56a0185ea50",
        "parent_id": null,
        "user_id": null
    }
}
```

**Quick double push:**
```
{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "on",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:14:18.221082+00:00",
    "context": {
        "id": "482bc8d948fecce50b83675fa5da8783",
        "parent_id": null,
        "user_id": null
    }
}
```

Thx again and Merry Christmas:

                                  _
                               .-(_)
                              / _/
                           .-'   \
                          /       '.
                        ,-~--~-~-~-~-,
                       {__.._...__..._}             ,888,
       ,888,          /\##"  6  6  "##/\          ,88' `88,
     ,88' '88,__     |(\`    (__)    `/)|     __,88'     `88
    ,88'   .8(_ \_____\_    '----'    _/_____/ _)8.       8'
    88    (___)\ \      '-.__    __.-'      / /(___)
    88    (___)88 |          '--'          | 88(___)
    8'      (__)88,___/                \___,88(__)
              __`88,_/__________________\_,88`__
             /    `88,       |88|       ,88'    \
            /        `88,    |88|    ,88'        \
           /____________`88,_\88/_,88`____________\
          /88888888888888888;8888;88888888888888888\
         /^^^^^^^^^^^^^^^^^^`/88\\^^^^^^^^^^^^^^^^^^\

Can you do me a huge favor? Can you make sure this stream of events is repeatable? The commands the button generates seem a bit odd for the interactions. I just want to make sure we do this right. Thanks.
Sure,

I clicked the button a few times for each possible event.

The possible commands are only: 
- toggle (Short single push)
- on (Quick double push)
- off (Long single push)

```
Event 12 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:51.747838+00:00",
    "context": {
        "id": "a97aff6ef2d4f408bef205588a94b10c",
        "parent_id": null,
        "user_id": null
    }
}

Event 11 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "off",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:50.973532+00:00",
    "context": {
        "id": "99671a1aba23918e7e06700dfdcf583d",
        "parent_id": null,
        "user_id": null
    }
}

Event 10 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "off",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:46.725064+00:00",
    "context": {
        "id": "d389bf7a289f6a9e755d96ca329bd01e",
        "parent_id": null,
        "user_id": null
    }
}

Event 9 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "off",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:42.352887+00:00",
    "context": {
        "id": "8bcb714643536a7b067f5d4f30e8ec24",
        "parent_id": null,
        "user_id": null
    }
}

Event 8 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "off",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:38.404510+00:00",
    "context": {
        "id": "6d492e493804d9e267d63b1e993325d5",
        "parent_id": null,
        "user_id": null
    }
}

Event 7 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "on",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:34.454259+00:00",
    "context": {
        "id": "71f876aeb5710c58b60f481676ed9865",
        "parent_id": null,
        "user_id": null
    }
}

Event 6 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:30.215167+00:00",
    "context": {
        "id": "ab6ba9354ad619657a3a093c0e4387ef",
        "parent_id": null,
        "user_id": null
    }
}

Event 5 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:19.428104+00:00",
    "context": {
        "id": "a1999832b3893cab3506672cb649f98a",
        "parent_id": null,
        "user_id": null
    }
}

Event 4 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "on",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:14.087347+00:00",
    "context": {
        "id": "e44488252bb316ef0ea69202ba46329a",
        "parent_id": null,
        "user_id": null
    }
}

Event 3 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "on",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:09.558862+00:00",
    "context": {
        "id": "60a17767482f97c001d71b563ae50c1d",
        "parent_id": null,
        "user_id": null
    }
}

Event 2 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:04.857847+00:00",
    "context": {
        "id": "e1f4574f7aebc0dfb8f66ff3e63a0d4e",
        "parent_id": null,
        "user_id": null
    }
}

Event 1 fired 9:38 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:38:00.185427+00:00",
    "context": {
        "id": "7a04f516f47fc5f84fb079a77ec83a3d",
        "parent_id": null,
        "user_id": null
    }
}

Event 0 fired 9:37 PM:

{
    "event_type": "zha_event",
    "data": {
        "device_ieee": "00:12:4b:00:22:14:e7:21",
        "unique_id": "00:12:4b:00:22:14:e7:21:1:0x0006",
        "device_id": "18ec46f405e34403911f5204855da3b5",
        "endpoint_id": 1,
        "cluster_id": 6,
        "command": "toggle",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-12-23T13:37:54.782768+00:00",
    "context": {
        "id": "1d297856341aeb59abfdf766cf7afba1",
        "parent_id": null,
        "user_id": null
    }
}

```
@dmulcahey I have a few of these and obviously would like them supported. Anything I can do to help?
Hi, 
I fall exactly on the same problem as @JMan7777 : having only a power state when the snzb-01 button is configured is very disturbing.
So I searched for a solution and fall on this bug and then I searched on a solution to handle the zha_event with nodered and found a simple tuto here (last part of it) : https://theautomationplace.com/2019/10/14/controlling-a-google-home-with-a-zigbee-button-and-node-red/

So I agree that something should be at least documented to help basic users like me to quickly understand what to do when having such a device. A zha_device_handler is perhaps a solution to propose a minimal behviour on button press...

Hi, I recommend using this blueprint: https://community.home-assistant.io/t/zha-sonoff-zigbee-switch/268628

It will let you create automations that respond directly to various button inputs with a nice UI.

Resolving patch:
diff --git a/zhaquirks/sonoff/__init__.py b/zhaquirks/sonoff/__init__.py
new file mode 100644
--- /dev/null
+++ b/zhaquirks/sonoff/__init__.py
@@ -0,0 +1 @@
+"""Quirks for Sonoff devices."""
diff --git a/zhaquirks/sonoff/button.py b/zhaquirks/sonoff/button.py
new file mode 100644
--- /dev/null
+++ b/zhaquirks/sonoff/button.py
@@ -0,0 +1,78 @@
+"""Device handler for eWeLink WB01."""
+import logging
+
+from zigpy.profiles import zha
+from zigpy.quirks import CustomDevice
+from zigpy.zcl.clusters.general import Basic, Identify, OnOff, PowerConfiguration
+
+from zhaquirks.const import (
+    BUTTON,
+    COMMAND,
+    COMMAND_OFF,
+    COMMAND_ON,
+    COMMAND_TOGGLE,
+    DEVICE_TYPE,
+    DOUBLE_PRESS,
+    ENDPOINTS,
+    INPUT_CLUSTERS,
+    LONG_PRESS,
+    MODELS_INFO,
+    OUTPUT_CLUSTERS,
+    PROFILE_ID,
+    SHORT_PRESS,
+)
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class SonoffButton(CustomDevice):
+    """Custom device representing sonoff devices."""
+
+    def __init__(self, *args, **kwargs):
+        """Init."""
+        super().__init__(*args, **kwargs)
+
+    signature = {
+        # <SimpleDescriptor endpoint=1 profile=260 device_type=0 device_version=0
+        # input_clusters=[0, 3, 1] output_clusters=[6, 3]>
+        MODELS_INFO: [("eWeLink", "WB01")],
+        ENDPOINTS: {
+            1: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                INPUT_CLUSTERS: [
+                    Basic.cluster_id,
+                    PowerConfiguration.cluster_id,
+                    Identify.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [
+                    Identify.cluster_id,
+                    OnOff.cluster_id,
+                ],
+            },
+        },
+    }
+
+    replacement = {
+        ENDPOINTS: {
+            1: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                INPUT_CLUSTERS: [
+                    Basic.cluster_id,
+                    PowerConfiguration.cluster_id,
+                    Identify.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [
+                    Identify.cluster_id,
+                    OnOff.cluster_id,
+                ],
+            },
+        }
+    }
+
+    device_automation_triggers = {
+        (SHORT_PRESS, BUTTON): {COMMAND: COMMAND_TOGGLE},
+        (DOUBLE_PRESS, BUTTON): {COMMAND: COMMAND_ON},
+        (LONG_PRESS, BUTTON): {COMMAND: COMMAND_OFF},
+    }


Test patch: