dresden-elektronik / deconz-rest-plugin

deCONZ REST-API plugin to control ZigBee devices
BSD 3-Clause "New" or "Revised" License
1.89k stars 496 forks source link

Xiaomi round push button #349

Closed diderikfrom closed 5 years ago

diderikfrom commented 6 years ago

The addition in (v9x) of double/triple/quadruple clicks broke the button pressed (1000), now I only have button released (1002) , as well as the multiple clicks (2002, 3002, 4002). Multiple clicks are great, but I it's a pity to lose button pressed and released. With button pressed, the switch works great as a push/pulse dimmer with a bit of scripting.

kristianwh commented 6 years ago

I also have this problem. Couldn't figure out why it didn't work, until I got different reponses on multiple clicks. This seems like a great feature, but I really need the button-press-release actions.

Try2Fly commented 6 years ago

How did you connect the switches to Deconz? I cannot get them to work. I can see it is connected by doing a GET call. But cannot see the switch in de webapp...

kristianwh commented 6 years ago

It is not showing in the webapp here either. Im using home assistant and the deconz component. Each press generates an event I can use to do something in an automation, e.g. turn on / off a light, etc. They are not supported as switches in the deconz ui, not sure if they follow the ZLL switch standard at all.

ebaauw commented 6 years ago

not sure if they follow the ZLL switch standard at all

As far as I know, ZLL only defines how a control device interacts with lights directly; there's no standard how a switch would connect to a gateway. Philips uses a manufacturer specific setup for their Hue dimmer switches. Most other switches don't even communicate with the gateway; deCONZ simply eavesdrops on the commands the switch sends to the lights and reverse-engineers the buttonevents from these. Xiaomi typically uses attribute reporting of an OnOff cluster, as if the switch (or sensor) were a light. For the double, treble, quadruple click, they report a manufacturer specific attribute, like the Hue dimmer switch.

Try2Fly commented 6 years ago

Yes I'm using them for Home assistant with the Deconz component too. The Door/sensor, motion detector (both Xiaomi) and ikea light work fine. But the button does not show up in the entities list of Home assistant. How is it defined in hass, as a sensor or binary sensor?

kristianwh commented 6 years ago

@ebaauw Thanks, I'm talking beyond my knowledge here :) Does this mean that the Xiaomi switches could also be supported to show up as "Switches" in e.g. Phoscon, like the now supported JUNG, GIRA, IKEA, Busch-Jaeger, dresden electronic and Philips switches do (with the ability to map them to lights)?

kristianwh commented 6 years ago

@Try2Fly: In the deconz component docs it is explained how to use them (See Remote Control Devices, and examples below that). They actually don't show up as sensors, but produce events to home assistant, with a value. These events can then be used in automations to trigger whatever you want.

ebaauw commented 6 years ago

Does this mean that the Xiaomi switches could also be supported to show up as "Switches" in e.g. Phoscon

Yes and no. In principle, Phoscon could expose anything available in the REST API. However, the switches currently supported in Phoscon all send commands to a group (even the Hue dimmer switch does so in parallel to the attribute reporting) and Phoscon "only" enables configuring and adding lights to that group. For the Xiaomi switches to be supported, Phoscon (or the REST API plugin) would need to create rules on the gateway instead, mapping the buttonevents (derived from the attribute notifications) to group commands. They're doing something like that for the IKEA remote's Next and Previous buttons.

Try2Fly commented 6 years ago

@kristianwh Thank you, did not understand this at first. But it says they should turn up with a battery_level, which they don't.

I've got this information from the gateway

{
    "config": {
        "on": true,
        "reachable": true
    },
    "ep": 1,
    "etag": "dda8e8e784937214a9cda28a028121a9",
    "manufacturername": "LUMI",
    "mode": 1,
    "modelid": "lumi.sensor_switch",
    "name": "lumi.sensor_switch 12",
    "state": {
        "buttonevent": 1002,
        "lastupdated": "2018-01-18T22:35:28"
    },
    "type": "ZHASwitch",
    "uniqueid": "00:15:8d:00:01:9c:a7:0a-01-0006"
}

And made this automation

- alias: xiaomi single
  # initial_state: 'on'
  trigger:
    - platform: event
      event_type: deconz_event
      event_data:
        id: lumi.sensor_switch_12
        event: 1002
  action:
    - service: input_boolean.toggle
      entity_id: input_boolean.watch_tv

unfortunately it does not work, I'm I missing something (again)?

ebaauw commented 6 years ago

deCONZ doesn't (yet) support config.battery for the Xiaomi sensors and switches. They do report it, but in a very nonstandard way, which seems to differ per device type.

kristianwh commented 6 years ago

@Try2Fly Hm. Try to double check the name of the sensor, it might have an added underscore or something to it. I enabled info level logging in home assistant configuration.yaml for this:

logger:
  default: info

Restart home assistant.

