Fluval Aquasky BLE RGB-Light

Hi Folks,
I’d like to integrate a Fluval Aquasky lamp into my home automation. It is BLE controlable via an app. I couldn’t find nearly any support for BLE-lights here. I did a little research and sniffing. It seems the device uses a controller from www.tuner168.com with an CC2541 TC Module.
I dont’t know how BLE works, however, I found some exposed services. I tried to find a difference between the lamp on and off, but I couldn’t find a difference. I tried to write some values-also no effect. The good thing is, I didn’t brick it…
So has anyone done this kind of reverse engeneering yet and point me to how to go forward…

19:40:07.1000: connected in underlying BLE layer.
19:40:07.1030: discovered services: (
    1000,
    "Device Information"
)
19:40:07.3210: discovered characteristics for service 180A: (
    "System ID",
    "Model Number String",
    "Serial Number String",
    "Firmware Revision String",
    "Hardware Revision String",
    "Software Revision String",
    "Manufacturer Name String",
    "IEEE Regulatory Certification",
    "PnP ID"
)
19:40:07.4710: discovered characteristics for service 1000: (
    1001,
    1002,
    1003,
    1004,
    1005
)
19:40:07.4720: state changed to 'Connected'.
19:40:07.7560: received update from characteristic 2A25___180A: <32303137 2d352d31 32>
19:40:08.0560: received update from characteristic 2A23___180A: <00000000 00000000>
19:40:08.0850: received update from characteristic 2A27___180A: <48617264 77617265 20526576 6973696f 6e>
19:40:08.1310: received update from characteristic 2A28___180A: <56322e37>
19:40:08.1600: received update from characteristic 2A29___180A: <7777772e 74756e65 72313638 2e636f6d>
19:40:08.1900: received update from characteristic 2A24___180A: <43433235 34312054 43204d6f 64756c65>
19:40:10.5450: received update from characteristic 2A25___180A: <32303137 2d352d31 32>
19:40:10.5460: received update from characteristic 2A29___180A: <7777772e 74756e65 72313638 2e636f6d>
19:40:10.5470: received update from characteristic 2A25___180A: <32303137 2d352d31 32>
19:40:10.5470: received update from characteristic 2A29___180A: <7777772e 74756e65 72313638 2e636f6d>
19:40:10.5750: received update from characteristic 2A23___180A: <00000000 00000000>
19:40:10.6060: received update from characteristic 2A24___180A: <43433235 34312054 43204d6f 64756c65>
19:40:10.6510: received update from characteristic 2A26___180A: <56322e37>
19:40:10.6810: received update from characteristic 2A27___180A: <48617264 77617265 20526576 6973696f 6e>
19:40:10.7100: received update from characteristic 2A28___180A: <56322e37>
19:40:10.7410: received update from characteristic 2A2A___180A: <fe006578 70657269 6d656e74 616c>
19:40:10.7860: received update from characteristic 2A50___180A: <010d0000 001001>
19:40:10.8310: received update from characteristic 2A23___180A: <00000000 00000000>
19:40:10.8600: received update from characteristic 2A24___180A: <43433235 34312054 43204d6f 64756c65>
19:40:10.8910: received update from characteristic 2A26___180A: <56322e37>
19:40:10.9210: received update from characteristic 2A27___180A: <48617264 77617265 20526576 6973696f 6e>
19:40:10.9510: received update from characteristic 2A28___180A: <56322e37>
19:40:10.9810: received update from characteristic 2A2A___180A: <fe006578 70657269 6d656e74 616c>
19:40:11.0110: received update from characteristic 2A50___180A: <010d0000 001001>
19:41:26.4470: received update from characteristic 1001___1000: <00000000 00000000 00000000 00000000 00000000>
19:41:26.4770: received update from characteristic 1002___1000: <>
19:41:26.5220: received update from characteristic 1004___1000: <30313432 30313033 00000000 00000000 00000000>
19:41:26.5670: received update from characteristic 1005___1000: <47>
19:41:26.5970: received update from characteristic 1001___1000: <00000000 00000000 00000000 00000000 00000000>
19:41:26.6420: received update from characteristic 1002___1000: <>
19:41:26.6870: received update from characteristic 1004___1000: <30313432 30313033 00000000 00000000 00000000>
19:41:26.7170: received update from characteristic 1005___1000: <47>

