Z2M - Xiaomi Cube Controller

Tags: #<Tag:0x00007fc4158ad7f8> #<Tag:0x00007fc4158ad6b8> #<Tag:0x00007fc4158ad5a0> #<Tag:0x00007fc4158ad460>

Blueprint to support the Xiaomi Cube Controller

Thanks for inspiration

@bobchadwick for this
#wmn79 for this

Supported actions:

Actions with respect to which of the six faces are up:

  • Slide
  • Knock
  • Flip90
  • Flip180
  • Rotate clockwise
  • Rotate counter-clockwise

Actions without respect to which of the six faces are up:

  • Drop
  • Shake

Version: 1.2 - Fixed double actions by @mbacks

blueprint:
  name: Aqara Magic Cube (Z2M)
  description: Control anything using Aqara Magic Cube (Z2M) - V1.2
  domain: automation
  input:
    remote:
      name: Remote
      description: Magic Cube to use
      selector:
        entity:
          integration: mqtt
          domain: sensor
    slide_face_0:
      name: Slide the cube with face 0 up
      default: []
      selector:
        action: {}
    doubletap_face_0:
      name: Double tap the cube with face 0 up
      default: []
      selector:
        action: {}
    flipped90_face_0:
      name: Flip the cube 90 degrees to face 0
      default: []
      selector:
        action: {}
    flipped180_face_0:
      name: Flip the cube 180 degrees to face 0
      default: []
      selector:
        action: {}
    rotate_cw_face_0:
      name: Rotate cube clockwise with face 0 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_0:
      name: Rotate cube counter clockwise with face 0 up
      default: []
      selector:
        action: {}
    slide_face_1:
      name: Slide the cube with face 1 up
      default: []
      selector:
        action: {}
    doubletap_face_1:
      name: Double tap the cube with face 1 up
      default: []
      selector:
        action: {}
    flipped90_face_1:
      name: Flip the cube 90 degrees to face 1
      default: []
      selector:
        action: {}
    flipped180_face_1:
      name: Flip the cube 180 degrees to face 1
      default: []
      selector:
        action: {}
    rotate_cw_face_1:
      name: Rotate cube clockwise with face 1 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_1:
      name: Rotate cube counter clockwise with face 1 up
      default: []
      selector:
        action: {}
    slide_face_2:
      name: Slide the cube with face 2 up
      default: []
      selector:
        action: {}
    doubletap_face_2:
      name: Double tap the cube with face 2 up
      default: []
      selector:
        action: {}
    flipped90_face_2:
      name: Flip the cube 90 degrees to face 2
      default: []
      selector:
        action: {}
    flipped180_face_2:
      name: Flip the cube 180 degrees to face 2
      default: []
      selector:
        action: {}
    rotate_cw_face_2:
      name: Rotate cube clockwise with face 2 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_2:
      name: Rotate cube counter clockwise with face 2 up
      default: []
      selector:
        action: {}
    slide_face_3:
      name: Slide the cube with face 3 up
      default: []
      selector:
        action: {}
    doubletap_face_3:
      name: Double tap the cube with face 3 up
      default: []
      selector:
        action: {}
    flipped90_face_3:
      name: Flip the cube 90 degrees to face 3
      default: []
      selector:
        action: {}
    flipped180_face_3:
      name: Flip the cube 180 degrees to face 3
      default: []
      selector:
        action: {}
    rotate_cw_face_3:
      name: Rotate cube clockwise with face 3 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_3:
      name: Rotate cube counter clockwise with face 3 up
      default: []
      selector:
        action: {}
    slide_face_4:
      name: Slide the cube with face 4 up
      default: []
      selector:
        action: {}
    doubletap_face_4:
      name: Double tap the cube with face 4 up
      default: []
      selector:
        action: {}
    flipped90_face_4:
      name: Flip the cube 90 degrees to face 4
      default: []
      selector:
        action: {}
    flipped180_face_4:
      name: Flip the cube 180 degrees to face 4
      default: []
      selector:
        action: {}
    rotate_cw_face_4:
      name: Rotate cube clockwise with face 4 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_4:
      name: Rotate cube counter clockwise with face 4 up
      default: []
      selector:
        action: {}
    slide_face_5:
      name: Slide the cube with face 5 up
      default: []
      selector:
        action: {}
    doubletap_face_5:
      name: Double tap the cube with face 5 up
      default: []
      selector:
        action: {}
    flipped90_face_5:
      name: Flip the cube 90 degrees to face 5
      default: []
      selector:
        action: {}
    flipped180_face_5:
      name: Flip the cube 180 degrees to face 5
      default: []
      selector:
        action: {}
    rotate_cw_face_5:
      name: Rotate cube clockwise with face 5 up
      default: []
      selector:
        action: {}
    rotate_ccw_face_5:
      name: Rotate cube counter clockwise with face 5 up
      default: []
      selector:
        action: {}
    shake:
      name: Shake the cube
      default: []
      selector:
        action: {}
    fall:
      name: Drop the cube
      default: []
      selector:
        action: {}
  source_url: https://community.home-assistant.io/t/z2m-xiaomi-cube-controller/263006
mode: queued
max: 5
max_exceeded: silent
trigger:
- platform: state
  entity_id: !input 'remote'
condition:
  - condition: and
    conditions:
      - condition: template
        value_template: >-
          {{ trigger.from_state.attributes.action != trigger.to_state.attributes.action }} 
      - condition: template
        value_template: >-
          {{ trigger.to_state.attributes.action in ('rotate_right', 'rotate_left', 'flip90', 'flip180', 'slide', 'tap', 'shake', 'fall') }}
action:
- variables:
    event: '{{ trigger.to_state.attributes.action }}'
    sub_event: '{{ trigger.to_state.attributes.side }}'
- service: system_log.write
  data:
    level: info
    message: 'Blueprint Script: {{ event }} {{ sub_event }}'
- choose:
  - conditions: '{{ event == "slide" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'slide_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'slide_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'slide_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'slide_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'slide_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'slide_face_5'
  - conditions: '{{ event == "tap" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'doubletap_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'doubletap_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'doubletap_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'doubletap_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'doubletap_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'doubletap_face_5'
  - conditions: '{{ event == "flip90" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'flipped90_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'flipped90_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'flipped90_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'flipped90_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'flipped90_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'flipped90_face_5'
  - conditions: '{{ event == "flip180" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'flipped180_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'flipped180_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'flipped180_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'flipped180_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'flipped180_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'flipped180_face_5'
  - conditions: '{{ event == "rotate_right" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'rotate_cw_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'rotate_cw_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'rotate_cw_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'rotate_cw_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'rotate_cw_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'rotate_cw_face_5'
  - conditions: '{{ event == "rotate_left" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'
        sequence: !input 'rotate_ccw_face_0'
      - conditions: '{{ sub_event == 1 }}'
        sequence: !input 'rotate_ccw_face_1'
      - conditions: '{{ sub_event == 2 }}'
        sequence: !input 'rotate_ccw_face_2'
      - conditions: '{{ sub_event == 3 }}'
        sequence: !input 'rotate_ccw_face_3'
      - conditions: '{{ sub_event == 4 }}'
        sequence: !input 'rotate_ccw_face_4'
      - conditions: '{{ sub_event == 5 }}'
        sequence: !input 'rotate_ccw_face_5'
  - conditions: '{{ event == "shake" }}'
    sequence: !input 'shake'
  - conditions: '{{ event == "fall" }}'
    sequence: !input 'fall'
5 Likes

If it interests you, it’s possible to use nested choose statements to simplify the action.

Here's your automation's `action` converted to use nested `choose`.
action:
- variables:
    event: '{{ trigger.event.data.old_state.state }}'
    sub_event: '{{ trigger.event.data.old_state.attributes.side }}'
