It's time to fix Aqara battery bug

Following up on this: I see similar behavior for Aqara illuminance sensors (lumi.sen_ill.mgl01): they went down to 0 % regularly - especially those which run for a longer time now (almost 3 years!).

Can we eliminate those 0 % reportings for that hardware too (like we did in Ignore invalid Aqara -100 temperature by amelchio · Pull Request #6723 · dresden-elektronik/deconz-rest-plugin · GitHub)?

Sure, do you know the “bad value” ?
The problem is this DDf use a JS script, so a change in this file will impact all device that use it, so better to create a new one for this device

Use Instead

if (measuredValue > 0 && measuredValue < 0xffff) {

Thinking about this a second time resulted in: maybe I misunderstood something.

  1. Aqara multi sensor (temperature, pressure etc.): had -100 ° values for temperature sensor. Sorted out by filtering those on a DDF level.
  2. Aqara illuminance sensor: it’s not the actual illuminance sensor (like it was the temperature sensor of the other Aqara device at #1), but the battery sensor falling to 0 % regularly for no obvious reason (as shown in It's time to fix Aqara battery bug - #41 by bcutter).

So if I/one would filter `battery: 0 % to be ignored", would that actually be a solution? Because, well… the battery at some day could really be empty, so reported as 0 % (never seen that, as all Aqara devices die way before, usually at 70 to at best 60 %).

Ha ? so you have the problem on the battery too ? Not the luminance ?
So it’s exactly the same code.

Did you read my last post? If I suppress 0 %, what happens if the battery actually is at 0 %? :slight_smile:

You will have the previous value, the one before the 0 value. And like you I never see a 0 value for battery, the device die before.

1 Like

OK, that sound’s good. Now I need a working custom DDF to test this. I’m (still) not really into DDF-ing.

a) Can you give me a kick-start?
b) Do you think this might be helpful for others too (or an individual/custom corner case) so it should make its way into deCONZ?

In the end it’s another workaround for the unreliable Aqara sensor firmware. -100° here, 0 % battery life there…

a) What is your OS ? (You have probably already say it, but I don’t remember all the post).
You can edit fastly the original DDF, or create a new one with the change in the user folder (I will give you the DDF)

b) IDK, on my side I don’t have the issue, and it’s not a common issue for me (I can be wrong), but you can sumbit a PR, we will see other devs decision.

I don’t think it would be allowed. Generally, you dont want to change values other than up/downscaling.

Well, you remember we filtered out the -100 degrees values (see earlier posts in this topic)? :slight_smile:

Using HA OS. I know from earlier custom DDFs how and where to store them so they are persistent (not overwritten on addon/deCONZ container update).

It’s only I have no idea what the content of the DDF should be to suppress/ignore the 0 % value only for the battery values for the „lumi.sen_ill.mgl01“ device.

Yes, but that’s different opposed to the battery percentage. Mine seem to work okay.

Ah OK. Why is it different? Because you’ve seen this (-100 °) with your sensors too? So following that logic now there are two options for the battery values:

  • my Aqara illuminance sensors are the only one in the world behaving like that
  • yours are the only one working perfectly meaning never sending 0 % for some time before switching back

Both cases are very likely not true and therefore not a no-go for a PR to deliver the fix to others. But TBH, once I have a custom DDF with the great help of @Smanar, I will test it for a while. If it works for me, fine. If others don’t need it, fine too :slight_smile:

1 Like

I never had the -100 degree one. Also, as its out its range (-20 as minimal temp, Aqara Temperature and Humidity Sensor T1 - Aqara UK Shop) it can be ignored.

I think i’ve seen the battery on lower than 70%.

IDK, he is right, on my side I never had 0 value for battery, so using a filter for them is an idea to test.

It’s only I have no idea what the content of the DDF should be to suppress/ignore the 0 % value only for the battery values for the „lumi.sen_ill.mgl01“ device

Can try this DDF, based on https://github.com/dresden-elektronik/deconz-rest-plugin/blob/master/devices/xiaomi/xiaomi_gzcgq01lm_light_sensor.json

{
  "schema": "devcap1.schema.json",
  "doc:path": "xiaomi/xiaomi_gzcgq01lm_light_sensor.md",
  "doc:hdr": "Light sensor GZCGQ01LM",
  "manufacturername": ["$MF_XIAOMI", "$MF_LUMI"],
  "modelid": ["lumi.sen_ill.mgl01", "lumi.sen_ill.mgl01"],
  "vendor": "Xiaomi",
  "product": "Mijia Light Sensor GZCGQ01LM",
  "status": "Gold",
  "md:known_issues": [ ],
  "sleeper": true,
  "subdevices": [
    {
      "type": "$TYPE_LIGHT_LEVEL_SENSOR",
      "restapi": "/sensors",
      "uuid": [
        "$address.ext",
        "0x01",
        "0x0400"
      ],
      "fingerprint": {
        "profile": "0x0104",
        "device": "0x0106",
        "endpoint": "0x01",
        "in": [
          "0x0001",
          "0x0400"
        ]
      },
      "items": [
        {
          "name": "attr/lastseen"
        },
        {
          "name": "attr/manufacturername"
        },
        {
          "name": "attr/modelid",
          "awake": true
        },
        {
          "name": "attr/name"
        },
        {
          "name": "attr/swversion",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x08",
            "script": "xiaomi_swversion.js"
          },
          "read": {
            "fn": "none"
          }
        },
        {
          "name": "attr/type"
        },
        {
          "name": "attr/uniqueid"
        },
        {
          "name": "config/on"
        },
        {
          "name": "config/battery",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x01",
            "eval": "const vmin = 2700; const vmax = 3000; const v = Math.max(vmin, Math.min(Attr.val, vmax)); const bat = Math.round(((v - vmin) / (vmax - vmin)) * 100); if (bat>0){Item.val = Math.max(0, Math.min(bat, 100));}"
          }
        },
        {
          "name": "config/reachable"
        },
        {
          "name": "config/tholddark"
        },
        {
          "name": "config/tholdoffset"
        },
        {
          "name": "state/lightlevel",
          "parse": {
            "at": "0x0000",
            "cl": "0x0400",
            "ep": 1,
            "script": "../generic/illuminance_cluster/sml_light_level.js"
          }
        },
        {
          "name": "state/dark"
        },
        {
          "name": "state/daylight"
        },
        {
          "name": "state/lux"
        },
        {
          "name": "state/lastupdated"
        }
      ],
      "example": {
        "config": {
          "battery": 100,
          "on": true,
          "reachable": true,
          "tholddark": 12000,
          "tholdoffset": 7000
        },
        "ep": 1,
        "etag": "761fff5a33ee73a8b9e4ec2e8ee5dac2",
        "lastseen": "2021-01-13T12:07Z",
        "manufacturername": "XIAOMI",
        "modelid": "lumi.sen_ill.mgl01",
        "name": "Light Sensor",
        "state": {
          "dark": false,
          "daylight": false,
          "lastupdated": "2021-01-13T12:07:27.511",
          "lightlevel": 13425,
          "lux": 22
        },
        "swversion": "0.0.0_0027",
        "type": "ZHALightLevel",
        "uniqueid": "04:cf:8c:df:3c:7f:1f:80-01-0400"
      }
    }
  ],
  "bindings": [
    {
      "bind": "unicast",
      "src.ep": 1,
      "cl": "0x0400",
      "report": [
        {
          "at": "0x0000",
          "dt": "0x21",
          "min": 10,
          "max": 3600,
          "change": "0x0064"
        }
      ]
    }
  ]
}

The magic is here


        {
          "name": "config/battery",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x01",
            "eval": "const vmin = 2700; const vmax = 3000; const v = Math.max(vmin, Math.min(Attr.val, vmax)); const bat = Math.round(((v - vmin) / (vmax - vmin)) * 100); if (bat>0){Item.val = Math.max(0, Math.min(bat, 100));}"
          }
        },

And what would this “magic” part needs to look like to efficiently filter the unwanted 0 (zero) values?

Like it is.

const vmin = 2700;
const vmax = 3000;
const v = Math.max(vmin, Math.min(Attr.val, vmax));
const bat = Math.round(((v - vmin) / (vmax - vmin)) * 100);
if (bat > 0) {
    Item.val = Math.max(0, Math.min(bat, 100));
}

The value is updated only if bat > 0

Well… as you can see here in the original DDF…

…there is not such a part in this DDF. What I can see which looks interesting is this part:

        {
          "name": "config/battery",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x01",
            "script": "xiaomi_battery.js"
          }

So this seems to be “outsourced” to the xiaomi_battery.js script:

As I don’t want to change the xiaomi_battery.js - as it is likely used by other devices where I don’t want/need/must implement this workaround, but only for the gzcgq01lm light sensors:

How to properly source that part back in to the xiaomi_gzcgq01lm_light_sensor.json? Help appreciated @Smanar.

Like here It's time to fix Aqara battery bug - #54 by Smanar

Managed to implement it.

Unfortunately, instantly after the new DDF started working, Home Assistant reported that few entities (illuminance and battery) of two sensors became unavailable. I checked and compared them.

These (firmware *21) are working just fine - no change (so far):

But these became unavailable (firmware *27):

Update:
1 out of 2 devices (firmware *27) still with unavailable entities, the other one recovered within minutes.

What the…?

  1. Any idea?
  2. Maybe those devices that would have a battery value of 0 (zero) now will be shown as unavailable due to the changed DDF? Or shall they keep the last value they had?

Reading of 0001 Power Configuration cluster:

And this is the deconz API output:

  "107": {
    "config": {
      "battery": 100,
      "on": true,
      "reachable": false,
      "tholddark": 12000,
      "tholdoffset": 7000
    },
    "ep": 1,
    "etag": "***REMOVED***",
    "lastannounced": null,
    "lastseen": "2024-02-04T19:30Z",
    "manufacturername": "XIAOMI",
    "modelid": "lumi.sen_ill.mgl01",
    "name": "Lichtsensor ***REMOVED***",
    "state": {
      "dark": false,
      "daylight": false,
      "lastupdated": "2023-12-30T19:15:16.375",
      "lightlevel": 0,
      "lux": 0
    },
    "swversion": "0.0.0_0027",
    "type": "ZHALightLevel",
    "uniqueid": "***REMOVED***"
  },

That "reachable": false, looks suspicious to me…


Update:

  • No it’s not suspicious, same for all light sensors not currently triggered.
  • After I triggered a value change for the remaining light sensor, its entities came back online.

Theory: after changing the DDF, the *27 firmware powered light sensors need some kind of trigger/interaction to push their new values. I think the DDF change made them “reset” somehow. Not into that inner processes of deCONZ, so just assumptions…

:warning::warning::warning: Latest remaining issue: all values don’t update anymore… meaning that illuminance is stuck on the value prior to the DDF swapping… :warning::warning::warning:

Why does that stuff need to be that complicated… :imp:

@Smanar I tested with several sensors: value "lux": 18 does not update anymore according to the API. What can I do to resolve this?

My DDF looks like this (only the battery part changed, put in in the `/data/.local/share/dresden-elektronik/deCONZ/devices/` folder and hit "hot reload" in the deCONZ DDF editor):
{
  "schema": "devcap1.schema.json",
  "doc:path": "xiaomi/xiaomi_gzcgq01lm_light_sensor.md",
  "doc:hdr": "Light sensor GZCGQ01LM",
  "manufacturername": ["$MF_XIAOMI", "$MF_LUMI"],
  "modelid": ["lumi.sen_ill.mgl01", "lumi.sen_ill.mgl01"],
  "vendor": "Xiaomi",
  "product": "Mijia Light Sensor GZCGQ01LM",
  "status": "Gold",
  "md:known_issues": [ ],
  "sleeper": true,
  "subdevices": [
    {
      "type": "$TYPE_LIGHT_LEVEL_SENSOR",
      "restapi": "/sensors",
      "uuid": [
        "$address.ext",
        "0x01",
        "0x0400"
      ],
      "fingerprint": {
        "profile": "0x0104",
        "device": "0x0106",
        "endpoint": "0x01",
        "in": [
          "0x0001",
          "0x0400"
        ]
      },
      "items": [
        {
          "name": "attr/lastseen"
        },
        {
          "name": "attr/manufacturername"
        },
        {
          "name": "attr/modelid",
          "awake": true
        },
        {
          "name": "attr/name"
        },
        {
          "name": "attr/swversion",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x08",
            "script": "xiaomi_swversion.js"
          },
          "read": {
            "fn": "none"
          }
        },
        {
          "name": "attr/type"
        },
        {
          "name": "attr/uniqueid"
        },
        {
          "name": "config/on"
        },
        {
          "name": "config/battery",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x01",
            "eval": "const vmin = 2700; const vmax = 3000; const v = Math.max(vmin, Math.min(Attr.val, vmax)); const bat = Math.round(((v - vmin) / (vmax - vmin)) * 100); if (bat>0){Item.val = Math.max(0, Math.min(bat, 100));}"
          }
        },
        {
          "name": "config/reachable"
        },
        {
          "name": "config/tholddark"
        },
        {
          "name": "config/tholdoffset"
        },
        {
          "name": "state/lightlevel",
          "parse": {
            "at": "0x0000",
            "cl": "0x0400",
            "ep": 1,
            "script": "../generic/illuminance_cluster/sml_light_level.js"
          }
        },
        {
          "name": "state/dark"
        },
        {
          "name": "state/daylight"
        },
        {
          "name": "state/lux"
        },
        {
          "name": "state/lastupdated"
        }
      ],
      "example": {
        "config": {
          "battery": 100,
          "on": true,
          "reachable": true,
          "tholddark": 12000,
          "tholdoffset": 7000
        },
        "ep": 1,
        "etag": "761fff5a33ee73a8b9e4ec2e8ee5dac2",
        "lastseen": "2021-01-13T12:07Z",
        "manufacturername": "XIAOMI",
        "modelid": "lumi.sen_ill.mgl01",
        "name": "Light Sensor",
        "state": {
          "dark": false,
          "daylight": false,
          "lastupdated": "2021-01-13T12:07:27.511",
          "lightlevel": 13425,
          "lux": 22
        },
        "swversion": "0.0.0_0027",
        "type": "ZHALightLevel",
        "uniqueid": "04:cf:8c:df:3c:7f:1f:80-01-0400"
      }
    }
  ],
  "bindings": [
    {
      "bind": "unicast",
      "src.ep": 1,
      "cl": "0x0400",
      "report": [
        {
          "at": "0x0000",
          "dt": "0x21",
          "min": 10,
          "max": 3600,
          "change": "0x0064"
        }
      ]
    }
  ]
}

I think it might be because of the changed location…

  • HA deCONZ addon default DDF storage: /usr/share/deCONZ/devices/xiaomi/
  • HA deCONZ addon custom user DDF storage: /data/.local/share/dresden-elektronik/deCONZ/devices/

And I think the path to the light level script…

        {
          "name": "state/lightlevel",
          "parse": {
            "at": "0x0000",
            "cl": "0x0400",
            "ep": 1,
            "script": "../generic/illuminance_cluster/sml_light_level.js"
          }

as well as the swversion script…

        {
          "name": "attr/swversion",
          "awake": true,
          "parse": {
            "at": "0x00f7",
            "ep": 1,
            "fn": "xiaomi:special",
            "idx": "0x08",
            "script": "xiaomi_swversion.js"
          },

can not be found anymore now.

So:
a) source them in, too?
b) change paths? relative would be difficult, not sure if absolute ones would work?


Another update: fixed now. I copied all the (only two) referenced *.js files also to the persistent custom DDF storage (/data/.local/share/dresden-elektronik/deCONZ/devices/ for HA deCONZ addon/container). Some inconsistent fiddling around with “Edit DDF”, edit another one, open the custom one again etc. together with several “Hot reload” magic in between… finally made it work.

Values report back via API and are shipped to Home Assistant. :white_check_mark:
That’s when just a small workaround either makes your smart home stop working or takes two hours of your time. Always fun to work with deCONZ, DDFs and stuff.

Note: absolute paths did NOT work (even the addon should be able to find the files… really strange). Maybe the DDFs only work with relative paths (as used in the default)? Would be really interesting to know!

Ha ? good to know, I never tried absolute path on my side.