
Xiaomi Magic Cube in Homeassistant einbinden
Vor kurzem habe ich mein Homeassistant von meinem Raspberry Pi 3 auf einen Intel NUC migriert und zeitgleich die Möglichkeit genutzt meine Konfiguration aus einem Python-Virtualenvironment zu befreien und in eine Hass.io-VM zu übertragen. Im selben Atemzug habe ich mir einen Conbee II zugelegt, um in Zukunft – ohne proprietäre Hubs – Zigbee Geräte steuern zu können und nicht länger nur auf deren Kompatibilität zu meiner vorhandenen Hue-Bridge angewiesen zu sein. Ein Conbee II eröffnet einem zugleich das Tor zur Welt in das Smart Home Ökosystem von Xiaomi/Aqara, welches eine Vielzahl günstiger Zigbee Sensoren bereitstellt.
Ein solches Device aus dem Hause Xiaomi ist der Magic Cube – ein kleines responsives Eingabegerät in Form eines Würfels, der eine Vielzahl von Bewegungen erkennt: Schütteln, Fallen, Rotieren, 90° Flip, 180° Flip, Schieben auf einer Seite, etc. Das kleine Device erschien mir perfekt als Musiksteuerungseinheit. Auf jede Seite eine Playlist, schütteln für Stop, rechts drehen für einen Track weiter und linksdrehen für einen Track zurück.
Der Würfel lässt sich ohne Probleme über das DeConz Add-On für Hass.io pairen und kann danach umgehend verwendet werden – jedoch leider nicht so, wie ich es mir vorgestellt hatte. In Hass tauchen nach dem Kopplungsvorgang lediglich zwei Batteriesensoren auf, die dem Cube zugeordnet sind. Es stellte sich heraus, dass das Gerät “auf dem Eventbus feuert” und dies auch nur mittels uninterpretierter 4-Stelligen Zahlencodes, die einer eindeutigen Eingabeaktion zugeordnet sind. Um diese Codes auszulesen, muss in Homeassistant der Menüpunkt Developer Options > Events angeklickt werden und in dem unteren Eingabefeld deconz_event
abonniert werden. Dreht, bewegt oder schüttelt man jetzt den Cube, erscheint dort ein zugehöriger Eventstream.
Um den Würfel sinnvoll nutzen zu können, sind solche uninterpretierten Zahlencodes nicht förderlich. Gleichzeitig gibt es schlichtweg zu viele dieser Codes um einen ellenlangen Template-Sensor zu erstellen. Templating auf dem Event Bus ist zusätzlich umständlich, nur bedingt sinnvoll und macht das Vorhaben nicht einfacher.
AppDaemon to the rescue!
Mit Appdaemon hatte ich zuvor keinerlei Annäherungsversuche gewagt. Für den Magic Cube schien es jedoch als das ideale Werkzeug die eingehenden Bus-Daten zu interpretieren. Appdaemon hat jedoch eine ziemlich steile Lernkurve, die mich als nicht sonderlich fähigen Hobby-Programmierer schnell abgeschreckt hat. Nach ein wenig Recherche fand ich dieses Skript, dass mir als eine gute Basis für mein Vorhaben erschien, jedoch nicht gänzlich meinen Vorstellungen entsprach. Unangepasst hat es lediglich zwei Dinge getan:
- Einen Sensor angelegt, der die aktuell nach oben gerichtete Würfelseite ausgibt
- Die 4-stelligen Zahlencodes interpretiert und diese auf einem frei-wählbaren Event erneut auf den Event-Bus geschickt
Nachteilg waren darüber hinaus, dass das Skript zwar die Mglichkeit bot direkt mehrere Cubes einzubinden, diese jedoch anschließend alle die gleichen Aktionen in Hass triggern würden. Auch wurde lediglich eine Rotation registriert, nicht jedoch in welche Richtung diese erfolgte. Ich habe das Skript daraufhin angepasst und zusätzlich zu den zuvor geschriebenen Funktionen folgendes erreicht:
- Die interpretierten Zahlencodes werden nicht nur zurück auf den Eventbus geschickt, sondern es wird ein zusätzlicher Sensor erzeugt, der einen sprechenden Namen der Aktion ausgibt, z.B.
flip90
,shake_air
,moove
,flip180
, etc. Sprechende Namen sind eine perfekte Ausgangsbasis für Automationen mit Templates. - Die Rotationsrichtung wird unterschieden in
rotate_left
undrotate_right
.
Die von mir weiterentwicklete Appdaemon App im Detail:
Mein Ziel war es mehrere Magic Cubes unabhängig voneinander auslesen zu können. Eingegeben werden muss lediglich die DECONZ_ID, die Homeassistant als Entity zugewiesen bekommen hat. Andere Variablen des Skripts lauten:
SENSOR_ID:
Der Name des Sensors, der erzeugt wird um die aktuell nach oben gerichtete Seite darzustellenACTION_ID:
Der Sensor der einen sprechenden Namen für die letzte Aktion des Würfels ausgibtEVENT_ID:
Das Thema mit dem die letzte Aktion auf dem Eventbus ausgegeben wird und dort abonniert werden kann
# magiccube_kueche.py # Based on https://github.com/iago1460/fast-deploy/blob/master/config_template/appdaemon/apps/cube.py import appdaemon.plugins.hass.hassapi as hass from enum import Enum DECONZ_IDS = ('zauberwurfel_kueche') SENSOR_ID = 'sensor.magiccube_kueche_faceup' ACTION_ID = 'sensor.magiccube_kueche_action' EVENT_ID = 'magiccube.kueche_action' class Action(str, Enum): FLIP90 = 'flip90' FLIP180 = 'flip180' MOVE = 'move' # = slide TAP_TWICE = 'tap_twice' SHAKE_AIR = 'shake_air' # SWING = 'swing' ?? ALERT = 'alert' # = wake? FREE_FALL = 'free_fall' ROTATE_LEFT = "rotate_left" ROTATE_RIGHT = "rotate_right" class CubeControl(hass.Hass): """ Recreating xiaomi aqara binary sensor platform for cube https://www.home-assistant.io/components/binary_sensor.xiaomi_aqara/ """ def initialize(self): self.listen_event(self.handle_event, "deconz_event") def handle_event(self, event_name, data, kwargs): if data['id'] in DECONZ_IDS: if data['event'] in [1000, 2000, 3000, 4000, 5000, 6000]: to_side = data['event'] // 1000 self.set_state(SENSOR_ID, state=to_side) self.set_state(ACTION_ID, state=Action.MOVE) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.MOVE) elif data['event'] in [1001, 2002, 3003, 4004, 5005, 6006]: to_side = data['event'] % 1000 self.set_state(SENSOR_ID, state=to_side) self.set_state(ACTION_ID, state=Action.TAP_TWICE) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.TAP_TWICE) elif data['event'] in [1006, 2005, 3004, 4003, 5002, 6001]: from_side = data['event'] % 1000 to_side = data['event'] // 1000 self.set_state(SENSOR_ID, state=to_side, attributes={'from_side': from_side}) self.set_state(ACTION_ID, state=Action.FLIP180) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.FLIP180) 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]: from_side = data['event'] % 1000 to_side = data['event'] // 1000 self.set_state(SENSOR_ID, state=to_side, attributes={'from_side': from_side}) self.set_state(ACTION_ID, state=Action.FLIP90) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.FLIP90) elif data['event'] == 7007: self.set_state(ACTION_ID, state=Action.SHAKE_AIR) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.SHAKE_AIR) elif data['event'] == 7008: self.set_state(ACTION_ID, state=Action.FREE_FALL) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.FREE_FALL) elif data['event'] == 7000: self.set_state(ACTION_ID, state=Action.ALERT) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.ALERT) else: degrees = data['event'] / 100 if degrees < 0: self.set_state(ACTION_ID, state=Action.ROTATE_LEFT, attributes={'direction': degrees}) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.ROTATE_LEFT, action_value=degrees) else: self.set_state(ACTION_ID, state=Action.ROTATE_RIGHT, attributes={'direction': degrees}) self.fire_event(EVENT_ID, entity_id=SENSOR_ID, action_type=Action.ROTATE_RIGHT, action_value=degrees)
Dieser Code wird im Ordner /config/appdaemon/apps im Config-Pfad von Homeassistant unter dem beispielhaften Namen magiccube_kueche.py
abgelegt. Zusätzlich muss nun noch die Datei apps.yaml
im Pfad /config/appdaemon angepasst und folgender Eintrag hinzugefügt werden:
# apps.yaml cube_control_kueche: module: magiccube_kueche class: CubeControl
Damit ist Homeassistant für den Magic Cube gewappnet und kann nach einem Neustart des Appdaemon Plugins die Daten des Würfels empfangen.
Ich habe hierfür mit Lovelace eine picture-elements-card angelegt, die neben der nach oben gerichteten Seite auch noch den sprechenden Namen der letzten Aktion und den Batteriestand des Devices anzeigt.
Aufgemerkt: Bis auf die Batterieangabe werden die anderen beiden Sensoren erst angezeigt, wenn der Würfel einmal eine Aktion ausgeführt hat.