- choose:
  # ---------------------------- SLIDE ----------------------------
  - conditions: '{{ event == "slide" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'    
        sequence: !input 'slide_face_0'
      - conditions: '{{ sub_event == 1 }}'    
        sequence: !input 'slide_face_1'
      - conditions: '{{ sub_event == 2 }}'    
        sequence: !input 'slide_face_2'
      - conditions: '{{ sub_event == 3 }}'    
        sequence: !input 'slide_face_3'
      - conditions: '{{ sub_event == 4 }}'    
        sequence: !input 'slide_face_4'
      - conditions: '{{ sub_event == 5 }}'    
        sequence: !input 'slide_face_5'

  # ---------------------------- KNOCK ----------------------------
  - conditions: '{{ event == "tap" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'    
        sequence: !input 'knocked_face_0'
      - conditions: '{{ sub_event == 1 }}'    
        sequence: !input 'knocked_face_1'
      - conditions: '{{ sub_event == 2 }}'    
        sequence: !input 'knocked_face_2'
      - conditions: '{{ sub_event == 3 }}'    
        sequence: !input 'knocked_face_3'
      - conditions: '{{ sub_event == 4 }}'    
        sequence: !input 'knocked_face_4'
      - conditions: '{{ sub_event == 5 }}'    
        sequence: !input 'knocked_face_5'

  # ---------------------------- FLIP ----------------------------
  - conditions: '{{ event == "flip90" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'    
        sequence: !input 'flipped_face_0'
      - conditions: '{{ sub_event == 1 }}'    
        sequence: !input 'flipped_face_1'
      - conditions: '{{ sub_event == 2 }}'    
        sequence: !input 'flipped_face_2'
      - conditions: '{{ sub_event == 3 }}'    
        sequence: !input 'flipped_face_3'
      - conditions: '{{ sub_event == 4 }}'    
        sequence: !input 'flipped_face_4'
      - conditions: '{{ sub_event == 5 }}'    
        sequence: !input 'flipped_face_5'

  # ---------------------------- ROTATE CW --------------------------
  - conditions: '{{ event == "rotate_right" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'    
        sequence: !input 'rotate_cw_face_0'
      - conditions: '{{ sub_event == 1 }}'    
        sequence: !input 'rotate_cw_face_1'
      - conditions: '{{ sub_event == 2 }}'    
        sequence: !input 'rotate_cw_face_2'
      - conditions: '{{ sub_event == 3 }}'    
        sequence: !input 'rotate_cw_face_3'
      - conditions: '{{ sub_event == 4 }}'    
        sequence: !input 'rotate_cw_face_4'
      - conditions: '{{ sub_event == 5 }}'    
        sequence: !input 'rotate_cw_face_5'

  # ---------------------------- ROTATE CCW ------------------------
  - conditions: '{{ event == "rotate_left" }}'
    sequence:
    - choose:
      - conditions: '{{ sub_event == 0 }}'    
        sequence: !input 'rotate_ccw_face_0'
      - conditions: '{{ sub_event == 1 }}'    
        sequence: !input 'rotate_ccw_face_1'
      - conditions: '{{ sub_event == 2 }}'    
        sequence: !input 'rotate_ccw_face_2'
      - conditions: '{{ sub_event == 3 }}'    
        sequence: !input 'rotate_ccw_face_3'
      - conditions: '{{ sub_event == 4 }}'    
        sequence: !input 'rotate_ccw_face_4'
      - conditions: '{{ sub_event == 5 }}'    
        sequence: !input 'rotate_ccw_face_5'

  # ---------------------------- SHAKE ----------------------------
  - conditions: '{{ event == "shake" }}'
    sequence: !input 'shake'

  # ---------------------------- DROP ----------------------------
  - conditions: '{{ event == "fall" }}'
    sequence: !input 'fall'

2 Likes

Thanks for this improvement and if you agree I would like to update the blueprint.

This is the first blueprint I made and I am happy with additions like this so that I can learn from it.

1 Like

Feel free to use it but be advised that you should test it thoroughly. I don’t have an Aqara Cube or zigbee2mqtt so I can’t test it.

This blueprint is one of several that addresses the challenge of creating actions for ‘remote-control’ devices. The Aqara Cube can have as many as 32 actions (5 events x 6 sub-events = 30 + 2 events). Most of the work is defining the actions.

For the curious, here’s how it could be done without a blueprint.

Create a script for each action and name each script using a consistent format. For example:

script.cube1_flip_3

That means it’s the script for the sensor called cube1 when its event isflip and its sub-event is 3.

Make as many scripts as you need for each cube, which means up to 32 scripts for a single Aqara Cube (it’s the same amount of work as filling out a blueprint).

This one short automation handles all actions for two Aqara Cubes:

alias: example 1
trigger:
- platform: event
  event_type: state_changed
  event_data:
    entity_id: sensor.cube1
- platform: event
  event_type: state_changed
  event_data:
    entity_id: sensor.cube2
action:
- service: script.turn_on
  data:
    entity_id: "script.{{trigger.to_state.object_id}}_{{ trigger.event.data.old_state.state }}_{{ trigger.event.data.old_state.attributes.side }}"

Anyway, that would be one of the ‘old-school’ ways of doing it. :slight_smile:

Good job!

I just wondering if it is possible to have a global condition (time period) to disable multiple toggles. Since I have multiple coordinators. Thx.

I want to look at that, but it is difficult for me to test this customization. Do you have an example of another automation in which you have been able to circumvent this problem? Do I understand correctly that you are using 2 coordinators in the same zigbee network and publishing in the same mqtt topic?

Thanks. Here is one of my automation tos disable repeated actions (> 2s).

I do find that it becomes more common on the Xiaomi single button. It always publishes two topics at the same time. Here is the discussion for ‘zigbee2mqtt’. https://github.com/Koenkk/zigbee2mqtt/issues/649https://github.com/Koenkk/zigbee2mqtt/issues/1309