Then in the .homeassistant folder:

tail -f home-assistant.log | grep deconz

Press the button, and you should get the event in the log, with id and event. Use these in your automation. If they actually are the same, you might have some other problem. (Remember to turn off info level logging afterwards - the logs can get pretty large with this on).

kristianwh commented 6 years ago

@diderikfrom Sorry for the issue-thread hijacking. If anyone knows anything about the actual issue (not getting the long-press events from these buttons anymore), feel free to answer ;-)

ebaauw commented 6 years ago

I think I found the missing press buttonevent (1000), see https://github.com/ebaauw/deconz-rest-plugin/commit/0f511f23161921812ad7bec5c672cbfba9cc491c. However, I cannot test this as I only have the square-ish Aqara push button, which, unlike the round push button doesn't generate an attribute report on press. Would some-one be able to compile my fork and test this, before I create a PR?

Much to my pleasant surprise, the Aqara push button does support double, treble, and quadruple press.

The double press, treble press, and quadruple press are currently mapped to buttonevent values 2002, 3002, and 4002. This gives the impression that they're different buttons. I think we better map these to buttonevents in the 1xxx range, so it's clear to the REST API that it's the same button. I changed them to 1004, 1005, and 1006 and updated the API version to 1.0.6, so applications could anticipate the difference. @diderikfrom, @kristianwh, @Try2Fly, @rtenklooster, is this OK with you?

kristianwh commented 6 years ago

@ebaauw Sounds like a good way to solve it. I also thought the mapping 2002 3002 4002 was a bit off compared to the other button types supported. Does any of the other button types support multiple presses? If so, how are they handled? I'll try to compile your fork now, and test.

ebaauw commented 6 years ago

Does any of the other button types support multiple presses? If so, how are they handled? I'll try to compile your fork now, and test.

If they do, deCONZ doesn't handle these.

kristianwh commented 6 years ago

Ok. Tested your fork now, everything work as expected (single press + released-event and double-, triple- and quadruple-clicks). Thanks!

Try2Fly commented 6 years ago

@kristianwh Thanks a million! That did the trick, had to leave out the dot. id=lumisensor_switch_12

On topic: I think the press&released function for the button is crucial too!

diderikfrom commented 6 years ago

This is wonderful news! Thanks a lot!

ebaauw commented 6 years ago

Change is incorporated in v2.05.00.

@diderikfrom, I think we can close this issue?

Try2Fly commented 6 years ago

@ebaauw I'm testing it now. So far works great! Is there also a 1001 and 1003 value? I'm getting 1000 after a click, and 1002 when I'm holding it. In the xiaomi component of HASS there is also a possibility for long click press and long click release. So the first is a value it sends immediately after it detects it is pressed for long time. The long click release is a different value after button is released after long click. I now get 1000 when I release after a long press. Would this also be possible?

Try2Fly commented 6 years ago

I also noticed that even a short click send the 1002 event before it sends the 1000 event. Would be better if the 1002 event is not send at a normale click.

ebaauw commented 6 years ago

Hm, you should get 1000 on press and 1002 on release. Sounds like they’re reversed?

If so, my bad. I only have the Aqara variant, which only reports, I guess, press. In that case, it should be mapped to 1002 to support pre-websocket apps that need to poll deCONZ (and might miss the 1000, so they only look for the 1002). Note to self: need different buttonmaps for Aqara vs non-Aqara smart switch.

Unlike the Hue dimmer switch, the Xiaomi smart switch doesn’t report anything on hold (1001), and doesn’t distinguish between release after press (1002) and release after hold (1003). My guess is the Xiaomi gateway or the HASS component times the duration between press and release to derive hold and release after hold. I see no way to do that in the deCONZ buttonhandler.

diderikfrom commented 6 years ago

Testing right now. It seems to work very well. I also get 1002 on press and 1000 on release. This is not illogical to me. After a short press, we will see 1000 (1002 will only be registered for a fraction of a second). So in practice, it will be like this

1000=Short press/button released 1002=Held 1004=Double click 1005=Triple click 1006=Quadruple click

This works great for me, it makes no difference if 1000 and 1002 are swapped or not.

ebaauw commented 6 years ago

I’ll unswap the 1000 and 1002. As mentioned above, apps that poll deCONZ (e.g. for Hue bridge compatibility) will only look for a 1002.

If you use websocket notifications, you can measure the time between press (1000) and release (1002) (or the time since press without a release) to conclude a long press, but if you poll deCONZ, you might catch the 1000 when it occurs just before reading and wrongfully assume a long press.

Try2Fly commented 6 years ago

Ok thanks. I'll have to find a work around within hass then. Because now a can't differentiate between a single click and a hold click. The single click will always be seen as a hold click. I wanted to have single click for turning light on/off, and hold for dimming. I can't turn the light off now.

xydix commented 6 years ago

