Skip to content

Instantly share code, notes, and snippets.

@samthor
Last active September 23, 2021 03:59
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samthor/42f41372dfeb942b0e419e238d72f752 to your computer and use it in GitHub Desktop.
Save samthor/42f41372dfeb942b0e419e238d72f752 to your computer and use it in GitHub Desktop.
Read/write Clipsal smart switches

This is notes for anyone who is interacting with (in Australia at least) Clipsal-branded smart switches that operate over BLE. (They also come with a ZigBee certification document, but it seems like it was never enabled.)

They have a couple of interesting UUIDs (these are here for y'all searching for them):

  • Control service, 720a9080-9c7d-11e5-a7e3-0002a5d5c51b

    • State characteristic (i.e., switch on/off), 720a9081-9c7d-11e5-a7e3-0002a5d5c51b
    • Level characteristic (i.e., brightness), 720a9082-9c7d-11e5-a7e3-0002a5d5c51b
  • Command service, 720a7080-9c7d-11e5-a7e3-0002a5d5c51b

    • Request characteristic, 720a7081-9c7d-11e5-a7e3-0002a5d5c51b
    • Response characteristic, 720a7082-9c7d-11e5-a7e3-0002a5d5c51b

Set switch on/off and brightness the naïve way

For clarification, I have some CLP5010's which have on/off as well as brightness. I also have CLP5011's which I've yet to install (but are just switches, and do not have a level)—these seem more suitable for controlled loads or lights that cannot dim.

So, you can write 0x00 or 0x01 to the state characteristic to turn a switch on or off. You can also write to the level characteristic (as a 16-bit little-endian number, for some reason) which ranges from 0-10,000. (So the highest value will be 0x2710, written as "0x10 0x27" in LE).

The brightness isn't independent from the power state. If the light is off, brightness will be zero. For example: if you set the brightness to 600 on an off switch, the light will come on, and the opposite if you set brightness to zero. (Toggling on/off will restore the most recent brightness, or there's an option in their app to have it restore at a specific value always.)

Regardless, it seems as if these characteristics ONLY report the last state you wrote over BLE.

  • You cannot read the latest value if a person physically toggled the switch.
  • You can see the latest value if you're subscribed to the characteristic, but this requires holding a BLE connection open (not really ideal when you have lots of switches).

This is… strangely broken for real use if you're trying to include these switches in a home automation system.

BLE scanner way

You can read the current state of the light by having a BLE scanner run. The advertising data packet contains a "manufacturer specific" (i.e., 0xff) payload that announces on/off and level. Yes, this means sufficiently motivated neighbours could read the status of your lights—it's public. But they could probably tell if your windows are lit up anyway.

The packet has a bunch of stuff in it, but importantly:

  • data[5] contains the current settings generation (it updates whenever a setting changes)
  • data[6] reports on/off state (the Clipsal app masks it with & 15 but no other data ever appears here)
  • data[7] has the brightness from 0-255 (it's really internally 0-10,000), you can / 255.0 * 10000 to bring it in-range

I use these packets to update my code which eventually informs e.g., Google Assistant as to whether the lights are on.

(I find it really amusing that the Clipsal app does this discovery, but still makes you tap on a specific light to see whether it's currently on. It already knows and could show the statuses all at once!)

Request/Response way

There's also a way to make arbitrary requests and wait for responses. This is how the app gets state and writes settings.

There's two types of requests: read and write. The app refers to using these as reading and writing "registers". The read command looks like:

Byte Value Notes
0 0xff
1 0xff
2 0x06 Magic value
3 0xff
4 0x43 Magic value
5-6 register The register to read, in 2-byte big-endian
6-7 length / 2 The number of 2-byte values we expect in return (length is total bytes)

The write is pretty similar, but contains more data:

Byte Value Notes
0 0xff
1 0xff
2 length + 7 The number of bytes to write, plus seven
3 0xff
4 0x10 Magic value
5-6 register The register to write, in 2-byte big-endian
6-7 length / 2 The number of 2-byte values we're reading (length is total bytes)
8 length The total number of bytes being written
9+ your payload The payload to write

Length must always be even and seems to be minimum eight. If the payload you're writing is fewer bytes (many registers only take 2-byte BE values) then pad it on the right with zeros.

Once you've done a read or write request (well, you should do this before that) watch for notifications on the response characteristic. It has a bunch of values as a prefix, but the result tends to start at byte 8.

Registers

You can e.g., read the light's name by reading register 0x1002. If you happen to have access to the app's source code, you can find the descriptions of additional registers in a file called "ParametersMapCommon.xml".

Done

Write a comment if you're interested in more or just want to say thanks.

@phindmarsh
Copy link

Ok so after some more tinkering I have them connected to Home Assistant using the ZHA integration (using an electro lama zzh! coordinator). I think the reason it wasn't working earlier was the coordinator I was using wasn't Zigbee 3.0.

I still haven't managed to get it back to "normal Bluetooth mode" even after a factory reset it seems to be in Zigbee mode still. But that's not really a problem because it's connected to the zigbee network now anyway. I'll play some more to work out if I can reset it, because it would still be handy to do OTA updates of the firmware etc via the Wiser app.

Thanks for the tip @ell249, seems like my main issue was an old Zigbee coordinator firmware!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment