Event information deconz with Xiaomi Cube

Thank you, will look at it

Hi I created a appdaemon app that creates an entity in Home-Assistant. But instead of the action to update the state of the sensor.aqara_cube you can off course put anything you like. Below is what I have now. Until now I have not yet took the time to think of actions I want on the different gestures there are with the cube. It might help you with setting it up.

import appdaemon.plugins.hass.hassapi as hass
import datetime
from datetime import datetime

class CubeControl(hass.Hass):

    def initialize(self):
        if 'event' in self.args:
            self.listen_event(self.handle_event, self.args['event'])

    def handle_event(self, event_name, data, kwargs):
        event_data = data["event"]
        event_id = data["id"]
        event_received = datetime.now()
        if data['id'] == self.args['id']:
            self.log(data['event'])
            if data['event'] in [1000, 2000, 3000, 4000, 5000, 6000]:
                self.set_state("sensor.aqara_cube", state = 'slide', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif data['event'] in [1001, 2002, 3003, 4004, 5005, 6006]:
                self.set_state("sensor.aqara_cube", state = 'double tap', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif data['event'] in [1006, 2005, 3004, 4003, 5002, 6001]:
                self.set_state("sensor.aqara_cube", state = 'flip180', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif data['event'] in [1002, 1003, 1004, 1005, 2001, 2003, 2004, 2006, 3001, 3002, 3005, 3006, 4001, 4002, 4005, 4006, 5001, 5003, 5004, 5006, 6002, 6003, 6004, 6005]:
                self.set_state("sensor.aqara_cube", state = 'flip90', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif data['event'] == 7007:
                self.set_state("sensor.aqara_cube", state = 'shake', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif data['event'] == 7008:
                self.set_state("sensor.aqara_cube", state = 'fall', attributes = {"event_data": event_data, "event_received": str(event_received)})                              
            elif data['event'] == 7000:
                self.set_state("sensor.aqara_cube", state = 'wake', attributes = {"event_data": event_data, "event_received": str(event_received)})
            elif len(str(event_data)) != 4 or str(event_data)[1:3] != '00':
                if data['event'] > 0:
                    self.set_state("sensor.aqara_cube", state = 'rotate cw', attributes = {"event_data": event_data, "event_received": str(event_received)})
                elif data['event'] < 0:                                 
                    self.set_state("sensor.aqara_cube", state = 'rotate ccw', attributes = {"event_data": event_data, "event_received": str(event_received)})'''
6 Likes

I did the same thing but with templates instead of AppDaemon. Anyway there is something to fix in deCONZ event handling behaviour inside of corresponding component, so users will not have to worry about it.

What needs to be fixed with the deconz event handling behaviour?
Everything works perfectly fine for me with deconz.

Aqara cube. Rotation and sides switches in events are with the same id. So I can’t just use trigger.event.data.event > 0 in templates for positive rotation and < 0 for negative. Because if I switch the cube from 6 to 1 side, it gives me the 6001 code, which will be recognized as positive rotation, because the event will be with the same id in event.data.
deCONZ sends events with different sensor IDs. So I can differ one from another one if I use deCONZ rest API directly.

Aha I understand. I don’t use the aqara cube, I thought you were talking about the event handling in general, sorry for the confusion.

Is there nothing else in the event data only the 4 digit key?

event.data is something like

{
  "id": "mi_cube",
  "event": "3003"
}

if I rotate the cube to the right for 30.03 degrees (it is degrees, I guess).
The same data for double tapping cube on the side 3.

Ah okay. So maybe you could talk to the developer of the deconz component to include the sensor id in the data of the event.

1 Like

That is why I am using appdaemon for it instead of templates, the last two lines of the code are for the rotation.

Thank you for sharing! After a day of reading and testing it finally works. But now I am not sure whats the best way to use these events. Right now I can only toggle lights, but it would be great to adjust the brightness by rotating.

        elif len(str(event_data)) != 4 or str(event_data)[1:3] != '00':
            if data['event'] > 0:
                self.set_state("sensor.aqara_cube", state = 'rotate cw', attributes = {"event_data": event_data, "event_received": str(event_received)})
                self.set_state("light.desk", state= 'on', attributes = {"brightness": 252})
            elif data['event'] < 0:
                self.set_state("sensor.aqara_cube", state = 'rotate ccw', attributes = {"event_data": event_data, "event_received": str(event_received)})
                self.turn_on("light.desk")

Using the turn_on action works flawlessly, so I thought I could also just use set_state to adjust the brightness (in a static way at first), but the changes will not “stick”. I can see the brightness attribute change in dev tools, but only for some seconds. The actual lamp is not affected.

I am (fairly) new to Home Assistant and its my first time using appdaemon, so any help would be appreciated. Should I use these events from your app for regular automations and if so, whats the right way to handle rotation events?

Update:
Rotate to adjust brightness now works, so here is the snippet:

            elif len(str(event_data)) != 4 or str(event_data)[1:3] != '00':
            if data['event'] > 0:

                if self.entities.light.desk.state != 'off':
                    self.cur_brightness = self.entities.light.desk.attributes.brightness
                else:
                    self.cur_brightness = 0
                self.new_brightness = (data['event'] // 300) + self.cur_brightness
                if self.new_brightness >= 255:
                    self.new_brightness = 255
                elif self.new_brightness <= 0:
                    self.new_brightness = 0
                if self.new_brightness == 0:
                    self.turn_off('light.desk')
                else:
                    self.turn_on('light.desk', brightness = self.new_brightness)

            elif data['event'] < 0:

                if self.entities.light.desk.state != 'off':
                    self.cur_brightness = self.entities.light.desk.attributes.brightness
                else:
                    self.cur_brightness = 0
                self.new_brightness = (data['event'] // 300) + self.cur_brightness
                if self.new_brightness >= 255:
                    self.new_brightness = 255
                elif self.new_brightness <= 0:
                    self.new_brightness = 0
                if self.new_brightness == 0:
                    self.turn_off('light.desk')
                else:
                    self.turn_on('light.desk', brightness = self.new_brightness)

I know that the code is repetitive, so any help for decoupling into a seperate function is appreciated - these are my first steps with python. But since it is working, I leave it here.

Can you please show your whole code?

Does this really work?
self.cur_brightness = self.entities.light.desk.attributes.brightness?
I don’t think that this works, you haven’t declared self.entities anywhere. Are there no errors in the logs?

Thats right. I did not declare entities, at least not knowingly, and currently there are no errors. Everything works as aspected and when i logged cur_brightness it printed the same value as seen in dev tools.

import appdaemon.plugins.hass.hassapi as hass
import datetime
from datetime import datetime

class CubeControl(hass.Hass):

def initialize(self):
    if 'event' in self.args:
        self.listen_event(self.handle_event, self.args['event'])
        self.log('CubeControl running')

def handle_event(self, event_name, data, kwargs):
    event_data = data['event']
    event_id = data['id']
    event_received = datetime.now()
    if data['id'] == self.args['id']:
        self.log(data['event'])
        if data['event'] in [1000, 2000, 3000, 4000, 5000, 6000]:
            self.set_state('sensor.aqara_cube', state = 'slide', attributes = {'event_data': event_data, 'event_received': str(event_received)})
            # self.toggle('light.bedroombulb')
        elif data['event'] in [1001, 2002, 3003, 4004, 5005, 6006]:
            self.set_state('sensor.aqara_cube', state = 'double tap', attributes = {'event_data': event_data, 'event_received': str(event_received)})
        elif data['event'] in [1006, 2005, 3004, 4003, 5002, 6001]:
            self.set_state('sensor.aqara_cube', state = 'flip180', attributes = {'event_data': event_data, 'event_received': str(event_received)})
            self.toggle('light.bedroombulb')
        elif data['event'] in [1002, 1003, 1004, 1005, 2001, 2003, 2004, 2006, 3001, 3002, 3005, 3006, 4001, 4002, 4005, 4006, 5001, 5003, 5004, 5006, 6002, 6003, 6004, 6005]:
            self.set_state('sensor.aqara_cube', state = 'flip90', attributes = {'event_data': event_data, 'event_received': str(event_received)})
            self.toggle('light.bedroom')
        elif data['event'] == 7007:
            self.set_state('sensor.aqara_cube', state = 'shake', attributes = {'event_data': event_data, 'event_received': str(event_received)})
            self.turn_off('group.all_lights')
        elif data['event'] == 7008:
            self.set_state('sensor.aqara_cube', state = 'fall', attributes = {'event_data': event_data, 'event_received': str(event_received)})
        elif data['event'] == 7000:
            self.set_state('sensor.aqara_cube', state = 'wake', attributes = {'event_data': event_data, 'event_received': str(event_received)})
        elif len(str(event_data)) != 4 or str(event_data)[1:3] != '00':
            if data['event'] > 0:
                if self.entities.light.bedroombulb.state != 'off':
                    self.cur_brightness = self.entities.light.bedroombulb.attributes.brightness
                else:
                    self.cur_brightness = 0
                self.new_brightness = (data['event'] // 300) + self.cur_brightness
                if self.new_brightness >= 255:
                    self.new_brightness = 255
                elif self.new_brightness <= 0:
                    self.new_brightness = 0
                if self.new_brightness == 0:
                    self.turn_off('light.bedroombulb')
                else:
                    self.turn_on('light.bedroombulb', brightness = self.new_brightness)
            elif data['event'] < 0:
                if self.entities.light.bedroombulb.state != 'off':
                    self.cur_brightness = self.entities.light.bedroombulb.attributes.brightness
                else:
                    self.cur_brightness = 0
                self.new_brightness = (data['event'] // 300) + self.cur_brightness
                if self.new_brightness >= 255:
                    self.new_brightness = 255
                elif self.new_brightness <= 0:
                    self.new_brightness = 0
                if self.new_brightness == 0:
                    self.turn_off('light.bedroombulb')
                else:
                    self.turn_on('light.bedroombulb', brightness = self.new_brightness)

This is pretty much the same code as above, with the rotation block (found in another thread) pasted in between.

Try this for the brightness part. I hope it is what you want.

    elif len(str(event_data)) != 4 or str(event_data)[1:3] != '00':
        cur_brightness = self.get_state('light.bedroombulb',
                                        attribute='brightness') or 0
        if data['event'] > 0:
            new_brightness = (data['event'] // 300) + cur_brightness

            if new_brightness > 255:
                new_brightness = 255
            self.turn_on('light.bedroombulb', brightness=new_brightness)
        elif data['event'] < 0:
            new_brightness = (data['event'] // 300) - cur_brightness
            if new_brightness <= 0:
                self.turn_off('light.bedroombulb')
            else:
                self.turn_on('light.bedroombulb', brightness=new_brightness)

There is a small error in the second line, it should be
self.cur_brightness = self.get_state(‘light.bedroombulb’, attribute=‘brightness’) or 0

Without self there will be following error:
AttributeError: ‘CubeControl’ object has no attribute ‘cur_brightness’

Otherwise it works as expected and is much cleaner. Thanks for your help!

is a basic part from AD since AD 3
but its still rarely used.

Thanks for the info.
Interesting, didn’t know about that.Looks like I overread this in the documentation.

Then there must be a typo somewhere, but you don’t need the self., if the attribute isn’t used anywhere outside of this method.

its well hidden. i cant even find it when i search for it :wink:
i wanted to tell you where it is written, but sorry, cant find it :wink: (but i know its there)

I am 1 of the oldest AD users and I am in the dev team, and still i get suprised once in a while :wink:

Hey I try to use your solution.
I installed appDeamon for hass.io, I added your script, I check it is initialized.

But, how can I have any custom sensor like your aqua_cube? So I added my cube to deconz, I named it to magic_cube, but I have only sensor.magic_cube_battery_level and sensor.magic_cube_battery_level2 sensors.
So how can I have sensor.magic_cube ? Could you help in this?

There won’t be any sensor for the cube itself. You will only receive events once you do something with the cube. This appdaemon app captures these events an then does something based on the event code.