Z2M - Xiaomi Cube Controller

Hi,
i use a automation, based on leow example

- alias: Cube1                                                                                                                                                           
  description: ''                                                                                                                                                        
  trigger:                                                                                                                                                               
  - platform: state                                                                                                                                                      
    entity_id: sensor.cube1_action                                                                                                                                       
  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:                                                                                                                                                                
  - service_template: >-
      {% if trigger.to_state.attributes.action in ("rotate_right", "rotate_left", "slide", "tap") %}
      script.cube_side{{ trigger.to_state.attributes.side}}_{{ trigger.to_state.attributes.action }} 
      {% elif trigger.to_state.attributes.action in ("shake", "fall") %} 
      script.cube_{{ trigger.to_state.attributes.action }}                                                                                       
      {% else %} 
      script.none 
      {% endif %}

But you can import it to the blueprint too. (untested, but i think it must work so)

[...]
trigger:
- platform: event
  event_type: state_changed
  event_data:
    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.event.data.new_state.state }}'
    sub_event: '{{ trigger.event.data.new_state.attributes.side }}'
[...]

For some reason this just comes up with an error in the log:

UndefinedError: 'dict object' has no attribute 'from_state'
1 Like

I’m getting the same error when using @mbacks 's condition in the template.

Which code block did you use? the blueprint was untested. when i think about it, with platform: event, there is no trigger.from_state object, so my blueprint codeblock cant work. i need for that more than 5 minutes to rewrite the blueprint. i try to have a look on it tomorrow…

2 Likes

@jekawmfux @mr.g
please have a try

blueprint:
  name: Aqara Magic Cube (Z2M)
  description: Control anything using Aqara Magic Cube (Z2M) - V1.1
  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'
1 Like

That seems to have worked! Thank you so much.

Tried this out and it works great with the Hue bulbs I have. I also have a few “Wiz” bulbs which I believe is a child company of Philips. There was no native support for these bulbs, but there is a HACS addon that works. The issue is that there is no device for the Wiz bulbs, just the entity. (If I’m wrong on this please let me know.)

Is it possible to still use this blueprint effectively? I tried an automation using ‘Call service’ but there was a pretty long delay compared to a bulb I could use as a device in the blueprint.

Thanks

sorry really basic question…how do i import this into HA? when i add blueprint it seems to take from the top.

Hi, thats correct. it is @luckypoppy 's choice to update his blueprint. i doesnt want to create my own, because it is doing the same with only one enhancement (filter to trigger only one event)

to install my update, you can create a new blueprint or edit the existing one. But a new is better at this moment.

the easiest way is per ssh or the file editor plugin

config/blueprints/automation/
├── luckypoppy
│   └── z2m-xiaomi-cube-controller.yaml
└── <your own folder>
    └── z2m-xiaomi-cube-controller-update.yaml

Please set a new name for the blueprint

blueprint:
  name: My own Aqara Magic Cube (Z2M) Blueprint

then reload automation.

1 Like

Thanks Max! I am toggling :slight_smile:

Hello @luckypoppy and @mbacks !
Thanks for this blueprint!
I’ve a question:how to use the rotate function to increase or decrease the position of the rolling shutter?
Selecting the needed one, and switching to yaml I have as action:

device_id: f0f3b3f0e61a046ceaa66b56dabfc1bf
domain: cover
entity_id: cover.rolling_shutter_switch_2
type: set_position

Thanks for support!
Simon

Hi,
i doesnt know how to set a cover position. please have a look in developer tools → Services how to set the position with a service. then look at the created yaml.

with templating you can calculate the new position. please have a look in the documentation how in detail. the following is only guessed.

device_id: f0f3b3f0e61a046ceaa66b56dabfc1bf
domain: cover
entity_id: cover.rolling_shutter_switch_2
type: set_position
position:>
  {% set oldpos =  state_attr('cover.rolling_shutter_switch_2', 'position') |float %}
  {% set step =  10 |float %}
  {% set newpos = oldpost + step %}
  {% if newpos > 100 %}100{% else %}{{ x }}{% endif %}

the cube sends the rotate angle in his state. you can access it with

{{ trigger.to_state.attributes.angle }}

Hello @mbacks , it’s partially working. I don’t konw why but the rolling shutter gets totally closed and not by step. Even correcting the typo {% set newpos = oldpos + step %}. I’ll try with template but have no idea how to debug.

Nice work! Sorry for my late response, I just tested the modified blueprint myself and it works a lot better indeed. I will update the blueprint so it can be imported more easily by others.