@Try2Fly Let my know if you find a way to use long press in HASS.

Try2Fly commented 6 years ago

@xydix have a look at this post: https://community.home-assistant.io/t/dim-light-with-appdaemon-deconz-and-xiaomi-wireless-switch/40952

It is made with appdaemon 2. Currently I'm using a slightly more advanced automation with appdaemon3, but in basic it is the same.

isabellaalstrom commented 6 years ago

@Try2Fly Could you please share your appdaemon app? Checked your repo, but saw it was empty. Am trying to write one for long press my self.

Try2Fly commented 6 years ago

@isabellaalstrom

This is for appdaemon3, I made it for multiple buttons. One changes the dimming of a light when holding, another for controlling the volume

Apps.yaml file:

tafel_button:
  module: buttonApp
  class: Click
  button: lumisensor_switch
  dim_type: light
  dim_object: light.table
salon_button:
  module: buttonApp
  class: Click
  button: lumisensor_switch_12
  dim_type: volume
  dim_object: media_player.receiver

ButtonApp.py

import appdaemon.plugins.hass.hassapi as hass
import time

class Click(hass.Hass):

  def initialize(self):
    self.listen_event(self.button_pressed, "deconz_event", id = self.args["button"] )
    self.button_hold = False
    self.object_dim = False
  def button_pressed(self, event_name, data, kwargs):
    self.click_type=data["event"]
    if self.click_type == 1002:
      if self.button_hold == False or self.get_state(self.args["dim_object"]) == "off":
        self.button_pressed = False
        self.toggle(self.args["dim_object"])
        self.log("toggle off")
      self.button_hold = False
    if self.click_type == 1000:
      self.button_pressed = True
      time.sleep(0.5)
      self.button_hold = True
      if self.button_pressed:
        self.long_press()
        self.log("long_press")
        self.object_dim = not self.object_dim
    if self.click_type == 1004:
       self.dubbel_click()
       self.log("dubbel_click")
    if self.click_type == 1005:
       self.triple_click()
       self.log("triple_click")

  def long_press(self):
    if self.args["dim_type"] == "light":
      if self.object_dim == False and self.button_hold == True and self.get_state(self.args["dim_object"]) == "on":
        while self.button_hold:
          self.new_level = self.get_state(self.args["dim_object"], attribute="brightness") + 25
          if self.new_level >= 255:
            self.new_level = 255
          self.turn_on(self.args["dim_object"], brightness = self.new_level)
        self.button_hold = False  

      elif self.object_dim == True and self.button_hold == True and self.get_state(self.args["dim_object"]) == "on":
        while self.button_hold:
          self.new_level = self.get_state(self.args["dim_object"], attribute="brightness") - 25
          if self.new_level <= 0:
            self.new_level = 6
          self.turn_on(self.args["dim_object"], brightness = self.new_level)
        self.button_hold = False

    if self.args["dim_type"] == "volume":
      if self.object_dim == False and self.button_hold == True and self.get_state(self.args["dim_object"]) == "on":
        while self.button_hold:
          self.new_level = (float(self.get_state(self.args["dim_object"], attribute="volume_level")) + 0.025)
          if self.new_level >= 0.75:
            self.new_level = 0.75
          self.call_service("media_player/volume_set", entity_id = self.args["dim_object"], volume_level = self.new_level)
          self.log(self.new_level)
        self.button_hold = False  

      elif self.object_dim == True and self.button_hold == True and self.get_state(self.args["dim_object"]) == "on":
        while self.button_hold:
          self.new_level = (float(self.get_state(self.args["dim_object"], attribute="volume_level")) - 0.025)
          if self.new_level <= 0.0:
            self.new_level = 0.0
          self.call_service("media_player/volume_set", entity_id = self.args["dim_object"], volume_level = self.new_level)
        self.button_hold = False

  def dubbel_click(self):
    if self.get_state("media_player.receiver") == "on" or self.get_state("group.livingroom_lights") == "on":
      self.turn_on("script.kill_switch")
      self.log("kill_switch")
    else:
      self.turn_on("group.livingroom_lights")
      self.log("livingroom_lights_on")
  def triple_click(self):
    if float(self.get_state("input_number.blinds")) == 0.0:
      self.turn_on("switch.blinds")
      self.log("blinds on")
    else:
      self.turn_off("switch.blinds")
      self.log("blinds off")

I used time.sleep() which should not be used and is not ideal. However I saw no other possibility to measure miliseconds. If you have another suggestion, please let me know

isabellaalstrom commented 6 years ago

Awesome, thanks.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

alex0411 commented 5 years ago

At the moment I only get (1000, 1002, 1004, 1005, 1006) from the REST API. Model is Xiaomi WXKG01LM. With zigbee2mqtt I could use this clicks: single, double, triple, quadruple, many, long, long_release click. Right now click and long respond both with 1000.

deCONZ 2.05.49