2 Likes

Did you manage to get anywhere with this. Would like to be able to control my BLT Fluval light with Home Assistant

No, not yet. However, I did a little research. There are BT-USB Dongles available. With a modded Firmware they are able to work with Wireshark. I am not shure if this leads somewhere…
I didn’t order one yet.

This looks also interesting: https://www.bluetooth.com/blog/a-new-way-to-debug-iosbluetooth-applications/

Any progress on this?

+1 This would be great to be able to use for RGBW control. Has anyone looked into the WiFi controller FluvalSmart has? Just discovered it, but not clear yet as to whether it will
Integrate with AquaSky / Plant 3.0 etc.

Did anyone get anywhere with this?

I’ve been screwing around with sniffing commands sent from my phone.

All I know at the minute is sending to 0x1000 > 0x1001

values of
545159650f0d67 will turn the unit off
5451f2cea4a7cd will turn the unit on

The values change a lot though, think it’s maybe sending last colour info and stuff to the light from the app

…sooo I just thought I’d see if anyone persevered with it and made sense of/decoded it, it’s been over a year after all!

Friend did just find this though, so I’ll have a bit of a play…

2 Likes

+1, although I don’t really care about controlling it. I just want to know if the light is on or not.

+1 Would be great!

Did you ever make any progress here? Seems like the recent advancements with Bluetooth in HA should make things a little easier. It would be great to have a little ESP32 running as a Bluetooth proxy and be able to control things from HA.

The “automation” from Fluval really is not very smart. Something like a simple power failure screws things up.

The github repo is actually the fluval app - great find! I will have a look at it, as I am about to install some temperature sensors based on ESP32 in my tanks in the near future. They should serve the temperature over a REST api and switch a relay- but switching my aquasky as well sounds nice.

What I found out so far:

  • Yes, indeed the AquaSky 2.0 is using a controller made by tuner168.com
  • The github repo contains an older version of the AquaSky App
  • This app contains a ble-managing library called AndroidBleApi_V1.1.6
  • This AndroidBleApi is using a native compiled c/c++ library called libhy_api.so

The good stuff:

  • Using and decompiling this libraries, the supplied source code etc. it should be possible to recreate the communication between AquaSky App and the lights

The bad stuff:

  • libhy_api.so is used with encryption turned on. Decompiling this is a bit more challenging. This library exports two functions: byte[] encodeMessage(byte[]) and byte[] decodeMessage(byte[]), and they seem to be using AES-128 for encryption. At least they are shuffling a few 128 bit registers around and then seem to go into encryption rounds.
  • The key/iv is not supplied to the libhy_api.so, so it is most likely within the libhy.api. “Hacking” .so files for key extraction is unfortunately not my favorite thing to do, but I will try.

So if someone here is better at disecting .so files, feel free to join. If we have those keys and maybe can validate that is in fact AES-128 used here (should be, the mcu of this CC2541 modules has a built in AES128 block), the rest should be quite simple :wink:

Best regards
mrzottel

Update: I managed to get the .so file included in a small test app and execute the encode/decode on android, checking the input/output.

No AES at all, simple replacement, for example 0x00 becomes 0x67 during encoding, and 0x67 becomes 0x00 again during decoding. Also the encoded message has 3 bytes attached to the front of the byte array, but they seem to be static: 0x54, 0x55, 0x33.
Byte encoding is not linear though, but anyway simple enough to implement on ESP32.

Is there still someone reading here anyway? :wink:

Update 1:
<<<
3rd byte of the encoded output is not static, it is a random byte (but always starting with 0x33 for the first encoded message) that is used by the decoder. It is simply added to the decoded byte.
It’s a xored with 84 decimal random number used to xor the packet bytes.

So a message like 0x54, 0x55, 0x33, 0x00 <-(here starts the content)

Byte 0x00 in becomes for example 0x34 (have to find out how) and then 0x33 is added → 0x67
<<<

Update2:
<<<
I can’t reply more than 3 times to one topic it seems, so I use this reply as “update log” :wink:
@Rahaaas:
Don’t worry, I will test with my own lamps first - so I don’t feel bad if I brick one :wink:
Once I am a bit confident, I will create a .net/c# application (my main language) to test more and put it into github for others to test. From there it should be simple to implement in other devices like ESP32.

I just asked if there is still interest in the topic, otherwise I would have stopped logging my progress here :wink:
<<<

Update3:
<<<
Now that is a simple encoder. No idea why I even thought that would be AES:

static byte[] Encode(byte[] data)
        {
            // 84, 85 seems to be a random header / marker for encrypted packets
            var result = new List<byte>
            {
                84,
                (1 ^ 84)
            };

            // random number. Fixed to 103 here to reproduce tests
            var rand = 103;

            // add the random number xor 84. The 84 is hardcoded in the libhy_api.so
            result.Add((byte)(rand ^ 84));

            // go through our encryption data and xor it with our random number
            foreach (var singleByte in data)
            {
                result.Add((byte)(singleByte ^ rand));
            }

            return result.ToArray();
        }

Decode:

 static byte[] Decode(byte[] data)
        {
            var rand = (byte)(data[2] ^ 84);

            var result = new List<byte>();
            
            foreach (var singleByte in data[3..])
            {
                result.Add((byte)(singleByte ^ rand));
            }

            return result.ToArray();
        }

Simple xor with a random number, the random number xored with 84 (decimal) is attached as 3rd byte to the header.

Todo: Find out how the random number permutates from 103 to 198 etc…

Best regards
mrzottel

2 Likes

Yes! I’m afraid I can’t contribute technically, but definitely keen to integrate the lights into HA.

Currently have an ESP32 helping out on various aquarium things… haven’t worked out how to enable it as a Bluetooth controller as yet.

Happy to test stuff, if it helps (have a couple of Aquaskys, and a Plant 3.0)

Ok, I made some progress:

  1. I started sniffing bluetooth packages and debug-logging the fluval app. Result is, that the new “starting vector” for encoding is 14 (rand), and the header changed to {0x54, 0x51}. This most likely is due to the “exposed” app on github beeing very old. I guess they changed it at some firmware/app update.

  2. I replaced openhab with home assistant so I know what you guys are talking about :wink:

  3. I started playing around with ESPHome (Wow, that thing is AWESOME!)

  4. I managed to turn on / off my LED (AquaSky 2.0) with this:

esp32_ble_tracker:

ble_client:
  # LED mac address
  - mac_address: 44:A6:XX:XX:XX:XX 
    id: my_ble_client

switch:
  - platform: template
    name: "Aquasky"
    turn_on_action:
      - ble_client.ble_write:
          id: my_ble_client
          service_uuid: "1000"
          characteristic_uuid: "1001"
          value: [0x54, 0x51, 0x5A, 0x66, 0x0D, 0x0F, 0x64]
          
    turn_off_action:
      - ble_client.ble_write:
          id: my_ble_client          
          service_uuid: "1000"
          characteristic_uuid: "1001"                    
          value: [0x54, 0x51, 0x5A, 0x66, 0x0D, 0x0E, 0x65]

Just as a test now. My yaml-ha-esphome-foo is quite low, since I am only using it for 2 days now, and the switch “falls back” to off, even when switching on. I guess there is a callback or something missing.