Hello @mbacks ,
I’ve tried your example, but as soon I place this code into the YAML version of the automation, it ends to not be editable at all from GUI and the result doesn’t work. Here what used:

      remote: sensor.0x00158d00042efedf_action
      rotate_cw_face_0:
      - device_id: f0f3b3f0e61a046ceaa66b56dabfc1bf
        domain: cover
        entity_id: cover.rolling_shutter_switch_2
        type: set_position
        position:>
          {% set oldpos =  state_attr('cover.rolling_shutter_switch_2', 'current_position') |int %}
          {% set step =  10 |int %}
          {% set newpos = oldpos + step %}
          {% if newpos >= 100 %}100{% else %}{{ newpos }}{% endif %}

If I try this code in the template, setting some outputs, I get the right values:

          {% set oldpos =  state_attr('cover.rolling_shutter_switch_2', 'current_position') |int %}
          {{ oldpos }}
          {% set step =  10 |int %}
          {% set newpos = oldpos + step %}
          {{ newpos }}
          {% if newpos >= 100 %}100{% else %}{{ newpos }}{% endif %}

The answer is:

80
90
90

where 80 is the roller shutter position and 90 the desired position.
BUT, if I try this code in the Developer Tools → Services → YAML mode:

service: cover.set_cover_position
target:
  device_id: f0f3b3f0e61a046ceaa66b56dabfc1bf
data:
  position: >
    {% set oldpos =  state_attr('cover.rolling_shutter_switch_2', 'current_position') |int %}
    {% set step =  10 |int %}
    {% set newpos = oldpos + step %}
    {% if newpos >= 100 %}100{% else %}{{ newpos }}{% endif %}

The anser from the popup is:

Failed to call service cover/set_cover_position. expected int for dictionary value @ data['position']

Could someone help me implementing it in the right way into the automation?

Thanks a lot!!!

Simon

Edit: a second try after googling and reading your post again :wink:
sorry

I’ve tried your example, but as soon I place this code into the YAML version of the automation, it ends to not be editable at all from GUI and the result doesn’t work. Here what used:

when you add a template, you cant edit it with the gui anymore

If I try this code in the template, setting some outputs, I get the right values:

so it looks like, that you can access the values of the current position correctly and the jinja template should work.

i try this with my lights, but it looks like not working correct too. i update my post later

service: light.turn_on
target:
  device_id: a393d49f951e2d52a8fc98b4c017963f
data:
  brightness_pct: >
    {% set oldpos =  state_attr(' light.flur_schrank_bulb', 'brightness') |int %}
    {% set step =  10 |int %}
    {% set newpos = oldpos + step %}
    {% if newpos >= 100 %}100{% else %}{{ newpos | int }}{% endif %}
1 Like

Hi @xefil

this is working for my light:

service: light.turn_on
target:
  device_id: a393d49f951e2d52a8fc98b4c017963f
data:
  brightness: >-
    {% set oldpos =  state_attr('light.schlaf_led', 'brightness') | int %}
    {% set step =  10 | int %}
    {% set newpos = oldpos - step %}
    {% if newpos >= 254 %}254{% else %}{{ newpos | int }}{% endif %}

so with this in mind: Cover - Home Assistant

this hier sould work:

service: cover.set_cover_position
target:
  entity_id: cover.rolling_shutter_switch_2
data:
  position: >-
    {% set oldpos = state_attr('cover.rolling_shutter_switch_2', 'current_position') | int %}
    {% set step =  10 | int %}
    {% set newpos = oldpos + step %}
    {% if newpos >= 100 %}100{% else %}{{ newpos | int }}{% endif %}

looks like, that the value in the dictionary is a string

Failed to call service cover/set_cover_position. expected int for dictionary value @ data['position']

when i try this {{ 100 | int }} under development tools → template, i get as return type a number

{% if newpos >= 100 %}{{ 100 | int }}{% else %}{{ newpos | int }}{% endif %}

Check my automation to control my cover (sun screen):

trigger:
  - platform: mqtt
    topic: zigbee2mqttt/Aqara Cube
condition:
  - condition: template
    value_template: '{{ trigger.payload_json.action in [''rotate_right'', ''rotate_left''] }}'
action:
  - service: cover.set_cover_position
    data:
      entity_id: cover.ekran_sloneczny
      position: >
        {%if is_state('input_select.cube_options', 'Ekran Słoneczny') %} {{ [ 0,
        [state_attr('cover.ekran_sloneczny', 'position') | int -
        trigger.payload_json.action_angle | int / 3, 100] | min ] | max }} {%
        endif %}

you can just remove my input_select and make it running like this:

position: >
    {{ [ 0, [state_attr('cover.sun_screen', 'position') | int +
        trigger.payload_json.action_angle | int / 3,
        100] | min ] | max }}

Got some much appreciated help recently :wink: now cover works like a charm. In connection with TTS which says cover position after action even more fancy.

hey @luckypoppy how do I know which face is which on the cube itself?

For this I use the event viewer in the developer tools. When you follow the topic ‘state_changed’ you will see changes including side numbers (when flipping the cube). Side 0 is when the logo faces up.

1 Like