alias: fall cube for corner light
description: ''
trigger:
  - platform: device
    domain: mqtt
    device_id: ***
    type: action
    subtype: fall
    discovery_id: **** action_fall
condition:
  - condition: template
    value_template: >
      {{ (as_timestamp(now()) - as_timestamp(states.switch.sw3.last_changed)) >
      2 }}
action:
  - type: toggle
    device_id: ***
    entity_id: switch.sw3
    domain: switch
mode: single

For future reference, this Template Condition:

condition:
  - condition: template
    value_template: >
      {{ (as_timestamp(now()) - as_timestamp(states.switch.sw3.last_changed)) >
      2 }}

can be reduced to this:

condition: "{{ now().timestamp() - states.switch.sw3.last_changed.timestamp() > 2 }}"

It uses shorthand notation and takes advantage of the built-in timestamp() method in now() and an entity’s State Object.

2 Likes

Hi There!

Thanks for great blueprint! Sadly I’ve faced an issue - laggy turning actions.
I’m using these to dim/brighten my mesh lights with zigbe2mqtt and Conbee II stick. With ZHA I had it with queued option seted to 5 and it was working fast and accurate. Now it’s roulette if the lights will dimm or not. Sometimes no reaction at all and sometimes it goes with all delayed actions. In general this should dim/brigten different lights based on side action. Any tips?
Thanks in advance!

hi thanks for sending this feedback. During testing, I noticed that it does not always go smoothly. With the adjustment you mentioned (queued option set to 5) it seems to be going better.

I will update the blueprint, you could test whether it is better for you?

Hi!
Thanks a lot for quick action. I’ve tested your new version but still facing delays and no action at all.

My default setup is:
side:0 turning cw/ccw is adjusting brightness of lamp 1,
side:3 cw/ccw is adjusting brighness of lamp 2
and accordingly:
side:0 tap toggles on/off for lamp 1 and side:3 toggles on/off for lamp 2.

Issues I’m facing:

  • toggle light is not working somehow, lights are just flashing once and turning off again (Have to use light.turn_on),
  • actions are not taken at all or with huge delay (all of these),
  • random actions are working (increasing brigtness is working but reducing not).

HA log shows that action is taken and automatization is released but nothing happen (I’ve noticed that for one automatization released there is quite some entries - I will provide screenshot) and Zigbee2MQTT log is also showing that actions are registered. I have Hassio running on Raspberry Pi 4 with 4GB RAM and maybe that is the reason why is so laggy? From the other hand when I was using cracked Xiaomi Gaterway 3 with ZHA actions were taken almost immediately but I really want fancy option to dim different lights according to cube side :wink:

I will test in a moment if Cube connected to Xiaomi Gateway with ZHA is working faster and if ordinary automatization just for one action a time is also giving better results.

sorry to hear that it is not working properly yet. I cannot reproduce the problems you describe and it seems to work well here with a similar automation: Turn to dim (side-0) and Shake or fall to turn off.

In your first post your problems sounded worse than I was experiencing and also immediately wondered if there might be some other cause such as overloaded hardware. Hopefully you have the option to rule this out somehow and maybe someone else can check this out

I am using a synology DS420 + and HA, MQTT, Z2M run in its own docker containers. And as coordinator a CC2538 Flashed with: https://github.com/Koenkk/Z-Stack-firmware

I see the same issues with light.toggle, it flashes and returns to the original state. It appears to be specifically z-wave devices for my setup, zigbee/wifi lights don’t experience it. I can issue the same light.toggle service call from the HA console and it works as expected, seems like something subtle is going on here. Just wanted to add my 2c.

The problem is that this automation sends the action 2 times. With scripts happen the same it executes them two times., if you have a toggle script it doesn’t work.

I also find a double action when setting toggle - thanks for this, its enabled me to get going and great fun, I wouldn’t have known how to use it otherwise, appreciated!

Did anyone find a solution to avoid the double action? It seems like the zigbee2mqtt message comes in two times.

This blueprint requires an entity in the first GUI field.
Z2M does not expose an action entity for me, with several tries to re-pair.
How to add the cube to this blueprint then?

Hi,

for filtering double actions, you can use the following condition

condition: and
conditions:
  - condition: template
    value_template: >-
      {{ trigger.from_state.attributes.action !=
      trigger.to_state.attributes.action }}
  - condition: template
    value_template: >-
      {{ trigger.to_state.attributes.action in ("rotate_right", "rotate_left",
      "flip90", "flip180", "slide", "tap") }}

best regards,
Max

Thanks Max… where would this condition go?