Skip to content

Instantly share code, notes, and snippets.

@rvaidya
Last active March 9, 2024 01:38
Show Gist options
  • Save rvaidya/3fe8a5f3b1d113ffa8625cf5ff2c88a3 to your computer and use it in GitHub Desktop.
Save rvaidya/3fe8a5f3b1d113ffa8625cf5ff2c88a3 to your computer and use it in GitHub Desktop.
ZHA - Aqara Magic Cube
blueprint:
name: Aqara Magic Cube
description: Control anything using Aqara Magic Cube.
domain: automation
input:
remote:
name: Magic Cube
description: Select the Aqara Magic Cube device
selector:
device:
integration: zha
manufacturer: LUMI
active_face:
name: Active Face
description: Select the number input for tracking active face
selector:
entity:
domain: input_number
flip_90:
name: Flip 90 degrees
description: 'Actions to run when cube flips 90 degrees.
This cancels all specific 90 degrees functions.
e.g From side 1 to side 2 will be the same as from side 6 to side 2'
default: false
selector:
boolean: {}
cube_flip_90:
name: Flip cube 90 degrees
description: Action to run when cube flips 90 degrees. This only works if 'Flip
90 degrees' is toggled
default: []
selector:
action: {}
flip_180:
name: Flip 180 degrees
description: 'Actions to run when cube flips 180 degrees.
This cancels all specific 180 degrees functions
e.g From side 1 to side 4 will be the same as from side 5 to side 2'
default: false
selector:
boolean: {}
cube_flip_180:
name: Flip cube 180 degrees
description: Action to run when cube flips 180 degrees. This only works if 'Flip
180 degrees' is toggled
default: []
selector:
action: {}
flip_any:
name: Flip cube to any side
description: 'Actions to run when cube flips to any side.
This cares about the end side, but cancels all specific flip functions.
e.g From side 1 to side 2 will be the same as from side 6 to side 2
but different than side 1 to side 5'
default: false
selector:
boolean: {}
cube_flip_1:
name: Flip cube to side 1
description: Action to run when cube flips to side 1. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
cube_flip_2:
name: Flip cube to side 2
description: Action to run when cube flips to side 2. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
cube_flip_3:
name: Flip cube to side 3
description: Action to run when cube flips to side 3. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
cube_flip_4:
name: Flip cube to side 4
description: Action to run when cube flips to side 4. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
cube_flip_5:
name: Flip cube to side 5
description: Action to run when cube flips to side 5. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
cube_flip_6:
name: Flip cube to side 6
description: Action to run when cube flips to side 6. This only works if 'Flip
any' is toggled
default: []
selector:
action: {}
slide_any_side:
name: Slide any side
description: 'Actions to run when cube slides on any side.
This cancels all specific ''slide'' functions
e.g Slide on side 1 will be the same as slide on side 2'
default: false
selector:
boolean: {}
cube_slide_any:
name: Slide cube on any side
description: Action to run when cube slides on any side. This only works if
'Slide any side' is toggled
default: []
selector:
action: {}
knock_any_side:
name: Knock on any side
description: 'Actions to run when knocking cube regardless of the side.
This cancels all specific ''knock'' functions
e.g Knock on side 1 will be the same as knocking side 2'
default: false
selector:
boolean: {}
cube_knock_any:
name: Knock cube on any side
description: Action to run when knocking cube on any side. This only works if
'Knock on any side' is toggled
default: []
selector:
action: {}
one_to_two:
name: From side 1 to side 2
description: Action to run when cube goes from side 1 to side 2
default: []
selector:
action: {}
one_to_three:
name: From side 1 to side 3
description: Action to run when cube goes from side 1 to side 3
default: []
selector:
action: {}
one_to_four:
name: From side 1 to side 4
description: Action to run when cube goes from side 1 to side 4
default: []
selector:
action: {}
one_to_five:
name: From side 1 to side 5
description: Action to run when cube goes from side 1 to side 5
default: []
selector:
action: {}
one_to_six:
name: From side 1 to side 6
description: Action to run when cube goes from side 1 to side 6
default: []
selector:
action: {}
two_to_one:
name: From side 2 to side 1
description: Action to run when cube goes from side 2 to side 1
default: []
selector:
action: {}
two_to_three:
name: From side 2 to side 3
description: Action to run when cube goes from side 2 to side 3
default: []
selector:
action: {}
two_to_four:
name: From side 2 to side 4
description: Action to run when cube goes from side 2 to side 4
default: []
selector:
action: {}
two_to_five:
name: From side 2 to side 5
description: Action to run when cube goes from side 2 to side 5
default: []
selector:
action: {}
two_to_six:
name: From side 2 to side 6
description: Action to run when cube goes from side 2 to side 6
default: []
selector:
action: {}
three_to_one:
name: From side 3 to side 1
description: Action to run when cube goes from side 3 to side 1
default: []
selector:
action: {}
three_to_two:
name: From side 3 to side 2
description: Action to run when cube goes from side 3 to side 2
default: []
selector:
action: {}
three_to_four:
name: From side 3 to side 4
description: Action to run when cube goes from side 3 to side 4
default: []
selector:
action: {}
three_to_five:
name: From side 3 to side 5
description: Action to run when cube goes from side 3 to side 5
default: []
selector:
action: {}
three_to_six:
name: From side 3 to side 6
description: Action to run when cube goes from side 3 to side 6
default: []
selector:
action: {}
four_to_one:
name: From side 4 to side 1
description: Action to run when cube goes from side 4 to side 1
default: []
selector:
action: {}
four_to_two:
name: From side 4 to side 2
description: Action to run when cube goes from side 4 to side 2
default: []
selector:
action: {}
four_to_three:
name: From side 4 to side 3
description: Action to run when cube goes from side 4 to side 3
default: []
selector:
action: {}
four_to_five:
name: From side 4 to side 5
description: Action to run when cube goes from side 4 to side 5
default: []
selector:
action: {}
four_to_six:
name: From side 4 to side 6
description: Action to run when cube goes from side 4 to side 6
default: []
selector:
action: {}
five_to_one:
name: From side 5 to side 1
description: Action to run when cube goes from side 5 to side 1
default: []
selector:
action: {}
five_to_two:
name: From side 5 to side 2
description: Action to run when cube goes from side 5 to side 2
default: []
selector:
action: {}
five_to_three:
name: From side 5 to side 3
description: Action to run when cube goes from side 5 to side 3
default: []
selector:
action: {}
five_to_four:
name: From side 5 to side 4
description: Action to run when cube goes from side 5 to side 4
default: []
selector:
action: {}
five_to_six:
name: From side 5 to side 6
description: Action to run when cube goes from side 5 to side 6
default: []
selector:
action: {}
six_to_one:
name: From side 6 to side 1
description: Action to run when cube goes from side 6 to side 1
default: []
selector:
action: {}
six_to_two:
name: From side 6 to side 2
description: Action to run when cube goes from side 6 to side 2
default: []
selector:
action: {}
six_to_three:
name: From side 6 to side 3
description: Action to run when cube goes from side 6 to side 3
default: []
selector:
action: {}
six_to_four:
name: From side 6 to side 4
description: Action to run when cube goes from side 6 to side 4
default: []
selector:
action: {}
six_to_five:
name: From side 6 to side 5
description: Action to run when cube goes from side 6 to side 5
default: []
selector:
action: {}
one_to_one:
name: Knock - Side 1
description: Action to run when knocking on side 1
default: []
selector:
action: {}
two_to_two:
name: Knock - Side 2
description: Action to run when knocking on side 2
default: []
selector:
action: {}
three_to_three:
name: Knock - Side 3
description: Action to run when knocking on side 3
default: []
selector:
action: {}
four_to_four:
name: Knock - Side 4
description: Action to run when knocking on side 4
default: []
selector:
action: {}
five_to_five:
name: Knock - Side 5
description: Action to run when knocking on side 5
default: []
selector:
action: {}
six_to_six:
name: Knock - Side 6
description: Action to run when knocking on side 6
default: []
selector:
action: {}
slide_on_one:
name: Slide - Side 1 up
description: Action to run when slides with Side 1 up
default: []
selector:
action: {}
slide_on_two:
name: Slide - Side 2 up
description: Action to run when slides with Side 2 up
default: []
selector:
action: {}
slide_on_three:
name: Slide - Side 3 up
description: Action to run when slides with Side 3 up
default: []
selector:
action: {}
slide_on_four:
name: Slide - Side 4 up
description: Action to run when slides with Side 4 up
default: []
selector:
action: {}
slide_on_five:
name: Slide - Side 5 up
description: Action to run when slides with Side 5 up
default: []
selector:
action: {}
slide_on_six:
name: Slide - Side 6 up
description: Action to run when slides with Side 6 up
default: []
selector:
action: {}
cube_wake:
name: Wake up the cube
description: Action to run when cube wakes up
default: []
selector:
action: {}
cube_drop:
name: Cube drops
description: Action to run when cube drops
default: []
selector:
action: {}
cube_shake:
name: Shake cube
description: Action to run when you shake the cube
default: []
selector:
action: {}
rotate_any_side:
name: Rotate any side
description: 'Actions to run when rotating cube regardless of the side.
This cancels all side-specific ''rotate'' functions
e.g Rotate on side 1 will be the same as rotating side 2'
default: false
selector:
boolean: {}
rotate_right:
name: Rotate right
description: Action to run when cube rotates right on any side. This only works if
'Rotate any side' is toggled
default: []
selector:
action: {}
rotate_left:
name: Rotate left
description: Action to run when cube rotates left on any side. This only works if
'Rotate any side' is toggled
default: []
selector:
action: {}
rotate_left_side_1:
name: Rotate cube left with side 1 up
default: []
selector:
action: {}
rotate_left_side_2:
name: Rotate cube left with side 2 up
default: []
selector:
action: {}
rotate_left_side_3:
name: Rotate cube left with side 3 up
default: []
selector:
action: {}
rotate_left_side_4:
name: Rotate cube left with side 4 up
default: []
selector:
action: {}
rotate_left_side_5:
name: Rotate cube left with side 5 up
default: []
selector:
action: {}
rotate_left_side_6:
name: Rotate cube left with side 6 up
default: []
selector:
action: {}
rotate_right_side_1:
name: Rotate cube right with side 1 up
default: []
selector:
action: {}
rotate_right_side_2:
name: Rotate cube right with side 2 up
default: []
selector:
action: {}
rotate_right_side_3:
name: Rotate cube right with side 3 up
default: []
selector:
action: {}
rotate_right_side_4:
name: Rotate cube right with side 4 up
default: []
selector:
action: {}
rotate_right_side_5:
name: Rotate cube right with side 5 up
default: []
selector:
action: {}
rotate_right_side_6:
name: Rotate cube right with side 6 up
default: []
selector:
action: {}
source_url: https://community.home-assistant.io/t/aqara-magic-cube-zha-51-actions/270829/1
mode: restart
max_exceeded: silent
trigger:
- platform: event
event_type: zha_event
event_data:
device_id: !input 'remote'
action:
- variables:
command: '{{ trigger.event.data.command }}'
value: '{{ trigger.event.data.args.value | default(0) }}'
flip_degrees: '{{ trigger.event.data.args.flip_degrees | default(0) }}'
flip_any: !input 'flip_any'
flip_90: !input 'flip_90'
flip_180: !input 'flip_180'
slide_any_side: !input 'slide_any_side'
knock_any_side: !input 'knock_any_side'
rotate_any_side: !input 'rotate_any_side'
flip90: 64
flip180: 128
slide: 256
knock: 512
shake: 0
drop: 3
active_face_entity: !input 'active_face'
active_face: "{{ states(active_face_entity) | int }}"
activated_face: "\n{% if command == \"slide\" or command == \"knock\" %}\n\n \
\ {% if trigger.event.data.args.activated_face == 1 %} 1\n\n {% elif trigger.event.data.args.activated_face\
\ == 2 %} 5\n\n {% elif trigger.event.data.args.activated_face == 3 %} 6\n\n\
\ {% elif trigger.event.data.args.activated_face == 4 %} 4\n\n {% elif trigger.event.data.args.activated_face\
\ == 5 %} 2\n\n {% elif trigger.event.data.args.activated_face == 6 %} 3\n\n\
\ {% endif %}\n\n{% elif command == 'flip' %}\n\n {{ trigger.event.data.args.activated_face\
\ | int }}\n\n{% endif %}\n"
from_face: "\n{% if command == \"flip\" and flip_degrees == 90 %}\n\n {{ ((value\
\ - flip90 - (trigger.event.data.args.activated_face - 1)) / 8) + 1 | int }}\n\
\n{% endif %}\n"
relative_degrees: "\n{% if command == \"rotate_right\" or command == \"rotate_left\" %}\n\n \
\ {{ trigger.event.data.args.relative_degrees | float }}\n\
\n{% endif %}\n"
- choose:
- conditions:
- '{{ command == ''checkin'' }}'
sequence: !input 'cube_wake'
- conditions:
- '{{ command == ''flip'' }}'
sequence:
- service: input_number.set_value
target:
entity_id: '{{ active_face_entity }}'
data:
value: '{{ activated_face | int }}'
- choose:
- conditions: '{{ flip_any }}'
sequence:
- choose:
- conditions: '{{ activated_face == 1 }}'
sequence: !input 'cube_flip_1'
- conditions: '{{ activated_face == 2 }}'
sequence: !input 'cube_flip_2'
- conditions: '{{ activated_face == 3 }}'
sequence: !input 'cube_flip_3'
- conditions: '{{ activated_face == 4 }}'
sequence: !input 'cube_flip_4'
- conditions: '{{ activated_face == 5 }}'
sequence: !input 'cube_flip_5'
- conditions: '{{ activated_face == 6 }}'
sequence: !input 'cube_flip_6'
- conditions:
- '{{ flip_degrees == 90 and flip_90 }}'
sequence: !input 'cube_flip_90'
- conditions:
- '{{ flip_degrees == 180 and flip_180 }}'
sequence: !input 'cube_flip_180'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 1 }}'
sequence:
- choose:
- conditions: '{{ from_face == 2 }}'
sequence: !input 'two_to_one'
- conditions: '{{ from_face == 3 }}'
sequence: !input 'three_to_one'
- conditions: '{{ from_face == 5 }}'
sequence: !input 'five_to_one'
- conditions: '{{ from_face == 6 }}'
sequence: !input 'six_to_one'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 2 }}'
sequence:
- choose:
- conditions: '{{ from_face == 1 }}'
sequence: !input 'one_to_two'
- conditions: '{{ from_face == 3 }}'
sequence: !input 'three_to_two'
- conditions: '{{ from_face == 4 }}'
sequence: !input 'four_to_two'
- conditions: '{{ from_face == 6 }}'
sequence: !input 'six_to_two'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 3 }}'
sequence:
- choose:
- conditions: '{{ from_face == 1 }}'
sequence: !input 'one_to_three'
- conditions: '{{ from_face == 2 }}'
sequence: !input 'two_to_three'
- conditions: '{{ from_face == 4 }}'
sequence: !input 'four_to_three'
- conditions: '{{ from_face == 5 }}'
sequence: !input 'five_to_three'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 4 }}'
sequence:
- choose:
- conditions: '{{ from_face == 2 }}'
sequence: !input 'two_to_four'
- conditions: '{{ from_face == 3 }}'
sequence: !input 'three_to_four'
- conditions: '{{ from_face == 5 }}'
sequence: !input 'five_to_four'
- conditions: '{{ from_face == 6 }}'
sequence: !input 'six_to_four'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 5 }}'
sequence:
- choose:
- conditions: '{{ from_face == 1 }}'
sequence: !input 'one_to_five'
- conditions: '{{ from_face == 3 }}'
sequence: !input 'three_to_five'
- conditions: '{{ from_face == 4 }}'
sequence: !input 'four_to_five'
- conditions: '{{ from_face == 6 }}'
sequence: !input 'six_to_five'
- conditions:
- '{{ flip_degrees == 90 and activated_face == 6 }}'
sequence:
- choose:
- conditions: '{{ from_face == 1 }}'
sequence: !input 'one_to_six'
- conditions: '{{ from_face == 2 }}'
sequence: !input 'two_to_six'
- conditions: '{{ from_face == 4 }}'
sequence: !input 'four_to_six'
- conditions: '{{ from_face == 5 }}'
sequence: !input 'five_to_six'
- conditions:
- '{{ value == flip180 + activated_face - 1 }}'
sequence:
- choose:
- conditions: '{{ activated_face == 1 }}'
sequence: !input 'four_to_one'
- conditions: '{{ activated_face == 2 }}'
sequence: !input 'five_to_two'
- conditions: '{{ activated_face == 3 }}'
sequence: !input 'six_to_three'
- conditions: '{{ activated_face == 4 }}'
sequence: !input 'one_to_four'
- conditions: '{{ activated_face == 5 }}'
sequence: !input 'two_to_five'
- conditions: '{{ activated_face == 6 }}'
sequence: !input 'three_to_six'
- conditions:
- '{{ command == ''knock'' }}'
sequence:
- service: input_number.set_value
target:
entity_id: '{{ active_face_entity }}'
data:
value: '{{ activated_face | int }}'
- choose:
- conditions: '{{ knock_any_side }}'
sequence: !input 'cube_knock_any'
- conditions: '{{ activated_face == 1 }}'
sequence: !input 'one_to_one'
- conditions: '{{ activated_face == 2 }}'
sequence: !input 'two_to_two'
- conditions: '{{ activated_face == 3 }}'
sequence: !input 'three_to_three'
- conditions: '{{ activated_face == 4 }}'
sequence: !input 'four_to_four'
- conditions: '{{ activated_face == 5 }}'
sequence: !input 'five_to_five'
- conditions: '{{ activated_face == 6 }}'
sequence: !input 'six_to_six'
- conditions:
- '{{ command == ''slide'' }}'
sequence:
- service: input_number.set_value
target:
entity_id: '{{ active_face_entity }}'
data:
value: '{{ activated_face | int }}'
- choose:
- conditions: '{{ slide_any_side }}'
sequence: !input 'cube_slide_any'
- conditions:
- '{{ value == slide + activated_face - 1 }}'
sequence:
- choose:
- conditions: '{{ activated_face == 1 }}'
sequence: !input 'slide_on_one'
- conditions: '{{ activated_face == 2 }}'
sequence: !input 'slide_on_two'
- conditions: '{{ activated_face == 3 }}'
sequence: !input 'slide_on_three'
- conditions: '{{ activated_face == 4 }}'
sequence: !input 'slide_on_four'
- conditions: '{{ activated_face == 5 }}'
sequence: !input 'slide_on_five'
- conditions: '{{ activated_face == 6 }}'
sequence: !input 'slide_on_six'
- conditions:
- '{{ command == ''rotate_left'' and rotate_any_side }}'
sequence: !input 'rotate_left'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 1 }}'
sequence: !input 'rotate_left_side_1'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 2 }}'
sequence: !input 'rotate_left_side_2'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 3 }}'
sequence: !input 'rotate_left_side_3'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 4 }}'
sequence: !input 'rotate_left_side_4'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 5 }}'
sequence: !input 'rotate_left_side_5'
- conditions:
- '{{ command == ''rotate_left'' and active_face == 6 }}'
sequence: !input 'rotate_left_side_6'
- conditions:
- '{{ command == ''rotate_right'' and rotate_any_side }}'
sequence: !input 'rotate_right'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 1 }}'
sequence: !input 'rotate_right_side_1'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 2 }}'
sequence: !input 'rotate_right_side_2'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 3 }}'
sequence: !input 'rotate_right_side_3'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 4 }}'
sequence: !input 'rotate_right_side_4'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 5 }}'
sequence: !input 'rotate_right_side_5'
- conditions:
- '{{ command == ''rotate_right'' and active_face == 6 }}'
sequence: !input 'rotate_right_side_6'
- conditions:
- '{{ command == ''shake'' or value == shake }}'
sequence: !input 'cube_shake'
- conditions:
- '{{ command == ''drop'' or value == drop }}'
sequence: !input 'cube_drop'
@tekstein
Copy link

