Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New device support]: Aqara Cube T1 Pro support request #15652

Closed
dabrahim opened this issue Dec 19, 2022 · 135 comments · Fixed by Koenkk/zigbee-herdsman-converters#5367
Closed
Labels
new device support New device support request

Comments

@dabrahim
Copy link

Link

https://www.aqara.com/en/product/cube-t1-pro

Database entry

{"id":5,"type":"EndDevice","ieeeAddr":"0x54ef44100062e05a","nwkAddr":25105,"manufId":4447,"manufName":"LUMI","powerSource":"Battery","modelId":"lumi.remote.cagl02","epList":[1,2,3],"endpoints":{"1":{"profId":260,"epId":1,"devId":259,"inClusterList":[0,3,6],"outClusterList":[0,3],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}},"2":{"epId":2,"inClusterList":[],"outClusterList":[],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}},"3":{"epId":3,"inClusterList":[],"outClusterList":[],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":25,"stackVersion":2,"hwVersion":1,"dateCode":"20220602","swBuildId":"2019\u0000www.","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1671404743897,"defaultSendRequestWhen":"immediate"}

Comments

I tried creating a custom converter using the one already exisiting for the older model MFKZQ01LM but I was stuck at some point, I didn't know how to properly fill the fromZigbee array in.

External converter

const fz = {...require('zigbee-herdsman-converters/converters/fromZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').fromZigbee};
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['lumi.remote.cagl02'],
    model: 'CTP-R01',
    vendor: 'Xiaomi'
    description: 'Aqara Cube T1 Pro',
    fromZigbee: [fz.xiaomi_basic],
    toZigbee: [], // Should be empty, unless device can be controlled (e.g. lights, switches).
    exposes: [e.battery(), e.battery_voltage(), e.angle('action_angle'), e.device_temperature(), e.power_outage_count(false), e.cube_side('action_from_side'), e.cube_side('action_side'), e.cube_side('action_to_side'), e.cube_side('side'), e.action(['shake', 'wakeup', 'fall', 'tap', 'slide', 'flip180', 'flip90', 'rotate_left', 'rotate_right'])],
};

module.exports = definition;

Supported color modes

No response

Color temperature range

No response

@dabrahim dabrahim added the new device support New device support request label Dec 19, 2022
@martindell
Copy link

martindell commented Dec 27, 2022

+1 from me - just purchased a Cube T1 Pro, not realising it's not yet supported. I'm happy to help with figuring out how to integrate it - but creating a custom converter is beyond my (very limited) abilities. It's really difficult to test too, because some cube actions seem to trigger every zigbee device on the network

@yschen5812
Copy link

+1!

@dabrahim
Copy link
Author

+1 from me - just purchased a Cube T1 Pro, not realising it's not yet supported. I'm happy to help with figuring out how to integrate it - but creating a custom converter is beyond my (very limited) abilities. It's really difficult to test too, because some cube actions seem to trigger every zigbee device on the network

From what I understand, the most important parts are the "fromZigbee" key (to process the input) and the "exposes" key (to output the entities that are displayed on HA). On the older version, they've created custom methods for the zigbee input processing. Maybe taking inspiration from there could lead us somewhere.

@dabrahim dabrahim changed the title [New device support]: [New device support]: Aqara Cube T1 Pro support request Dec 29, 2022
@Koenkk
Copy link
Owner

Koenkk commented Dec 29, 2022

What is logged to the debug log when triggering actions on this device?

See https://www.zigbee2mqtt.io/guide/usage/debug.html on how to enable debug logging.

@martindell
Copy link

I couldn't get it to log anything at all when I enabled debug logging - I'm bound to be doing something wrong. Part of the problem is that even without any external converter, the cube does weird 'stuff', messing with every other zigbee device - so there's a lot of noise to contend with. In response to @dabrahim 's comment - I played with the converter you specified at the top of this post, but couldn't get the device to show any values in Zigbee2MQTT

Screenshot 2022-12-29 at 10 36 47

@JJPro
Copy link

JJPro commented Dec 30, 2022

T1 Pro is a lot different than the classic cube.
It can operate in two modes:

  1. Scene Mode (default mode, this is new)
  • rotate
  • shake
  • hold
  • side up (which side is facing up when put down)
  • 1 min inactivity
  1. Action Mode (default mode, works like the classic cube)
  • slide
  • rotate
  • tap
  • flip90, flip180
  • shake
  • 1 min inactivity (new feature, but I personally didn't see any responses after 1min delay, I suppose it has to be manually turned on via Aqara Home app)

I'm new to HA, and only get to know z2m literally two weeks ago. So totally novice on coding converters.
But I fiddled with MFKZQ01LM's code a bit, and able to get shake, rotation, hold, and side up working. able to get action mode working (slide, rotate, tap, flip90, flip180, shake).

Those satisfies most of my needs.
It'll be really awesome if tap and flip could work too, but that requires switching operation mode to Action Mode, and I can't figure out how, to be precise, I don't know what cluster attribute controls the operation mode.

Looks like action mode is the default operation mode, I haven't figured out how to switch mode yet.


UPDATE 1/3

Pressing the LINK/PAIRING button 5 times can toggle between the two modes.

Some folks reporting my converter code doesn't work, and they run into debug messages like this:

Debug 2023-01-03 21:52:45 - Received Zigbee message from '0x54ef44100062c386', type 'commandToggle', cluster 'genOnOff', data '{}' from endpoint 1 with groupID 0
Debug 2023-01-03 21:52:45 - No converter available for 'CTP-R01' with cluster 'genOnOff' and type 'commandToggle' and data '{}'

It never occurred to me, I guess it could be because the cube needs the Aqara Home app for initial configuration, and I happen to have an Aqara hub and played with that first before pairing to Z2M and coding my converter.

Anyways, Here's my code, feel free to take it:

/**
  # Two Modes
 
  ## Scene Mode
   - rotate
   - shake
   - hold
   - side up
   - trigger after one-min inactivity
 
  ## Action Mode
   - slide
   - rotate
   - tap twice
   - flip90, flip180
   - shake
   - trigger after one-min inactivity

  # Clusters (Scene Mode): 

  ## Endpoint 2: 

  | Cluster            | Data                      | Description                   |
  | ------------------ | ------------------------- | ----------------------------- |
  | aqaraopple         | {329: 0-5}                | i side facing up              |
  | genMultistateInput | {presentValue: 0}         | action: shake                 |
  | genMultistateInput | {presentValue: 4}         | action: hold                  |
  | genMultistateInput | {presentValue: 2}         | action: wakeup                |
  | genMultistateInput | {presentValue: 1024-1029} | action: fall with ith side up |

  ## Endpoint 3: 

  | Cluster   | Data                                  | Desc                                       |
  | --------- | ------------------------------------- | ------------------------------------------ |
  | genAnalog | {267: 500, 329: 3, presentValue: -51} | 267: NA, 329: side up, presentValue: angle |
  
   
 */

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const xiaomi = require('zigbee-herdsman-converters/lib/xiaomi');
const e = exposes.presets;
const ea = exposes.access;

const OPS_MODE = {
  ACTION: 0,
  SCENE: 1,
};
const ops_mode_lookup = { 0: 'action mode', 1: 'scene mode' };

const aqara_opple = {
  cluster: 'aqaraOpple',
  type: ['attributeReport', 'readResponse'],
  options: (definition) => [
    ...xiaomi.numericAttributes2Options(definition),
    exposes.enum('operation_mode', ea.STATE, ['scene mode', 'action mode']),
  ],
  convert: (model, msg, publish, options, meta) => {
    // console.log('>>>> ops mode', meta.state.operationMode);
    if (msg.data.hasOwnProperty('155') || msg.data.hasOwnProperty('328')) {
      const ops_mode = msg.data['155'] || msg.data['328'];
      meta.state.operationMode = ops_mode;
    }

    const operation_mode = ops_mode_lookup[meta.state.operationMode];

    return {
      ...xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data),
      operation_mode,
      action: 'side_up',
      side_up: msg.data['329'],
    };
  },
};

const action_multistate = {
  ...fz.MFKZQ01LM_action_multistate,
  convert: (model, msg, publish, options, meta) => {
    if (meta.state.operationMode === OPS_MODE.ACTION) {
      return fz.MFKZQ01LM_action_multistate.convert(
        model,
        msg,
        publish,
        options,
        meta
      );
    } else {
      const value = msg.data['presentValue'];
      let scene_action_multistate;
      if (value === 0) scene_action_multistate = { action: 'shake' };
      else if (value === 2) scene_action_multistate = { action: 'wakeup' };
      else if (value === 4) scene_action_multistate = { action: 'hold' };
      else if (value >= 1024)
        scene_action_multistate = { action: 'side_up', side_up: value - 1024 };

      return scene_action_multistate;
    }
  },
};

const definition = {
  zigbeeModel: ['lumi.remote.cagl02'],
  model: 'CTP-R01',
  vendor: 'Lumi',
  description: 'Aqara cube T1 Pro',
  meta: { battery: { voltageToPercentage: '3V_2850_3000' } },
  fromZigbee: [aqara_opple, action_multistate, fz.MFKZQ01LM_action_analog],
  toZigbee: [],
  exposes: [
    /* Device Info */
    e.battery(),
    e.battery_voltage(),
    e.device_temperature(),
    e.power_outage_count(false),
    exposes
      .enum('operation_mode', ea.STATE, ['scene mode', 'action mode'])
      .withDescription(
        'Press LINK button 5 times to toggle between action mode and scene mode'
      ),
    /* Actions */
    e.angle('action_angle'),
    e.cube_side('action_from_side'),
    e.cube_side('action_side'),
    e.cube_side('action_to_side'),
    e.cube_side('side').withDescription('Destination side of action'),
    e.cube_side('side_up').withDescription('Upfacing side of current scene'),
    e.action([
      'shake',
      'wakeup',
      'fall',
      'tap',
      'slide',
      'flip180',
      'flip90',
      'hold',
      'side_up',
      'rotate_left',
      'rotate_right',
    ]),
  ],
};

module.exports = definition;

@filipeaparicio91
Copy link

I tried using the external converter code provided by @JJPro and although the cube is now correctly identified by Z2M (including sensor, configuration and diagnostic entities), they all report N/A or Unknown states.
Not only that, but when I shake the cube, it toggles all of by Zigbee lights on/off, despite not having any automation set up.

@JJPro
Copy link

JJPro commented Jan 1, 2023

they all report N/A or Unknown states

Do you mean the following:

  • action
  • action_angle
  • action_side
  • side_up
  • action_to_side
  • action_from_side

It's normal you see null values, cause they only contain values while in motion, and change very fast and won't get a chance to show in z2m frontend. If you take a look at the z2m logs or node-red debug console, you'll see their values being logged.

Not only that, but when I shake the cube, it toggles all of by Zigbee lights on/off, despite not having any automation set up.

I don't have this issue. Could it be the gateway you use? T1 Pro requires Zigbee 3.0. I'm using SONOFF Dongle Plus-E.

@filipeaparicio91
Copy link

@JJPro, thank you for the reply.

My logs are not reporting any activity from the Cube T1 Pro.

As for the dongle, I'm using the SONOFF Dongle Plus-P, which supports Zigbee 3.0 as well.

The configuration is correct, so I'm not really sure why this is happening.

@sjohannsen1
Copy link

I have a similar problem. The only activity i see in my logs is from endpoint 1 cluster "genOnOff". These messages are empty but are toggling all of my devices on/off, despite having no automation set up.

@JJPro
Copy link

JJPro commented Jan 3, 2023

@filipeaparicio91 @sjohannsen1

I've updated code in my original comment.

Last night, I reset my cube and found all my actions failing to work. Then I figured the cube is now in action mode (which works exactly like the classic cube).

Most likely during my last experiment, I have switched its operation mode to scene mode in Aqara Home app before pairing to Z2M, and the mode was saved. I wasn't aware of the situation, and assumed that to be the default operation mode.

Now the code has been updated, it should pull data in correctly, and work like the classic cube. I still haven't figured how to switch between modes yet.