Der zugehörige Code sieht folgendermaßen aus:
type: picture-elements image: /local/magiccube.png elements: - entity: sensor.magiccube_kueche_faceup style: font-size: 600% left: 72% top: 32% type: state-label - entity: sensor.magiccube_kueche_action style: font-size: 180% left: 72% top: 58% type: state-label - entity: sensor.switch_13_battery_level style: font-size: 180% left: 72% top: 78% type: state-label
Das Hintergrundbild mit dem Würfel ist tranparent als PNG-Datei vorhanden und eignet sich daher auch hervaorragend für das Zusammenspiel mit wechselnden Themes in der Lovelace UI. Einfach rechtklick drauf machen, beim Herunterladen umbenennen in magiccube.png
und im Ordner /config/www/ ablegen:

Magic Cube zur Steuerung von Spotify nutzen
Nachdem die Lovelace Card eingebunden und der Magic Cube korrekt erkannt wird, kommt nun der spaßige Part. Jede Seite des Würfels wird mit einer Playlist belegt. Dreht man den Würfel auf diese Seite, startet diese auf dem media_player
der Wahl. Dreht man den Würfel nach rechts, wird der nächste Track abgespielt. Dreht man ihn nach links, spielt der vorherige Track. Wird der Würfel geschüttelt, schaltet sich der Media_Player aus.
Im folgenden Beispiel erfolgt das mit Spotify als Media_Player. Als Abspielgerät kommt mein Bose Soundtouch 10 in der Küche zum Einsatz. Damit die dargestellten Automationen nicht zu lang werden, habe ich im Folgenden nur die Würfelseiten 1 und 6 dargestellt und exemplarisch 3 Aktionen (Linksdreh, Rechtsdreh und Schütteln) dargestellt:
- alias: Magic Cube Kueche Musiccontrol initial_state: True trigger: - platform: state entity_id: sensor.magiccube_kueche_faceup action: - service: media_player.turn_on entity_id: media_player.spotify - service: media_player.select_source data: entity_id: media_player.spotify source: Kueche - service: media_player.play_media data_template: media_content_type: playlist entity_id: media_player.spotify media_content_id: > {% if is_state("sensor.magiccube_kueche_faceup", "1") %} spotify:playlist:37i9dQZF1DX1xPh0VVE326 {% elif is_state("sensor.magiccube_kueche_faceup", "6") %} spotify:playlist:37i9dQZF1DZ06evO02TC95 {% endif %} - alias: Magic Cube Kueche Actioncontrol initial_state: True trigger: - platform: state entity_id: sensor.magiccube_kueche_action action: - service_template: > {% if is_state("sensor.magiccube_kueche_action", "shake_air") %} media_player.turn_off {% elif is_state("sensor.magiccube_kueche_action", "rotate_right") %} media_player.media_next_track {% elif is_state("sensor.magiccube_kueche_action", "rotate_left") %} media_player.media_previous_track {% endif %} data: entity_id: media_player.soundtouch_kuche
Weitere Magic Cubes einbinden
Möchte man zustzlich weitere Cubes einbinden, so erstellt man ganz einfach für jeden neuen Würfel eine neue App im Ordner /config/appdaemon/app und bindet diese analog zu dieser Beispielapp in der apps.yaml
im Ordner /config/appdaemon ein.
Viel Spaß beim Ausprobieren. Anregungen, Verbesserungen und einfallsreiche Ideen zur Nutzung des Magic Cubes sind in den Kommentaren herzlich willkommen!
Hi, bin gerade dieser Anleitung gefolgt – hat alles wunderbar funktioniert.
Nutze als “ScriptEngine” aber NodeRed, das tut aber nix zur Sache.
Vielleicht wäre es super, wenn du noch beschreibst, wie man die DeConz ID heraus findest – da habe ich etwas gebraucht, bis ich es rausgefunden habe. (Nämlich über den Eventbus die Info zu bekommen..)
Gruß,
Tristan
Hallo Tristan,
danke für das Feedback. Im Artikel wird doch ausführlich erklärt, dass man den Deconz Event im Event Bus abonnieren muss. Was genau war dir da nicht klar?
Hallo,
bei mir funktioniert es dank deiner Anleitung grundsätzlich auch, aber mein Cube erkennt nicht alle Actionen. Wenn ich Tap Twice oder rotate mit dem Cube mache erkennt er das garnicht. In der Entität ändert sich kein Wert. Wie kann ich das beheben ?
Hallo Luca,
ich habe gerade nochmal bei mir getestet und sowohl rotate als auch tap twice funktioniert einwandfrei. Abonniere mal im Eventbus das deconz_event und schau, ob du ggf. eine andere Version den Würfels hast und ob die Event Codes für diese beiden Events auch ausgelöst werden. Viel Erfolg.
Hallo,
richtig coole Anleitung.
Es wäre noch cool, wenn man den Cube für z.B. 10 Sekunden nicht beweg, dass der Status (sensor.magiccube_kueche_action) auf “idle” geht.
Hast du eine Idee wie man das umsetzten kann?
Das müsstest du in der AppDaemon App direkt regeln. Das sollte vermutlich mit einem:
import time
time.sleep(10)
an der richtigen Stelle zu bewältigen sein. Wenn du es herausfindest, ergänze ich den Beitrag gerne.
Habe es nicht hinbekommen und mir mit seiner Homeassistant Automation geholfen.
Hi,
ich versuche mich gerade mit der Einbindung deines Beispiels und habe es hinbekommen, die Events in einem eigenen Lovelace dargestellt zu bekommen, wenn auch ohne dem png, das nicht dargestellt wird, obwohl ich es in ein (neues) Verzeichnis /config/www kopiert habe.
Allerdings frage ich mich nun, in welchem Kontakt ich das Spotify-Skript einzubinden habe. Automatisierung, Skript…? Sorry, bin Newbie.
Wäre klasse, wenn du mir dazu Tipps geben könntest!
Vielen Dank!
Hallo Thomas,
Das sind beide Automationen. Der Ordner ./config/www wird erst nach einem Neustart von Homeassistant geladen, nicht aber sofort nach der Erstellung.
Viel Erfolg.
Danke dir! Klappt soweit prima und inzwischen wird auch das png geladen. Auf shake_air erfolgt keine Reaktion, obwohl es erkannt wird, aber ich tüftle weiter dran.
Bin noch am Überlegen, wie ich die Lautstärke am besten regeln werde …
Jedenfalls ne prima Anleitung und eine gute Anwendung für den Cube 🙂 Vielen Dank!
Ich habe festgestellt, dass ein zweiter Shake in Folge ignoriert wird. Erst nach einer bestimmten Zeit, die ich noch nicht herausgefunden habe, wird auf einen zweiten Shake reagiert. Dasselbe Verhalten habe ich bei einem analog angesteuerten Smart Switch festgestellt, wenn man den Schalter ein zweites mal betätigt. Löst man eine andere Aktion wie Gedrückhalten oder Doppelklick aus, wird darauf reagiert. Dieselbe Aktion kann nicht wiederholt werden. Mehrmaliges Rotieren klappt allerdings.
Konntest du selbiges Verhalten ebenfalls feststellen? Und hast du eventuell eine Lösung bzw. einen Workaround dafür?