Great blueprint! Improved a good blueprint and made it better. I'm kind of a noob, so I have just one problem. I don't understand how to activate the faces of the magic cube. Could you please advise?

@rvaidya
Copy link
Author

rvaidya commented Jul 28, 2022

Great blueprint! Improved a good blueprint and made it better. I'm kind of a noob, so I have just one problem. I don't understand how to activate the faces of the magic cube. Could you please advise?

I've got some notes on the blueprint exchange page: https://community.home-assistant.io/t/zha-aqara-magic-cube-69-actions/348204

Note that since this blueprint uses an input number helper to track which side is active, it will only register side changes from valid side actions (again, flip/knock/slide). If you lift the cube, rotate it freely, and then place it down on a different side, this is not registered as a valid action and the tracking variable won’t be updated.

What I do is I don't use the slide action for anything specific, instead I use the slide to register the current face so that I know the rotation is going to apply to that face.

... is what I would say if I were still using my cube - but it's just been way too flaky for me to really rely on. I mostly stick with Aqara buttons now.

@clinta
Copy link

clinta commented Jan 2, 2023

I have an issue where if you change the cube face and immediately rotate it, the face does not get updated in the helper. I've changed the mode to queued which fixes this issue.

@kanthamohan
Copy link

Hi @rvaidya any reason why the active_face tracker is changing some of the face to a different one? e.g. 2 mapped to 5 and 3 mapped to 6 ?

@jbouv
Copy link

jbouv commented Mar 9, 2024

I found the same -- the active_face_entity was not accurately tracking the correct active face. The zha_event is correctly reporting it, however, so it seems to be an issue with keeing the active_face_entity in sync. I also noticed that the cube's zha_event all show the active face arg in each raised event, so is there any way to modify the blueprint to just pull the active_face from the arg and not use the the active_face_entity at all?

@jbouv
Copy link

jbouv commented Mar 9, 2024

I just noticed this block of code that seems to be the reason for the issues that I am seeing (i.e. wrong activate_face on knock event):

activated_face: >
{% if command == "slide" or command == "knock" %}
{% if trigger.event.data.args.activated_face == 1 %} 1
{% elif trigger.event.data.args.activated_face == 2 %} 5
{% elif trigger.event.data.args.activated_face == 3 %} 6
{% elif trigger.event.data.args.activated_face == 4 %} 4
{% elif trigger.event.data.args.activated_face == 5 %} 2
{% elif trigger.event.data.args.activated_face == 6 %} 3
{% endif %}

It seems on a knock or slide, this is overriding the active_face to be a different face than is indicated in the event arg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment