Correctly calculate lux values at Xiaomi Aqara Motion Sensor's illuminance channel

Dear all,

thanks for the awesome openHAB software and the great and helpful community! Here’s a small contribution I can return.

I’ve played around with the said Aqara devices for a while now and have also seen the very small values for the illuminance channel. I’ve found various threads in the community suggesting to just multiply it by 10.000 to get the lux value. I believe that’s incorrect. You rather need to revert a transformation which is designed in the Zigbee spec but incorrectly implemented in the device.

Just add a custom transformation, e.g. by using a JavaScript Transformation as follows, and you’ll receive the correct value in lux.

function log10(val) {
  return Math.log(val) / Math.log(10);

(function(i) {
  var measuredValue = parseFloat(i);
  if(isNaN(measuredValue)) return "- lx";
  var illuminance = 10000*log10(measuredValue+1);
  return illuminance.toFixed(0) + " lx";

For details see here:


1 Like

Thank you for investigation.
I will try to add this to my setup.

I know my question is not related to the correct calculation itself, but more to the Javascript transformation service.
May I ask how your items looks like and how you handle the calculated lux values in rules?

String <itemName> { channel="<channelUID>"[profile="transform:JS", function="<filename>", sourceFormat="<valueFormat>"]}

The given above will give the result of the calculation as string type, but in rules I want to compare numbers which each other.

You may use the transformation in an item like this:

Number MyItem "Illuminance [JS(TransformationScript.js):%s]" {channel="zigbee:device:***:***_1_illuminance"}

In rules I calculate the value separately again because it’s fairly easy to do so.

I wonder if the reason you got a string type was because your item type is a String item type?

In other words, what if instead of:

String <itemname> .....

you use:

Number <itemname>

Would that solve it?

How to you convert the returned value of the transformation to a number? Please could you give an example how you calculate and compare this value in a rule.

@JimT: I’ve tried to do so.
Therefore I’ve changed the script slightly to return a number instead of a string, but I’m even not sure if a number is returned.

function log10(val) {
return Math.log(val) / Math.log(10);

(function(i) {
var measuredValue = parseFloat(i);
if(isNaN(measuredValue)) return 1;
var illuminance = 10000*log10(measuredValue+1);
return illuminance;

String MyItem “Illuminance” { channel=“zigbee:device:***_1_illuminance”[profile=“transform:JS”, function=“aqaraLuxCalc.js”]}

Number MyItem “Illuminance” { channel=“zigbee:device:***_1_illuminance”[profile=“transform:JS”, function=“aqaraLuxCalc.js”]}

The String Item will have the correct value, but the Number Item has always the value 1.0

Based on that post the JS Transformation always returns a String value.

To expand a bit, all of the Transformation services always return a raw string.

When you use a transform within a binding, the binding is written to parse the string return into whatever form the channel requires.
For example, it is possible to process an incoming MQTT payload in a transform, and the binding deals with converting the transform’s string output into a number for a number channel.
The binding author has to write code to support all this, and so not all bindings support transforms.

The transform profile does not currently implement any conversion on return string, so it really only works with String type Items. That’s a shame, but that’s how it is today.

1 Like

Can you gave an example of how you would display this in basic UI?

Not sure what exactly you are looking for, but here you should find it:

Just to complete this thread how I calculate the correct values, display them on the sitemap, but also use them in rules.


String MotionSensor_Illuminance "Illuminance [%s lux]" <light> { channel="zigbee:device:***:***:***_1_illuminance"[profile="transform:JS", function="aqaraLuxCalc.js"]}


function log10(val) {
  return Math.log(val) / Math.log(10);

(function(i) {
  var measuredValue = parseFloat(i);
  if(isNaN(measuredValue)) return 0;
  var illuminance = 10000*log10(measuredValue+1);
  return illuminance.toFixed(0) ;


Default item=MotionSensor_Illuminance


var illuminance = Integer::parseInt(MotionSensor_Illuminance.state.toString)
if(illuminance < threshold) ... //turn on light

Hiya, sorry, this is probably an extremely thick question, still learning :grinning: I’m using a set of these sensors via Zigbee2MQTT - presumably there’s no way to get the extra decimals on the lux value because the MQTT payload is coming through with the (incorrect) transformation already done?

When using Zigbee2MQTT the transformation is not required. It’s specific to the native Zigbee binding in OH.

The value you can see in the Z2M frontend (in the “Details” tab) is the correct one. As long as you can see the same value in OH you’re good.

Z2M also doesn’t provide a decimal value for the illumination of Aqara motion sensors. However, the accuracy of the measurement is not as good as a decimal position would make any sense, though.

Hope that helps :slight_smile:

Thank you for that, @guy-inkognito . Yeah, I’m seeing the same values in the Z2M interface. The numbers are just oddly low and often zero even for a dim but lit room, was hoping to get more precision out of them.

I actually get very odd results in general - when I reset the sensors they’ll register fluctuating figures in the two-hundreds and sample every few seconds, then after a while (I think after they go to sleep once) they’ll only sample once a minute, and pretty much only show values between 0 and 8. On waking they often stick at either 0 or 8, so activation that takes into account light level is impossible. Any thoughts on ways round this?

All of what you describe is true for my Aqara Motions Sensors as well. They’re just not designed to provide precise data on low illumination levels. The standard use case is “decide if day or night”, that’s it. For that, they’re good enough. Also, when you pair them, they have a very quick update cycle for the first 15 minutes or so and then switch into an more energy-saving mode where they provide fewer updates.

If you really need a more detailled measurement resolution on lower light levels you should consider e.g. using a BH1750 sensor in combination with a ESP8266.

Please be aware, low light illumination measurement specifically and light measurement in general is tricky and you’ll most likely never get close to “realistic” values when using non-professional devices. So always think of it as working with data points relative to each other within the boundaries of comparable equipment.

1 Like

Thanks Guy - I suspected I’d want to use a separate sensor but good to have it confirmed before I proceed. I’ve got the Raspberry Pi server running in the same room so I’ll just plug a BH1750 into that.