For your issue randomly toggling other devices on/off, it yet haven't occurred to me.
I have a hypothesis, could it be like the cube is paired to a router instead of the coordinator? and that router is not fully compatible with zigbee 3.0? I'm still new to HA and Z2M, just random thoughts. Then I suggest try repairing it directly to your main coordinator. (See Map view in Z2M frontend to confirm)

image

@filipeaparicio91
Copy link

@JJPro,
I tried your updated code and the behavior is the same. I was able to retrieve some debug log entries from Z2M, which say this:

Debug 2023-01-03 21:52:45 - Received Zigbee message from '0x54ef44100062c386', type 'commandToggle', cluster 'genOnOff', data '{}' from endpoint 1 with groupID 0
Debug 2023-01-03 21:52:45 - No converter available for 'CTP-R01' with cluster 'genOnOff' and type 'commandToggle' and data '{}'

I keep getting these entries when I shake the cube and occasionally when I rotate it on a random side.

I continue not getting any values reported in Z2M, even in the info logs.

As for the connection, it is currently connected through a router (which supports Zigbee 3.0), but the same behavior was observed when connected directly to the coordinator.

@jzee923
Copy link

jzee923 commented Jan 4, 2023

I have a similar problem. The only activity i see in my logs is from endpoint 1 cluster "genOnOff". These messages are empty but are toggling all of my devices on/off, despite having no automation set up.

I'm having this exact same issue.

I have noticed this behavior
Flip:
Received Zigbee message from '0x54ef4410006a47c0', type 'commandOn', cluster 'genOnOff', data '{}' from endpoint 1 with groupID 0

Rotate:
Received Zigbee message from '0x54ef4410006a47c0', type 'commandOff', cluster 'genOnOff', data '{}' from endpoint 1 with groupID 0

Tap:
Received Zigbee message from '0x54ef4410006a47c0', type 'commandToggle', cluster 'genOnOff', data '{}' from endpoint 1 with groupID 0

Attempt to switch modes by pressing the reset button 5x:
Received Zigbee message from '0x54ef4410006a47c0', type 'attributeReport', cluster 'aqaraOpple', data '{"328":0}' from endpoint 1 with groupID 0
This last one goes back and forth from data '{"328":0}' to data '{"328":1}'

@JJPro
Copy link

JJPro commented Jan 4, 2023

Hey, I updated my code again in original comment, Now it's working on both scene mode and action mode. Pressing the LINK button 5 times to toggle between them.

I never saw the genOnOff/commandToggle message probably because the cube needs the Aqara Home app for initial configuration, and I happen to have an Aqara hub and played with that first before pairing to Z2M and started coding my converter. I have no idea.

I only have one cube and can't start from fresh to cover the genOnOff cluster personally. I don't see an option to reset the cube within Aqara Home app. and long pressing the LINK button and repairing doesn't give me that message either.

@jzee923
Copy link

jzee923 commented Jan 4, 2023

So your comment about the device being configured when you first connected it to the aqara hub got me thinking and I think I'm onto something.
I added a configure: section

Here's a snippet from your code with the added configure section

const definition = {
    zigbeeModel: ['lumi.remote.cagl02'],
    model: 'CTP-R01',
    vendor: 'Lumi',
    description: 'Aqara cube T1 Pro',
    meta: { battery: { voltageToPercentage: '3V_2850_3000' } },
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await endpoint.write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f});
        await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff','genPowerCfg','genMultistateInput']);
    },
    fromZigbee: [aqara_opple, action_multistate, fz.MFKZQ01LM_action_analog],

I'm not sure if this is complete and fully configured but I am finally starting to see the proper actions.

2023-01-04 01:00:02Received Zigbee message from '0x54ef4410006a47c0', type 'attributeReport', cluster 'genAnalogInput', data '{"267":500,"329":2,"presentValue":-58.40999984741211}' from endpoint 3 with groupID 0
Info 2023-01-04 01:00:02MQTT publish: topic 'zigbee2mqtt/0x54ef4410006a47c0', payload '{"action":"rotate_left","action_angle":-58.41,"action_from_side":null,"action_side":null,"action_to_side":null,"battery":null,"device_temperature":null,"linkquality":15,"operationMode":1,"operation_mode":"scene mode","power_outage_count":null,"side":null,"side_up":2,"voltage":null}'
Info 2023-01-04 01:00:02MQTT publish: topic 'zigbee2mqtt/0x54ef4410006a47c0', payload '{"action":"","action_angle":null,"action_from_side":null,"action_side":null,"action_to_side":null,"battery":null,"device_temperature":null,"linkquality":15,"operationMode":1,"operation_mode":"scene mode","power_outage_count":null,"side":null,"side_up":2,"voltage":null}'
Info 2023-01-04 01:00:02MQTT publish: topic 'zigbee2mqtt/0x54ef4410006a47c0/action', payload 'rotate_left'

I also needed to comment out these lines (55 and 68):
...xiaomi.numericAttributes2Options(definition),
...xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data),

@filipeaparicio91
Copy link

filipeaparicio91 commented Jan 4, 2023

@jzee923 & @JJPro

You two are getting somewhere!
Adding the configure block at first gave me a configuration error, but after switching between action and scene modes a few times, if correctly configured the cube and started reporting payloads correctly.

Commenting lines 55 & 68 was not required for my cube to start behaving correctly.

I have not tried any automation yet, but the action mode is showing some inconsistent behaviours. For example, occasionally it becomes stuck on one of the sides and does not update until a rotate or shake action happens. Using the scene mode is much more reliable, and accurately reports the side facing up every time.

Z2M logs also report rotate_left and rotate_right in both scene and action mode, but the HA action sensor only reports rotation when the cube is set to action mode.
EDIT: After changing modes back and forth, HA is now picking up rotation for scenes as well! I'm going to start testing some automations.

Nevertheless, this is a massive improvement and I can finally start testing the cube without having my lights flashing all the time.

@sjohannsen1
Copy link

sjohannsen1 commented Jan 4, 2023

Thank you @JJPro & @jzee923! The cube is finally reporting correctly with the addition of your code.
Just like @filipeaparicio91 has commented, I got a configuration error that could be resolved by changing the mode a few times.
Both modes behave consistently.
However, I noticed that the reported side was always off. After looking into the old "action_multistate"- converter, i noticed that assignment of numbers to sides has changed. While the old cube was numbered 0-5, the new one is numbered 1-6.

        Source: https://github.com/kirovilya/ioBroker.zigbee
            +---+
            | 3 |
        +---+---+---+
        | 5 | 1 | 2 |
        +---+---+---+
            | 4 |
            +---+
            | 6 |
            +---+
        Side 1 is with the MI logo, side 6 contains the battery door.

adapted from "zigbee-herdsman-converters/converters/fromZigbee"

I added:

const utils = require('zigbee-herdsman-converters/lib/utils');

and changed @JJPro converters by adding the old converter with updated sides:

const aqara_opple = {
   cluster: 'aqaraOpple',
   type: ['attributeReport', 'readResponse'],
   options: (definition) => [
     ...xiaomi.numericAttributes2Options(definition),
     exposes.enum('operation_mode', ea.STATE, ['scene mode', 'action mode']),
   ],
   convert: (model, msg, publish, options, meta) => {
     
     if (msg.data.hasOwnProperty('155') || msg.data.hasOwnProperty('328')) {
       const ops_mode = msg.data['155'] || msg.data['328'];
       meta.state.operationMode = ops_mode;
     };
     
     const operation_mode = ops_mode_lookup[meta.state.operationMode];
 
     return {
       ...xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data),
       operation_mode,
       action: 'side_up',
       side_up: msg.data['329']+1,
     };
   },
 };
 
const action_multistate = {
 cluster: 'genMultistateInput',
 type: ['attributeReport', 'readResponse'],
 options: [exposes.options.legacy()],
 convert: (model, msg, publish, options, meta) => {
   const value = msg.data['presentValue'];
   let result;
   if (value === 0) result = { action: 'shake' };
   else if (value === 2) result = { action: 'wakeup' };
   else if (value === 4) result = { action: 'hold' };
   else if (value >= 512) result = { action: 'tap', side: value - 511 };
   else if (value >= 256) result = { action: 'slide', side: value - 255 };
   else if (value >= 128) result = { action: 'flip180', side: value - 127 };
   else if (value >= 64) result = { action: 'flip90', action_from_side: Math.floor((value - 64) / 8) + 1, action_to_side: (value % 8) + 1, action_side: (value % 8) + 1, from_side: Math.floor((value - 64) / 8) + 1, to_side: (value % 8) + 1, side: (value % 8) + 1 };
   else if (value >= 1024) result = { action: 'side_up', side_up: value - 1023 };
   if (result && !utils.isLegacyEnabled(options)) { delete result.to_side, delete result.from_side };
   return result ? result : null;
 },
};

Now everything works consistently and as it should for me.
The only thing missing is a way to change modes without accessing the reset button.

@filipeaparicio91
Copy link

filipeaparicio91 commented Jan 4, 2023

@sjohannsen1 this works!

I had to reformat some of the else statements as Z2M was not starting, and it's now reporting the correct side every time.

Here is the full working code:

/**
  # Two Modes

  ## Scene Mode
    - rotate
    - shake
    - hold
    - side up
    - trigger after one-min inactivity

  ## Action Mode
    - slide
    - rotate
    - tap twice
    - flip90, flip180
    - shake
    - trigger after one-min inactivity

  # Clusters (Scene Mode): 

  ## Endpoint 2: 

  | Cluster            | Data                      | Description                   |
  | ------------------ | ------------------------- | ----------------------------- |
  | aqaraopple         | {329: 0-5}                | i side facing up              |
  | genMultistateInput | {presentValue: 0}         | action: shake                 |
  | genMultistateInput | {presentValue: 4}         | action: hold                  |
  | genMultistateInput | {presentValue: 2}         | action: wakeup                |
  | genMultistateInput | {presentValue: 1024-1029} | action: fall with ith side up |

  ## Endpoint 3: 

  | Cluster   | Data                                  | Desc                                       |
  | --------- | ------------------------------------- | ------------------------------------------ |
  | genAnalog | {267: 500, 329: 3, presentValue: -51} | 267: NA, 329: side up, presentValue: angle |


 */

  const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
  const exposes = require('zigbee-herdsman-converters/lib/exposes');
  const xiaomi = require('zigbee-herdsman-converters/lib/xiaomi');
  const utils = require('zigbee-herdsman-converters/lib/utils');
  const e = exposes.presets;
  const ea = exposes.access;
  
  const OPS_MODE = {
    ACTION: 0,
    SCENE: 1,
  };
  const ops_mode_lookup = { 0: 'action mode', 1: 'scene mode' };
  
  const aqara_opple = {
    cluster: 'aqaraOpple',
    type: ['attributeReport', 'readResponse'],
    options: (definition) => [
      ...xiaomi.numericAttributes2Options(definition),
      exposes.enum('operation_mode', ea.STATE, ['scene mode', 'action mode']),
    ],
    convert: (model, msg, publish, options, meta) => {
      
      if (msg.data.hasOwnProperty('155') || msg.data.hasOwnProperty('328')) {
        const ops_mode = msg.data['155'] || msg.data['328'];
        meta.state.operationMode = ops_mode;
      };
      
      const operation_mode = ops_mode_lookup[meta.state.operationMode];
  
      return {
        ...xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data),
        operation_mode,
        action: 'side_up',
        side_up: msg.data['329']+1,
      };
    },
  };
  
  const action_multistate = {
    cluster: 'genMultistateInput',
    type: ['attributeReport', 'readResponse'],
    options: [exposes.options.legacy()],
    convert: (model, msg, publish, options, meta) => {
        const value = msg.data['presentValue'];
        let result;
        if (value === 0) result = { action: 'shake' };
        else if (value === 2) result = { action: 'wakeup' };
        else if (value === 4) result = { action: 'hold' };
        else if (value >= 512) result = {action: 'tap', side: value-511};
        else if (value >= 256) result = {action: 'slide', side: value-255};
        else if (value >= 128) result = {action: 'flip180', side: value-127};
        else if (value >= 64) result = {action: 'flip90', action_from_side: Math.floor((value-64) / 8)+1, action_to_side: (value % 8)+1, action_side: (value % 8)+1, from_side: Math.floor((value-64) / 8)+1, to_side: (value % 8)+1, side: (value % 8)+1};
        else if (value >= 1024) result = { action: 'side_up', side_up: value - 1023 };
        if (result && !utils.isLegacyEnabled(options)) {delete result.to_side, delete result.from_side};
      return result ? result : null;
    },
  };
  
  const definition = {
    zigbeeModel: ['lumi.remote.cagl02'],
    model: 'CTP-R01',
    vendor: 'Lumi',
    description: 'Aqara cube T1 Pro',
    meta: { battery: { voltageToPercentage: '3V_2850_3000' } },
    configure: async (device, coordinatorEndpoint, logger) => {
      const endpoint = device.getEndpoint(1);
      await endpoint.write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f});
      await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff','genPowerCfg','genMultistateInput']);
    },
    fromZigbee: [aqara_opple, action_multistate, fz.MFKZQ01LM_action_analog],
    toZigbee: [],
    exposes: [
      /* Device Info */
      e.battery(),
      e.battery_voltage(),
      e.device_temperature(),
      e.power_outage_count(false),
      exposes
        .enum('operation_mode', ea.STATE, ['scene mode', 'action mode'])
        .withDescription(
          'Press LINK button 5 times to toggle between action mode and scene mode'
        ),
      /* Actions */
      e.angle('action_angle'),
      e.cube_side('action_from_side'),
      e.cube_side('action_side'),
      e.cube_side('action_to_side'),
      e.cube_side('side').withDescription('Destination side of action'),
      e.cube_side('side_up').withDescription('Upfacing side of current scene'),
      e.action([
        'shake',
        'wakeup',
        'fall',
        'tap',
        'slide',
        'flip180',
        'flip90',
        'hold',
        'side_up',
        'rotate_left',
        'rotate_right',
      ]),
    ],
  };
  
  module.exports = definition;

EDIT: I just tried automating the cube while in "scene mode" and everything runs smoothly. Using MQTT as the trigger to listen to "zigbee2mqtt/[YOUR_CUBE_NAME]/action", I can choose between "tap", "side_up", "rotate_left", "rotate_right", "hold", "shake" and "wakeup" payloads in combination with the "Side up" sensor.
I have not tried doing any automations using the "action mode" yet, but I assume it will work as well.

As previously mentioned, I think the last missing piece is being able to change the Operation mode directly on HA or Z2M without having to open the cube.

@jzee923
Copy link

jzee923 commented Jan 4, 2023

As previously mentioned, I think the last missing piece is being able to change the Operation mode directly on HA or Z2M without having to open the cube.

I'm fairly certain we need to create or use an existing converter in toZigbee in order for this to work.
I tried a few things but have made no progress.
I'm not entirely sure where to go for this one.

@JJPro
Copy link

JJPro commented Jan 5, 2023

@CrazyCoder no, ... is spread operator in JS.

@dabrahim
Copy link
Author

dabrahim commented Jan 5, 2023

Thank you all for your support ! I was finally able to set up the cube and trigger a few events ! The only event I'm failing to trigger so far is the "double tap" one. Aside from that everything works flawlessly on my side.

@jzee923
Copy link

jzee923 commented Jan 6, 2023

Been working on getting a toZigbee converter working in order to change the operating mode in the z2m ui.
image
I was able to get it showing up as a toggle button but it doesn't actually function

const tzLocal = {
    MFCZQ12LM_scene_mode: {
        key: ['operation_mode'],
        convertSet: async (entity, key, value, meta) => {
            // Support existing syntax of a nested object just for the state field. Though it's quite silly IMO.
            const targetValue = value.hasOwnProperty('state') ? value.state : value;
            // Switches using aqaraOpple 0x0200 on the same endpoints as the onOff clusters.
            const lookupState = {"action": 0, "scene": 1};
            await entity.write('aqaraOpple', {0x0148: {value: lookupState[targetValue], type: 0x20}}, {manufacturerCode: 0x115f});
        },
        convertGet: async (entity, key, meta) => {
            await entity.read('aqaraOpple', [0x0148], {manufacturerCode: 0x115f});
        },
    },
}

I'm having issues with this line here
await entity.write('aqaraOpple', {0x0148: {value: lookupState[targetValue], type: 0x20}}, {manufacturerCode: 0x115f});

If anyone has a sniffer they can use to get the correct values, I think we'll be one step closer to a fully functional device

I've also cleaned up the code a bit so that it better matches the samples provided in the docs. Most of this has just been a lot of trial and error so i'm not sure if everything is correct.

/**
# Two Modes

## Scene Mode
    - rotate
    - shake
    - hold
    - side up
    - trigger after one-min inactivity

## Action Mode
    - slide
    - rotate
    - tap twice
    - flip90, flip180
    - shake
    - trigger after one-min inactivity

# Clusters (Scene Mode): 

## Endpoint 2: 

| Cluster            | Data                      | Description                   |
| ------------------ | ------------------------- | ----------------------------- |
| aqaraopple         | {329: 0-5}                | i side facing up              |
| genMultistateInput | {presentValue: 0}         | action: shake                 |
| genMultistateInput | {presentValue: 4}         | action: hold                  |
| genMultistateInput | {presentValue: 2}         | action: wakeup                |
| genMultistateInput | {presentValue: 1024-1029} | action: fall with ith side up |

## Endpoint 3: 

| Cluster   | Data                                  | Desc                                       |
| --------- | ------------------------------------- | ------------------------------------------ |
| genAnalog | {267: 500, 329: 3, presentValue: -51} | 267: NA, 329: side up, presentValue: angle |


*/

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const xiaomi = require('zigbee-herdsman-converters/lib/xiaomi');
const e = exposes.presets;
const ea = exposes.access;

const fzLocal = {
    MFCZQ12LM_action_multistate: {
        cluster: 'genMultistateInput',
        type: ['attributeReport', 'readResponse'],
        options: [exposes.options.legacy()],
        convert: (model, msg, publish, options, meta) => {
            /*
            Source: https://github.com/kirovilya/ioBroker.zigbee
                +---+
                | 4 |
            +---+---+---+
            | 3 | 0 | 2 |
            +---+---+---+
                | 1 |
                +---+
                | 5 |
                +---+
            Side 0 is with the Aqara logo, side 5 contains the battery door.
            presentValue = 0 = shake
            presentValue = 2 = wakeup
            presentValue = 3 = fly/fall
            presentValue = y + x * 8 + 64 = 90º Flip from side x on top to side y on top
            presentValue = x + 128 = 180º flip to side x on top
            presentValue = x + 256 = push/slide cube while side x is on top
            presentValue = x + 512 = double tap while side x is on top
            */
            const value = msg.data['presentValue'];
            let result = null;
            if (meta.state.operation_mode === "action") {
                if (value === 0) result = {action: 'shake'};
                else if (value === 2) result = {action: 'wakeup'};
                else if (value === 3) result = {action: 'fall'};
                else if (value >= 512) result = {action: 'tap', side: value-512+1};
                else if (value >= 256) result = {action: 'slide', side: value-256+1};
                else if (value >= 128) result = {action: 'flip180', side: value-128+1};
                else if (value >= 64) {
                    result = {
                        action: 'flip90', 
                        action_from_side: Math.floor((value-64) / 8) + 1, 
                        action_to_side: (value % 8) + 1, 
                        action_side: (value % 8) + 1,
                        from_side: Math.floor((value-64) / 8) + 1, 
                        to_side: (value % 8) + 1, 
                        side: (value % 8) + 1,
                    };
                }
    
                return result ? result : null;
            } else {
                if (value === 0) result = { action: 'shake' };
                else if (value === 2) result = { action: 'wakeup' };
                else if (value === 4) result = { action: 'hold' };
                else if (value >= 1024)
                    result = { action: 'side_up', side_up: value - 1024 + 1 };

                return result;
            }
        },
    },
    aqara_opple: {
        cluster: 'aqaraOpple',
        type: ['attributeReport', 'readResponse'],
        options: xiaomi.numericAttributes2Options,
        convert: (model, msg, publish, options, meta) => {
            let payload = {};
            payload = xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data);
            if( msg.data.hasOwnProperty(328) ){
                const opmode = {0: "action", 1: "scene"}
                payload.operation_mode = opmode[msg.data[328]]
            }
            if( msg.data.hasOwnProperty(329) ){
                payload.side = msg.data[329] + 1;
            }
            return payload;
        },
    },
}

const tzLocal = {
    MFCZQ12LM_scene_mode: {
        key: ['operation_mode'],
        convertSet: async (entity, key, value, meta) => {
            // Support existing syntax of a nested object just for the state field. Though it's quite silly IMO.
            const targetValue = value.hasOwnProperty('state') ? value.state : value;
            // Switches using aqaraOpple 0x0200 on the same endpoints as the onOff clusters.
            const lookupState = {"action": 0, "scene": 1};
            await entity.write('aqaraOpple', {0x0148: {value: lookupState[targetValue], type: 0x20}}, {manufacturerCode: 0x115f});
        },
        convertGet: async (entity, key, meta) => {
            await entity.read('aqaraOpple', [0x0148], {manufacturerCode: 0x115f});
        },
    },
}

const definition = {
    zigbeeModel: ['lumi.remote.cagl02'],
    model: 'MFCZQ12LM',
    vendor: 'Xiaomi',
    description: 'Aqara Cube T1 Pro',
    meta: { battery: { voltageToPercentage: '3V_2850_3000' } },
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await endpoint.write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f});
        await reporting.bind(endpoint, coordinatorEndpoint, ['genBasic','genOnOff','genPowerCfg','genMultistateInput']);
        await endpoint.read('genPowerCfg', ['batteryVoltage']);
        await endpoint.read('aqaraOpple', [0x0148], {manufacturerCode: 0x115f});
        await endpoint.read('aqaraOpple', [0x0149], {manufacturerCode: 0x115f});
    },
    fromZigbee: [fz.xiaomi_basic,fzLocal.aqara_opple, fzLocal.MFCZQ12LM_action_multistate, fz.MFKZQ01LM_action_analog],
    toZigbee: [tzLocal.MFCZQ12LM_scene_mode],
    exposes: [
        /* Device Info */
        e.battery(),
        e.battery_voltage(),
        e.device_temperature(),
        e.power_outage_count(false),
        exposes
            .enum('operation_mode', ea.STATE_SET, ['scene', 'action'])
            .withDescription(
                'Press LINK button 5 times to toggle between action mode and scene mode'
            ),
        /* Actions */
        e.angle('action_angle'),
        e.cube_side('action_from_side'),
        e.cube_side('action_side'),
        e.cube_side('action_to_side'),
        e.cube_side('side').withDescription('Destination side of action'),
        e.cube_side('side_up').withDescription('Upfacing side of current scene'),
        e.action([
            'shake',
            'wakeup',
            'fall',
            'tap',
            'slide',
            'flip180',
            'flip90',
            'hold',
            'side_up',
            'rotate_left',
            'rotate_right',
        ]),
    ],
};

module.exports = definition;

@JJPro
Copy link

JJPro commented Jan 6, 2023

@jzee923

await entity.write('aqaraOpple', {0x0148: {value: lookupState[targetValue], type: 0x20}}, {manufacturerCode: 0x115f});

How do you find the data points (0x0148, 0x20) to overwrite? I'm still new to z2m, there's a lot to learn. Thanks~

@jzee923
Copy link

jzee923 commented Jan 6, 2023

0x0148 is just 328 in hex. (the value we were seeing for changing modes)
I'm brand new to this too so I've just been fumbling through all of this.
I took a look at the logs when I set the mode using the 5 clicks.

zigbee-herdsman:controller:log Received 'zcl' data '{"frame":{"Header":{"frameControl":{"frameType":0,"manufacturerSpecific":false,"direction":1,"disableDefaultResponse":true,"reservedBits":0},"transactionSequenceNumber":60,"manufacturerCode":null,"commandIdentifier":10},"Payload":[{"attrId":328,"dataType":32,"attrData":0}],"Command":{"ID":10,"name":"report","parameters":[{"name":"attrId","type":33},{"name":"dataType","type":32},{"name":"attrData","type":1000}]}},"address":13477,"endpoint":1,"linkquality":36,"groupID":0,"wasBroadcast":false,"destinationEndpoint":1}'

I saw this in there and just gave it a shot
"Payload":[{"attrId":328,"dataType":32,"attrData":0}],

So datatype: 32 translates to 0x20 in hex. But this wasn't working.
I'm not sure how to get these values without a sniffer.

@Ekinox09
Copy link

Ekinox09 commented Jan 6, 2023

Hi, thanks all for your great job. I've started a thread (#15956) yesterday for this device but your job is much more advanced.
I've tested your code on a external converter and it works perfectly (plugin zigbee2MQTT on Jeedom software).
Here are my suggestion and observation:

  • Suggestion: Would be great to have the sides numbered from 1 to 6 (because they are numbered this way, physically, on the cube). That would really help.
  • Observation: In "action mode" my device is not reliable (on Z2M or deCONZ). Some movements are not detected, randomly, during a short period. I've noticed that a "tap" or a "shake" is always detected. But a "flip" or a "slide" are not; especially after a sleep period. On the contrary, in the "scene mode", it's much more reliable and the "side_up" is near to 100% with my device. So I will continue this way (I use the cube for managing the house modes (sleep, wakeup, leaving, coming home); unfortunately, I will have to forget "slide" and "tap" movements...

@JJPro
Copy link

JJPro commented Jan 31, 2023

@Rhysgh r u talking about Null values for battery stuff? Those info takes a long time to show. It's normal for battery powered devices.
But you can make them show immediately by pressing on LINK, or give your cube a throw motion.

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

Hi, yes, sorry, I'm terrible for lacking context.

I did try a throw, but I guess I didn't throw it right .. I see it's disabled here. Could that be the issue? i still have all null entries

image

@OstfrieseUnterwegs
Copy link

I would try to remove/unlink and add the device again - maybe even a couple of times.

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

Ok tried deleign (forece reoved) , reatrted and readded .. i noticed the values where N/A and then changed to Null .. but are always Null nomatter what I try.. and the avalibility is still disabled.

@filipeaparicio91
Copy link

filipeaparicio91 commented Jan 31, 2023

@Rhysgh, when you perform an action on the cube, does Z2M produce any logs? You should be seeing a payload with the action show up when you rotate, flip, shake, etc.
On HA and Z2M, the values show as Null or N/A for side, rotate, etc. because they are instant. If they show up in the Z2M logs, it means that the cube is functioning properly.
To make automations, you can use MQTT as a trigger or condition, and just use the payload content instead of the device state.
Example:

Trigger - MQTT
Topic - "zigbee2mqtt/CUBE NAME/action"
Condition - Template - {{ trigger.payload == "rotate_left" }}

This will trigger the automation when an action payload is detected by MQTT, and only perform it if the cube was rotated left.

The Availability showing up as "Disabled" is standard on Z2M. To change this you need to modify your Z2M configuration: https://www.zigbee2mqtt.io/guide/configuration/device-availability.html

EDIT:

If you want to enable availability, edit your configuration.yaml in the zigbee2mqtt folder and add this line:
availability: true

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

Thanks, I had a look. I see this.

Re availability, I had figured that was the issue. Do I need to set it to available? or is it ok as it is?

image

@jzee923
Copy link

jzee923 commented Jan 31, 2023

Make sure you're using the latest code (https://github.com/JJPro/CTP-R01-converter/blob/trunk/CTPR01.js)
I was having this issue when the cube wasn't configuring properly.
Might also be worth a shot trying the orange reconfigure button

@filipeaparicio91
Copy link

@Rhysgh
The availability feature doesn't affect the Cube's functionality - it simply reports if the device is online or not.
Based on the logs, it looks like the converter was not properly set up. Did you include the file in your zigbee2mqtt configuration.yaml file?
Also, have you restarted Z2M after adding the converter and editing the file?

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

I used this file - https://github.com/JJPro/CTP-R01-converter
image

This is my setup. Is that correct?

image

@OstfrieseUnterwegs
Copy link

@Rhysgh
That looks correct. Also, Z2M seems to have recognized the device as it shows up in a details page (your earlier screenshot)
Another guess: When you add the device to the network, did you limit the pairing to the coordinator? Sometimes it makes a difference if one adds new devices via a repaeter.

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

@OstfrieseUnterwegs

No, I was permitted to join all, yea before adding the file ( I didn't know it wasn't supported), I got unsupported, but now I see it as you say.

its connected, and seeing it as in the sate I see what mode its in, and it changes when i click 5 times
image

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

I am on

Home Assistant 2023.2.0b7
Supervisor 2023.01.1
Operating System 9.5
Frontend 20230130.0 - latest

maybe that's an issue?

@OstfrieseUnterwegs
Copy link

I cannot help with the HASS environment. But if you have not done so far, try:

  • Remove the device

  • Permit joining on coordinator only
    image

  • add the device again

@JJPro
Copy link

JJPro commented Jan 31, 2023

Also check your battery, the first screenshot you posted has a linkquality of 42. That's probably too low.
If distance to your coordinator isn't an issue, then it might be the battery.

@JJPro
Copy link

JJPro commented Jan 31, 2023

The pairing process will draw a lot of power.

@JJPro
Copy link

JJPro commented Jan 31, 2023

The minimum working linkquality from my experiment, just tested minutes ago, in my environment, is around 67.

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

Ok, removed, restarted, join coordinator, re-added.

image

I have these logs

image

Do I need something in here?
image

I think ill delete everything and start again.

@Rhysgh
Copy link

Rhysgh commented Jan 31, 2023

This may help you diagnose the issue.

image

@JJPro
Copy link

JJPro commented Jan 31, 2023

Hey @Rhysgh, telegram me if u still need help diagnosing the problem.
We can set up a Zoom meeting.
Besides helping you out, you can also help me make the converter better, because yours reported genOnOff cluster messages that I never come across, it's a good chance for me to dig more into it.

Let me know.
Telegram: contact info deleted

@amaisano
Copy link

amaisano commented Feb 1, 2023

Ah shoot, already added. I just saw this pop up on the 2023.2 HA update log:

Sensor sensor.cube_action_angle has device class None, state class None and unit ° thus indicating it has a numeric value; however, it has the non-numeric value: None (<class 'str'>); Please update your configuration if your entity is manually configured, otherwise create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+mqtt%22

@JJPro
Copy link

JJPro commented Feb 1, 2023

@amaisano It's nothing about the converter. z2m emits empty action values when you have home assistant integration enabled. See here., and the empty value (null value) is interpreted as None by HA.

After looking at HA codebase, it's the newly introduced precision feature that puts stricter constraints on numerical values, that raised this warning.
z2m may needs an update to cope with the new HA release.
There's already an open issue about it. #16360

@SirGoodenough
Copy link

SirGoodenough commented Feb 3, 2023

Has anyone noticed that the temp reported by this thing is always 25C? I even see it in the shared logs and cube data above from other users.
Is it a code problem not reading the right variable?

It should currently be reading about 15c... Always 25.

    operation_mode: 'scene_mode',
    voltage: 3061,
    battery: 100,
    device_temperature: 25,
    power_outage_count: 17,
    current: 0,
    ...

@oskarslusitis
Copy link

yes, it's always 25°C for me as well.

@JJPro
Copy link

JJPro commented Feb 6, 2023

The device reports static temperature 25 °C, rendering device_temperature useless.
So I have created a PR to remove it.
Koenkk/zigbee-herdsman-converters#5446

@SirGoodenough
Copy link

SirGoodenough commented Feb 6, 2023

The device reports static temperature 25 °C, rendering device_temperature useless. Koenkk/zigbee-herdsman-converters#5446

What a shame. I use that as a sensor on the old cubes...

Related / unrelated question. @JJPro ...
The ZigPY version of the cube driver (used by ZHA) does not include the features you have found. Would you consider looking at that? I understand if you don't. Thought maybe your knowledge could give them a boost.

zigpy/zha-device-handlers#2145 (comment)

@JJPro
Copy link

JJPro commented Feb 6, 2023

@SirGoodenough
Unfortunately, I'm not familiar with ZigPY, and don't have the time to learn a new system.
However, feel free to ping me if you find someone who wants to work on it, I'd love to give him/her some hints and share knowledge about my implementation (specifically mode switch).

@NicoQubo
Copy link

NicoQubo commented Mar 13, 2023

Hi everyone,

The scene mode of the new cube is amazingly accurate indeed.

I wanted to ask if anyone else experiences the same (inconsistent) behaviour of the cube as I do:

  1. When placed on a surface it accurately displays the side_up:{"action":"side_up","linkquality":116,"operation_mode":"scene_mode","side":4}

  2. When rotated right or left - then sometimes the payload doesn't come trough and sometimes it does.
    {"action":"rotate_right","action_angle":11.7,"linkquality":105,"operation_mode":"scene_mode","side":4}

This second message/payload:

  • Sometimes immediately comes through, when placing the cube down and directly rotating it left or right.
  • Sometimes doesn't come through at all, when placing the cube down and directly rotating it left or right. After waiting for a bit and rotating again... it does come through.
  • Always comes through, when placing the cube down and after waiting for a bit (like 3 seconds) and then rotating right or left.

So, it feels a bit like 'rotating_left' and 'rotating_right' directly is not working as expected right now? In the ideal situation, you would like to choose a face up - like 'lights on' and use rotate_left/right to dim those lights directly. Or have consistent feedback when using the cube and rotating left or right.

Or maybe I'm missing something here? What are you guys thinking?

@JJPro
Copy link

JJPro commented Mar 15, 2023

Hi @NicoQubo, I see what you were trying to achieve. If I understand you correctly, you want to achieve "rotating on side change", e.g. turning on lights and dimming at the same time.
Unfortunately, that won't work, side change and rotation can't be triggered in one go, because flip_to_side and rotate actions are reported from the same multistate cluster, and it reports one action at a time.

Always comes through, when placing the cube down and after waiting for a bit (like 3 seconds) and then rotating right or left.

That is the expected behavior.
This is what happened during the operation sequence:

  1. You place down the cube and let go (OR wait for 3s like you did). (Side won't be reported unless you let go, otherwise it'll think you are still fiddling with it, and will wait until you let go to report the final up-facing side)
  2. multistate cluster reports flip_to_side event.
  3. aqaraOpple cluster reports side_up event.
  4. You rotate cube
  5. multistate cluster reports rotation event.

Hope this clears your confusion.

@NicoQubo
Copy link

NicoQubo commented Mar 16, 2023

Hi @JJPro,

Thanks for your insightful reply! There was something in what you said that triggered me.
'Place down the cube and let go.'

This sounds pretty obvious, but actually was the key for me!

I now also understand the inconsistency in 1. and 2., because I wasn't aware that my hand resting on the cube in between actions influenced the sensors! I probably sometimes 'let go' or 'rested hand on it' which obviously gave these weird reports. So: THANKS for that brilliant remark.

  1. Sometimes immediately comes through, when placing the cube down and directly rotating it left or right.
  2. Sometimes doesn't come through at all, when placing the cube down and directly rotating it left or right. After waiting for a bit and rotating again... it does come through.

In both these cases: I placed down the cube and probably sometimes removed my hand from it in advance of wanting to rotate.
This is the key. REMOVE YOUR HANDS FROM THE CUBE :-)

I tried 'placing down, letting go and rotating' a dozen times now, and it works every time.
In which case you can almost immediately rotate left or right!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new device support New device support request
Projects
None yet
Development

Successfully merging a pull request may close this issue.