Anyway, I am somewhat happy with the progress so far. Especially since both “values” in the example above use the same “rand”-number and still work on the LED - so the whole “permutation of the random number” seems to be obsolete. This “lowers” the hardeness of the encryption somewhat, but it was quite soft to begin with. I will call it obfuscation from now on :wink:

  1. I guess I have to figure out how to create an ESPHome-component based on the ble_client now, as the LED seems to be too complex to map everything to switches. At least for me.

best regards,
mrzottel

1 Like

This is awesome! also registering my interest in this project - although my assistance is limited to an offer of helping to test on my units - im not smart enough to help with the development

So, I had a fun weekend coding - until my cat and my wife teamed up and reported me missing =D

Status update:

  1. Got a stable connection with a component which is a node of ble client. This allows for reading and writing to the LED.

  2. Writing was already working, but reading took a lot of effort. “Encryption” is a bit more complex than I thought. The xor-key is byte1 xor byte3 from the 3 byte header. The rest is then xored with that key. Well, anyway, reading works now.

  3. I started decoding the packets sent by the LED. Fortunately, quite easy with the leaked app on github :wink:

  4. Currently working commands by “sending bytes through buttons/switches”:

  • Switch between manual, auto and pro mode
  • Turn LED on/off in manual mode
  1. Currently working readouts (i.e. sensors provided by the component):
  • manual mode (0, 1)
  • auto mode (0, 1)
  • pro mode (0, 1)
  • On/Off in manual mode (0, 1)

Where 0 means off, 1 means on obviously :wink:

  1. Still to do / planned:
  • Figure out how to read the device status during startup. Currently it requires one command sent to the LED to get a status back. There must be a better option.
  • Implement at least the channels (4 or 5 of them) to set the LED color (read and write)
  • Find a better way to send commands than writing byte arrays in yaml - looking at automation.py

Btw: List of supported devices so far, at least I think they are supported:

  • 4 channels:
    – Auqasky
    – Roma
  • 5 channels:
    – Marine
    – Fresh
    – Nano Marine
    – Nano Fresh
    – Vicenza
    – Venezia
    – A-Sky Aqua
    – Plant Aqua

The general idea (please discuss if you have better ideas) is to provide the channels as automation targets instead of trying to replicate the app in home assistant. So largely ignoring auto/pro mode settings, as they are very complex to handle inside an ESP32 automation thingy. Switch to manual and let home assistant / ESPHome do the color stuff. Maybe fallback to a preset auto/pro profile, if ha is down. Also one downside of this whole thing is: While ESP32 is connected to the LED, no more connections (App for example) are possible. Maybe I can fix this with disconnecting between commands - but that’s for a much later time :wink:

Also I hope to have a version that is stable enough to test and play around with until the end of the month. A small preview:

And yes, the ESP is also monitoring the temperature of the tank. Also please excuse the crudity of the output - I’m just starting with home assistant and have not bothered to change the overview to anything than “just dump everything” :wink:

best regards,
mrzottel

3 Likes

Nice work. Recommend making a true homeassistant integration, that will allow use of internal BT adapters or ESPHome proxies. More reliable than running directly on ESPHome. There is already a BLE light component: LED BLE - Home Assistant so you can probably edit that or use it as a guide for your own. They can help on discord too if have any questions. bdraco might even be able to take when you already figured out and just add it in (the advertisement data local_name and the write/read commands)

Good idea, but why not do both? :wink:

For my home use case I need this to run on an ESP, or multiple in fact, due to 7 tanks all over the home, most of them out of reach of a bluetooth-dongle attached to my raspi. So increasing the range by ESP is perfect for me, especially since every tank already has one for temperature monitoring and switching of stuff like heating/cooling.

But once I really got behind this stuff and finish the ESPHome component, I will reach out to bdraco and see what he needs to implement it into a native ha integration.

Also I will have a look at that proxy you mentioned. Sounds interesting =)

best regards,
mrzottel