Skip to content

Latest commit

 

History

History
1352 lines (1159 loc) · 70.7 KB

scratchnotes.md

File metadata and controls

1352 lines (1159 loc) · 70.7 KB

Scratch notes

Basic info & connection params stuff

  • Device name advertised as: LEDneWF02001D0CDC48
  • Wireshark says it's a JMZengge
  • UUID: FF00
    • 0xFF22 - read/write without response notify
    • 0xFF11 - write without response
  • UUID: FFFF
    • 0xFF02 - Read notify
    • 0xFF01 - write

Based on previous escapades in BLE I assume you have to enable notifications by writing to one of the notification endpoints on FFFF.

Not sure if this is relevant: MTU: 200 bytes Command length: 8 Min interval: 16 (20 msec) Max interval: 24 (30 msec) Timeout multiplier: 600 (6 sec)

$ gatttool -I
[                 ][LE]> connect 08:65:F0:0C:DC:48
Attempting to connect to 08:65:F0:0C:DC:48
Connection successful
[08:65:F0:0C:DC:48][LE]> primary 
attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0011 uuid: 0000fe00-0000-1000-8000-00805f9b34fb
attr handle: 0x0012, end grp handle: 0x0017 uuid: 0000ffff-0000-1000-8000-00805f9b34fb
[08:65:F0:0C:DC:48][LE]> characteristics
handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x02, char value handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0009, char properties: 0x20, char value handle: 0x000a, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x16, char value handle: 0x000e, uuid: 0000ff22-0000-1000-8000-00805f9b34fb
handle: 0x0010, char properties: 0x04, char value handle: 0x0011, uuid: 0000ff11-0000-1000-8000-00805f9b34fb
handle: 0x0013, char properties: 0x12, char value handle: 0x0014, uuid: 0000ff02-0000-1000-8000-00805f9b34fb
handle: 0x0016, char properties: 0x0c, char value handle: 0x0017, uuid: 0000ff01-0000-1000-8000-00805f9b34fb

Initial conversation

Probably enabling a notification

We sent:

0000   02 02 00 09 00 05 00 04 00 12 15 00 01 00         ..............
  • Write Request 0x12
  • Handle: 0x0015
    • Service UUID 0xffff
    • Characteristic: 0xff02
    • UUID: 0x2902

We received:

0000   02 02 20 05 00 01 00 04 00 13                     .. .......
  • Write response: 0x13
  • Handle: 0x0015
    • Service: 0xffff
    • Char: 0xff02
    • UUID: 0x2902

Characteristic Configuration Client: 0x0001, Notification

Next write request (perhaps some kind of set up)

We sent:

0000   02 02 00 13 00 0f 00 04 00 12 17 00 00 01 80 00   ................
0010   00 04 05 0a 81 8a 8b 96                           ........
  • Write request 0x12
  • Handle: 0x0017
    • Service: 0xffff
    • UUID: 0xff01

Value is hex: 00 01 80 00 00 04 05 0a 81 8a 8b 96

which results in us being sent this over multiple frames:

0000   3e 00 04 00 1b 14 00 04 43 80 00 00 33 34 0a 7b   >.......C...34.{
0010   22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c 6f 61   "code":0,"payloa
0020   64 22 3a 22 38 31 31 44 32 33 32 35 30 45 30 30   d":"811D23250E00
0030   33 32 33 32 46 46 30 30 30 32 30 30 33 30 38 39   3232FF0002003089
0040   22 7d                                             "}

>C34 {"code":0,"payload":"811D23250E003232FF0002003089"}

04 43 80 00 00 33 34 0a 7b 22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c 6f 61 64 22 3a 22 38 31 31 44 32 33 32 35 30 45 30 30 33 32 33 32 46 46 30 30 30 32 30 30 33 30 38 39 22 7d C 3 4 sp { " c o d e " : 0 , " p a y l o a d " : " 8 1 1 D 2 3 2 5 0 E 0 0 3 2 3 2 F F 0 0 0 2 0 0 3 0 8 9 " }

I would guess that this is an 8 byte header?

We then send:

0000   02 02 00 1b 00 17 00 04 00 12 17 00 00 02 80 00   ................
0010   00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4   .......... .....
  • Write request 0x12
    • Handle: 0x0017
    • Service: 0xffff
    • UUID: 0xff01

Value in hex: 00 02 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4

We received a write response.

Then we send (to the same service and UUID): Value in hex: 00 03 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4

We received a write response and then

In summary, these are the three payloads we sent:

  • 00 01 80 00 00 04 05 0a 81 8a 8b 96
  • 00 02 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4
  • 00 03 80 00 00 0c 0d 0b 10 14 16 0a 1d 0e 20 00 06 00 0f a4

Toggling the light on and off from the app

I think the first "on" request looks like this:

  • Service: 0xffff
  • UUID: 0xff01 Payload: 02 02 00 1c 00 18 00 04 00 12 17 00 00 04 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91

which results in receiving this: 3e 00 04 00 1b 14 00 04 44 80 00 00 33 34 0c 7b22636f6465223a302c227061796c6f6164223a2238313144323432353045303033323332464630303032303033303841227d >D34 {"code":0,"payload":"811D24250E003232FF000200308A"}

The I think the first "off" request looks like this:

  • Service: 0xffff
  • UUID: 0xff01 Payload: 02 02 00 1c 00 18 00 04 00 12 17 00 00 05 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90 and we receive: 3e 00 04 00 1b 14 00 04 45 80 00 00 33 34 0c 7b22636f6465223a302c227061796c6f6164223a2238313144323332353045303033323332464630303032303033303839227d >E34 {"code":0,"payload":"811D23250E003232FF0002003089"}

If I look at just on the on/off messages I sent they look like this (including packet headers 02 02 00 1c 00 18 etc):

02 02 00 1c 00 18 00 04 00 12 17 00 00 04 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 05 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 06 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 07 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 08 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 09 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0a 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 0b 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0c 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91
02 02 00 1c 00 18 00 04 00 12 17 00 00 0d 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90
02 02 00 1c 00 18 00 04 00 12 17 00 00 0e 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91

Changing some colours. RGB Only

Skipping all the preamble, let's look at the values of the write messages first. In a big list to see if we can spot the patterns. Starting from opening the app.

0028000000c0d0b1014160a1d12032806000fb3
0038000000c0d0b1014160a1d12032806000fb3

0004 8000000d0e0b3b23 000000 00000000 32 00 00 90
0005 8000000d0e0b3b24 000000 00000000 32 00 00 91
0006 8000000d0e0b3b23 000000 00000000 32 00 00 90
0007 8000000d0e0b3b24 000000 00000000 32 00 00 91
0008 8000000d0e0b3b23 000000 00000000 32 00 00 90
0009 8000000d0e0b3b24 000000 00000000 32 00 00 91
000a 8000000d0e0b3b23 000000 00000000 32 00 00 90
000b 8000000d0e0b3b24 000000 00000000 32 00 00 91
000c 8000000d0e0b3b23 000000 00000000 32 00 00 90
000d 8000000d0e0b3b24 000000 00000000 32 00 00 91
000e 8000000d0e0b3b23 000000 00000000 32 00 00 90

000f 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0010 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0011 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0012 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0013 8000000d0e0b3ba1 3c6464 00000000 00 00 00 e0
0014 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0015 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0016 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0017 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0018 8000000d0e0b3ba1 786464 00000000 00 00 00 1c
0019 8000000d0e0b3ba1 006464 00000000 00 00 00 a4
001a 8000000d0e0b3ba1 006464 00000000 00 00 00 a4
001b 8000000d0e0b3ba1 006464 00000000 00 00 00 a4

001c 8000000d0e0b3b24 000000 00000000 32 00 00 91

I ignore the first two packets, I expect they are some handshaking. Everything breaks up reasonably neatly as I have done with the spaces.

  • First two bytes are a counter
  • The next eight bytes seem to stay the same
  • The next three bytes differ at the top (and the last packet which is an off I think) and the middle section which is where I was changing the colours. 6 bytes for colours would be RGB, but the numbers don't make sense)
  • The last byte really looks like a checksum. It's always the same for each of the commands, so isn't effected by the counter.

More colour changing

Trace with times On 18:57

Full color 18:57 Press on Press red 18:58 5 times Dragged to red slid around colours fiddle with saturation brightness down, all down, up off

	18:58:04   	0005 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:06   	0006 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:08   	0007 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:24   	0008 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:24   	0009 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:25   	000a 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:25   	000b 8000000d0e0b3ba1 006464 00000000000000a4	
	18:58:46   	000c 8000000d0e0b3ba1 40e464 0000000000000064	
	18:58:46   	000d 8000000d0e0b3ba1 40e464 0000000000000064	
	18:58:58   	000e 8000000d0e0b3ba1 43e464 0000000000000067	
	18:58:58   	000f 8000000d0e0b3ba1 43e464 0000000000000067	
	18:59:01   	0010 8000000d0e0b3ba1 52e464 0000000000000076	
	18:59:01   	0011 8000000d0e0b3ba1 52e464 0000000000000076	
	18:59:03   	0012 8000000d0e0b3ba1 5fe464 0000000000000083	
	18:59:04   	0013 8000000d0e0b3ba1 5fe464 0000000000000083	
	18:59:05   	0014 8000000d0e0b3ba1 6be464 000000000000008f	
	18:59:05   	0015 8000000d0e0b3ba1 6be464 000000000000008f	
	18:59:06   	0016 8000000d0e0b3ba1 706464 0000000000000014	
	18:59:06   	0017 8000000d0e0b3ba1 706464 0000000000000014	
	18:59:08   	0018 8000000d0e0b3ba1 766464 000000000000001a	
	18:59:08   	0019 8000000d0e0b3ba1 766464 000000000000001a	
	18:59:10   	001a 8000000d0e0b3ba1 7ee464 00000000000000a2	
	18:59:10   	001b 8000000d0e0b3ba1 7ee464 00000000000000a2	
	18:59:11   	001c 8000000d0e0b3ba1 856464 0000000000000029	
	18:59:11   	001d 8000000d0e0b3ba1 856464 0000000000000029	
	18:59:14   	001e 8000000d0e0b3ba1 8b6464 000000000000002f	
	18:59:14   	001f 8000000d0e0b3ba1 8b6464 000000000000002f	
	18:59:14   	0020 8000000d0e0b3ba1 91e464 00000000000000b5	
	18:59:14   	0021 8000000d0e0b3ba1 91e464 00000000000000b5	
	18:59:15   	0022 8000000d0e0b3ba1 9ae464 00000000000000be	
	18:59:15   	0023 8000000d0e0b3ba1 9ae464 00000000000000be	
	18:59:15   	0024 8000000d0e0b3ba1 9fe464 00000000000000c3	
	18:59:16   	0025 8000000d0e0b3ba1 9fe464 00000000000000c3	
	18:59:16   	0026 8000000d0e0b3ba1 aae464 00000000000000ce	
	18:59:16   	0027 8000000d0e0b3ba1 aae464 00000000000000ce	
	18:59:17   	0028 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:17   	0029 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:17   	002a 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:18   	002b 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:19   	002c 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:19   	002d 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:25   	002e 8000000d0e0b3ba1 005264 0000000000000092	
	18:59:25   	002f 8000000d0e0b3ba1 005264 0000000000000092	
	18:59:26   	0030 8000000d0e0b3ba1 003c64 000000000000007c	
	18:59:26   	0031 8000000d0e0b3ba1 003c64 000000000000007c	
	18:59:27   	0032 8000000d0e0b3ba1 002364 0000000000000063	
	18:59:27   	0033 8000000d0e0b3ba1 002364 0000000000000063	
	18:59:30   	0034 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:30   	0035 8000000d0e0b3ba1 006464 00000000000000a4	
	18:59:49   	0036 8000000d0e0b3ba1 006406 0000000000000046	
	18:59:49   	0037 8000000d0e0b3ba1 006406 0000000000000046	
	18:59:53   	0038 8000000d0e0b3ba1 006400 0000000000000040	
	18:59:53   	0039 8000000d0e0b3ba1 006400 0000000000000040	
	18:59:56   	003a 8000000d0e0b3ba1 006464 00000000000000a4
	18:59:56   	003b 8000000d0e0b3ba1 006464 00000000000000a4

Those colours just don't look right at all.

If you go here: https://jsbin.com/bezomev/edit?html,css,js,output and paste that col. in you see things like: https://imgur.com/a/mZloElF

Get another trace of just pressing the red, green, blue buttons. Strip out all the common parts, and we're left with...

 00 64 64
 00 64 64
 00 64 64
 00 64 64
 3c 64 64
 3c 64 64
 3c 64 64
 3c 64 64
 78 64 64
 78 64 64
 78 64 64
 78 64 64
 78 64 64

... I don't think this is RGB at all.

What about HSL?

0 = red 120 = green 240 = blue

Let's convert the numbers in to decimal. I might not need all the columns, but try it anyway...

0   100 100
0   100 100
0   100 100
60  100 100
60  100 100
60  100 100
120 100 100
120 100 100
120 100 100 

Welp, what if we multiplied the first column by 2?

0
0
0
120
120
120
240
240
240

That looks familiar, but, like, really though? Maybe first byte here is hue then?

Now we need to try it out with some brightness changes.

red red red blue blue blue green 50% sat 0% sat 50% sat 100% sat 75% sat 100% sat 50% bright 0% bright 25% bright 50% bright 75% bright 100% bright red

file: hsl_test.log

0004 800000 0d 0e0b3ba1   00 64 64   00 000000000000 a4
0005 800000 0d 0e0b3ba1   78 64 64   00 000000000000 1c
0006 800000 0d 0e0b3ba1   78 64 64   00 000000000000 1c
0007 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
0008 800000 0d 0e0b3ba1   3c 31 64   00 000000000000 ad
0009 800000 0d 0e0b3ba1   3c 31 64   00 000000000000 ad
000a 800000 0d 0e0b3ba1   00 00 64   00 000000000000 40
000b 800000 0d 0e0b3ba1   00 00 64   00 000000000000 40
000c 800000 0d 0e0b3ba1   3c 35 64   00 000000000000 b1
000d 800000 0d 0e0b3ba1   3c 35 64   00 000000000000 b1
000e 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
000f 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
0010 800000 0d 0e0b3ba1   3c 4c 64   00 000000000000 c8
0011 800000 0d 0e0b3ba1   3c 4c 64   00 000000000000 c8
0012 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
0013 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
0014 800000 0d 0e0b3ba1   3c 64 32   00 000000000000 ae
0015 800000 0d 0e0b3ba1   3c 64 32   00 000000000000 ae
0016 800000 0d 0e0b3ba1   3c 64 00   00 000000000000 7c
0017 800000 0d 0e0b3ba1   3c 64 00   00 000000000000 7c
0018 800000 0d 0e0b3ba1   3c 64 1c   00 000000000000 98
0019 800000 0d 0e0b3ba1   3c 64 1c   00 000000000000 98
001a 800000 0d 0e0b3ba1   3c 64 34   00 000000000000 b0
001b 800000 0d 0e0b3ba1   3c 64 34   00 000000000000 b0
001c 800000 0d 0e0b3ba1   3c 64 4a   00 000000000000 c6
001d 800000 0d 0e0b3ba1   3c 64 4a   00 000000000000 c6
001e 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
001f 800000 0d 0e0b3ba1   3c 64 64   00 000000000000 e0
0020 800000 0d 0e0b3ba1   00 64 64   00 000000000000 a4

Once more to really make sure. File:

Connect On green

Set Bri 100% Sat 100% Set bri 50% sat 100% Set bri 25% sat 100% Set bri 0% sat 100% Set bri 100% sat 100% Set red Set blue

File: finalhsltest.log

RGB: [0, 0, 0] RGB: [0, 255, 0] RGB: [0, 132, 0] RGB: [0, 61, 0] RGB: [0, 0, 0] RGB: [0, 255, 0] RGB: [255, 0, 0] RGB: [0, 0, 255]

Success!

It's taken pretty much all day, but I think I have the colour settings understood. I still can't send messages yet, but that can come next. It's HSV, where the first byte is H/2 (so it will fit in one byte).

Day 2

Let's look at the white LEDs to start with.

White on. Click through the white colours from warm to cool. Brightness 100%, 75%, 50%, 25%, 0, 50 , 100 off

The first line is from the previous RGB experiments so we can compare and contrast.

0026 8000000d 0e0b3b a1 aae464 0000 0000000000 ce

0004 8000000d 0e0b3b 23 000000 0000 0000320000 90 - power on

0005 8000000d 0e0b3b b1 000000 0064 0000000000 50
0006 8000000d 0e0b3b b1 000000 1464 0000000000 64
0007 8000000d 0e0b3b b1 000000 2864 0000000000 78
0008 8000000d 0e0b3b b1 000000 3c64 0000000000 8c
0009 8000000d 0e0b3b b1 000000 5064 0000000000 a0
000a 8000000d 0e0b3b b1 000000 6464 0000000000 b4
000b 8000000d 0e0b3b b1 000000 6444 0000000000 94
000c 8000000d 0e0b3b b1 000000 642e 0000000000 7e
000d 8000000d 0e0b3b b1 000000 6415 0000000000 65
000e 8000000d 0e0b3b b1 000000 6401 0000000000 51
000f 8000000d 0e0b3b b1 000000 642e 0000000000 7e
0010 8000000d 0e0b3b b1 000000 6464 0000000000 b4

0011 8000000d 0e0b3b 24 000000 0000 0000320000 91 - power off

The white led settings are the two bytes after the RGB colours by the looks of it. They max out at 0x64 which is 100, so I would assume that it's a percentage, certainly for the brightness which is the 2nd of the two bytes. Also note that byte 10 has changed from 0xa1 to 0xb1 for white led control.

That was easy.

symphony

This is the effects stuff. From the main screen I did this...

speed 100 speed 50 speed 0 speed 10 speed 75 speed 100 brightness 75 b 50 b 25 b 0 b 25 b 50 b 75 b 100

click throu the effects 1 by 1 for 10 off

0002 800000 0c0d0b1014160a1e0a192907000fc4
0003 800000 0c0d0b1014160a1e0a192907000fc4
0012 800000 0d0e0b3ba1 3c6464 0000 0000000000 e0 - rgb command
000c 800000 0d0e0b3bb1 000000 642e 0000000000 7e - white command

cntr ?????? mode?    e  s  b
0004 800000 04050b38 01 64 64
0005 800000 04050b38 01 32 64
0006 800000 04050b38 01 01 64
0007 800000 04050b38 01 33 64
0008 800000 04050b38 01 4d 64
0009 800000 04050b38 01 64 64
000a 800000 04050b38 01 64 4c
000b 800000 04050b38 01 64 32
000c 800000 04050b38 01 64 16
000d 800000 04050b38 01 64 00
000e 800000 04050b38 01 64 1a
000f 800000 04050b38 01 64 34
0010 800000 04050b38 01 64 4c
0011 800000 04050b38 01 64 64
0012 800000 04050b38 01 64 64
0013 800000 04050b38 02 64 64
0014 800000 04050b38 03 64 64
0015 800000 04050b38 04 64 64
0016 800000 04050b38 05 64 64
0017 800000 04050b38 06 64 64
0018 800000 04050b38 07 64 64
0019 800000 04050b38 08 64 64
001a 800000 04050b38 09 64 64
001b 800000 04050b38 0a 64 64
0005 800000 04050b38 01 64 64
0006 800000 04050b38 02 64 64
0007 800000 04050b38 03 64 64
0008 800000 04050b38 04 64 64
0009 800000 04050b38 71 64 64

001c 800000 0d0e0b3b240000000000000032000091 - off

Starting to look like we have 4 bytes counter, 3 bytes fixed as 80 00 00 over all commands. The the effects have a different 4 bytes to the colours, but seems fixed during all effects. Then a byte for effect number, a byte for speed and a byte for brightness. These packets are shorter than the colour packets and don't seem to have a checksum at all.

Music mode

I have no interest in getting this working, so not going to spend time on it. If you're interested please get in touch.

Smear

This tool is quite hard to use in the app, and I'm not really sure how it works. I think it might be useful though, so I'm going to try and work it out. How hard can it be? It's probably a long list of pixel colours.

diy 1 - red diy 2 - green (might have missed the button here, I don't see it in the traces) diy 3 - blue slider to red slider to green slider to red click paint tool start at 12 o clock and draw round to 6 o clock green start at 12 go to 6 click erase start at 12, anti clockwise to 6 off

0001 8000000 40 50 a818a8b96
0002 8000000 c0 d0 b1014160a1e0b110807000f9 c7
0003 8000000 c0 d0 b1014160a1e0b110907000f9 d7

|-------fixed---------------| cnt? same   cmd? 
0202 00a5 00a1 0004 0012 1700 0004 800000 9697 0b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff00000 1333200 8f
0202 00a5 00a1 0004 0012 1700 0004 800000 9697 0b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff00000 1333200 8f
0202 00a5 00a1 0004 0012 1700 0006 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0007 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0008 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 0009 800000 9697 0b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff0 1333200 8f
0202 00a5 00a1 0004 0012 1700 000a 800000 9697 0b59 d40000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d400000 1333200 87
0202 00a5 00a1 0004 0012 1700 000b 800000 9697 0b59 d40000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d400000 1333200 87
0202 00a5 00a1 0004 0012 1700 000c 800000 9697 0b59 19d400 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff d40000 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d4000 1333200 c6
0202 00a5 00a1 0004 0012 1700 000d 800000 9697 0b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d400 19d4000 1333200 30
0202 001c 0018 0004 0012 1700 000e 800000 0d0e 0b3b 240000 000000 000032 000091 - off?

Notes:

  • There is a new header First 12 bytes seem to be the same for each line of RGB data
  • Then there is a counter by the looks of it (yes, this continues to increment from where the previous commands left off when changing modes)
  • Then the classic 800000 we've seen before (what is this??)
  • Then two bytes of something which is the same for the colour settings and different for the last command which I think must be an off
  • There are 48 RGB tuples per line, and according to the app there are 48 RGB LEDs. So this is probably just a stream of colours for each LED in turn. Nice!
  • Then there is a fixed tail. I don't think the last bit is a checksum, I think it's some kind of command indicator. edit: this is used for the modes in smear.
  • I don't think moving the slider in the app sends any data, only the pattern is sent whole. Makes sense

Next test: its off smear All red (doy 1) All green All blue Erase all Red Draw 12 - 6 clock wise Erase 12 - 6 clock wise Green Draw 12 - 6 anti clock wise Erase 12 - 6 anti close wise off

(messed this up a bit, but should still be understandable. Clicked the wrong thing)

00028000000c0d0b1014160a1e0b360507000fbe
00038000000c0d0b1014160a1e0b360507000fbe
020200a500a1000 400121700 0004 800000 96970b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 01333200 8f
020200a500a1000 400121700 0005 800000 96970b59 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 01333200 8f
020200a500a1000 400121700 0006 800000 96970b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 01333200 8f
020200a500a1000 400121700 0007 800000 96970b59 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 000000 000000 000000 0000ff 0000ff 0000ff 01333200 92
020200a500a1000 400121700 0008 800000 96970b59 000000 000000 000000 000000 000000 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 0000ff 000000 000000 000000 000000 000000 000000 01333200 9a
020200a500a1000 400121700 0009 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000a 800000 96970b59 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 ff0000 01333200 8f
020200a500a1000 400121700 000b 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000c 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000d 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000e 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 000f 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0010 800000 96970b59 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 d40000 01333200 9f
020200a500a1000 400121700 0011 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0012 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0013 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 d40000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 93
020200a500a1000 400121700 0014 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0016 800000 96970b59 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 01333200 47
020200a500a1000 400121700 0017 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 0018 800000 96970b59 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 33
020200a500a1000 400121700 0019 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 bf
020200a500a1000 400121700 001a 800000 96970b59 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 27d400 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01333200 33

001b 80 00 00 0d 0e 0b 3b 23 00 00 00 00 00 00 00 32 00 00 90 - on 
001c 80 00 00 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91 - off

All black Advanced Smear Mode Lightning 80 Lightning 100 Lightning 50 L 25 L 1 L 100 Bright 100 0 50 100 Stream one way Stream the other Strobe Jump off

0001 800000 04050a818a8b96
0002 800000 0c0d0b1014160a1e0d2c1307000fc4
0003 800000 0c0d0b1014160a1e0d2c1307000fc4

020200a500a1000 400121700 0019 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf - previous edits for comparison

020200a500a1000 400121700 0004 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0005 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0006 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 59 32 00 e5
020200a500a1000 400121700 0007 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 32 00 f0
020200a500a1000 400121700 0008 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 33 32 00 bf
020200a500a1000 400121700 0009 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 17 32 00 a3
020200a500a1000 400121700 000a 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 01 32 00 8d
020200a500a1000 400121700 000b 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 32 00 f0
020200a500a1000 400121700 000c 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 64 00 22
020200a500a1000 400121700 000d 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 00 00 be
020200a500a1000 400121700 000e 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 33 00 f1
020200a500a1000 400121700 000f 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 01 64 64 00 22
020200a500a1000 400121700 0010 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0011 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0012 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 00 23
020200a500a1000 400121700 0013 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 02 64 64 01 24
020200a500a1000 400121700 0014 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 03 64 64 01 25
020200a500a1000 400121700 0015 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 03 64 64 00 24
020200a500a1000 400121700 0016 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 04 64 64 00 25
020200a500a1000 400121700 0017 800000 96970b59 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 04 64 64 00 25

00188000000d0e0b3b240000000000000032000091 - off

Conclusion for smear:

  • The packets are 170 bytes long
  • There is a 12 byte header of some sort. Seems fixed.
  • Then there is the counter. I'm reasonably sure this is a two byte counter
  • Then there is a three byte "80 00 00" which we've seen elsewhere
  • Then four bytes which stay the same
  • Then 48 bytes of RRGGBB. Pixel zero is where the wire enters the metal circle and then with the stand on the left they go clockwise
  • Then there are 5 bytes:
    • mode (1 stream one way, 2 stream the other, 3 strobe, 4 jump)
    • Speed (1 - 100%)
    • Brightness (1-100%)
    • ?
    • ? checksum perhaps?

Timer

all red timer button time 14:18 create timer time set 14:30 to turn off confirm create timer 14:45 turn on create timer 15:00 turn on repeat sun, wed, sat turn on

0001 800000 04 05 0a 81 8a 8b 96
                                 M? H?
0002 800000 0c 0d 0b 10 14 16 0a 1e 0e 11 31 07 00 0f c8
0003 800000 0c 0d 0b 10 14 16 0a 1e 0e 11 31 07 00 0f c8
0004 800000 0d 0e 0b 3b a1 00 64 64 00 00 00 00 00 00 00 a4
0005 800000 05 06 0a 22 2a 2b 0f 86
0006 800000 05 06 0a 11 1a 1b 0f 55

This is non-obvious. Not important for now.

Initial conection

Enable BT in app Scans Gets some basic info Click in Connects Turn off Back to main screen exit

  1. MTU negotiation 200 bytes. Later tablet asked for 256, but light said 200.
  2. A load of service discovery stuff I don't really understand. Not sure where the setting the notification is happening
  3. First outgoing request to light is: handle: 0x0017 service UUID: 0xffff, UUID: 0xff01. Value 0001 800000 04 05 0a 81 8a 8b 96
  4. Recvd handle value notification. Handle 0x0014. UUID 0xffff. UUID: 0xff02. Value 046d80000033340a7b22636f6465223a302c227061796c6f6164223a2238313144323336314630303046463030303030303032303033303433227d
  5. Sent (0xffff 0xff01) 0002 800000 0c 0d 0b 10 14 16 0a 1e 0e 39 2f 07 00 0f ee
  6. Sent (0xffff 0xff01) 0003 800000 0c 0d 0b 10 14 16 0a 1e 0e 39 2f 07 00 0f ee
  7. Sent (0xffff 0xff01) 0004 800000 0d 0e 0b 3b 24 00 00 00 00 00 00 00 32 00 00 91 (I think this is an on or an off)
  8. Rxed (0xffff 0xff02) 046e 800000 33 34 0c 7b 22 63 6f 64 65 22 3a 30 2c 22 70 61 79 6c6f6164223a2238313144323436314630303046463030303030303032303033303434227d

Dunno. This is boring and hard.

Checksum guess work

0004 8000000d 0e0b3ba1 00646400 00000000 0000a4
0005 8000000d 0e0b3ba1 3c643000 00000000 0000ac
0006 8000000d 0e0b3ba1 78646400 00000000 00001c
0007 8000000d 0e0b3ba1 1e646400 00000000 0000c2
0008 8000000d 0e0b3ba1 00646400 00000000 0000a4
0011 8000000d 0e0b3ba1 00646400 00000000 0000a4
0012 8000000d 0e0b3ba1 00640000 00000000 000040
0013 8000000d 0e0b3ba1 00640000 00000000 000040
0014 8000000d 0e0b3ba1 00640000 00000000 000040
0015 8000000d 0e0b3ba1 00640000 00000000 000040
0016 8000000d 0e0b3ba1 00000000 00000000 0000dc
0017 8000000d 0e0b3ba1 00000000 00000000 0000dc
0018 8000000d 0e0b3ba1 00006400 00000000 000040
0019 8000000d 0e0b3ba1 00000100 00000000 0000dd
001a 8000000d 0e0b3ba1 00006400 00000000 000040
001b 8000000d 0e0b3ba1 00006400 00000000 000040
001c 8000000d 0e0b3ba1 00000100 00000000 0000dd
001d 8000000d 0e0b3ba1 00006400 00000000 000040
001e 8000000d 0e0b3ba1 00000100 00000000 0000dd
001f 8000000d 0e0b3ba1 00646400 00000000 0000a4
0020 8000000d 0e0b3ba1 00006400 00000000 000040
0021 8000000d 0e0b3ba1 3c643000 00000000 0000ac
0022 8000000d 0e0b3ba1 00006400 00000000 000040
0023 8000000d 0e0b3ba1 78646400 00000000 00001c
0024 8000000d 0e0b3ba1 00006400 00000000 000040
0025 8000000d 0e0b3ba1 1e646400 00000000 0000c2

0026 8000 000d 0e0b3ba1 00006400 00000000 000040

Dunno. It doesn't get solved by any of the online CRC calculators.

Attempting to make sense of the decompiled app - apktool

I loaded the app in to apktool and looked in at the files. They don't make any sense to me. I found a couple of interesting things that look relevant in case someone smarter than me can make sense of them.

The interesting stuff lives in /smali/com/zengge/hagallbjarkan/

Fun fact: Hagall Bjarkan is the who Bluetooth is named after.

Useful resources:

Files of interest:

  • remoter/RemoterController.smali - defines .method public static send(
  • protocol/remoter/RemoterProtocol.smali - Seems to construct the byte array which gets sent to the BLE device

None of what follows should be considered truth. It's just my guess work.

In RemoteProtocol.smali I found reference to "encryption" which is just xor.

It seems to pass something in to encrypt1 and then in to encrypt2 and then maybe CRC? I've added my best guess comments below (I have no idea what I'm doing). What's confusing me most, other than this strange code is that the packets aren't really encrypted since I can read perfectly logical values from the payload. So what is being encrypted? I assume nothing, and this is just a checksum. But then why so complicated?

If encoder is what builds the byte array, where is that called from? It is called from send in RemoterController.smali. send looks like this:

.method public static send(Landroid/content/Context;II[B)V // Called with a context?, two ints and a byte array
    .locals 1
    int-to-byte p2, p2 // int p2 is converted to a byte. I think this is the 2nd int passed in
    const/16 v0, 0x1f // v0 is set to 0x1f v0 is the first local register
	// encoder is called with p2, 0x1f, the first int, the byte array
    invoke-static {p2, v0, p1, p3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encoder(BBI[B)[[B
    move-result-object p1
    invoke-static {p1}, Lcom/zengge/hagallbjarkan/utils/AdvertiseUtils;->createAdvertiseData03([[B)Landroid/bluetooth/le/AdvertiseData;
    move-result-object p1
    invoke-static {p0, p1}, Lcom/zengge/hagallbjarkan/utils/AdvertiseUtils;->advertiseData(Landroid/content/Context;Landroid/bluetooth/le/AdvertiseData;)V
    return-void
.end method

So send calls RemoterProtocol->encoder with a byte, a byte, and int and a byte array, it returns a byte array. The returned array is then sent out via AdvertiseData in the Landroid stack. I assume this is just sending it over the air, but I don't get why it's called "Advertise". So perhaps this is wrong, or perhaps the send happens elsewhere.

Anyway, the thing I think I need to know is what is getting passed in to encoder. Reading this: https://github.com/JesusFreke/smali/wiki/Registers#how-method-parameters-are-passed-into-a-method

.method public static encoder(BBI[B)[[B
    .locals 7
    const/16 v0, 0x9
    const/4 v1, 0x0
    if-nez p3, :cond_0    // if the byte array is not zero
    new-array p3, v0, [B
    goto :goto_0
    :cond_0               // we have been passed an array with something in
    array-length v2, p3   // v2 is now array len
    if-ge v2, v0, :cond_1 // if len > 0x9 jump to cond_1
    new-array v2, v0, [B  // not bigger than 9, so new byte array v2 0x9 long
    array-length v3, p3   // v3 now len of passed in array
    invoke-static {p3, v1, v2, v1, v3}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
	//       p3 = passed in array
	//       v1 = 0x0
	//       v2 = len of array
	//       v1 = 0x0
	//       v3 = 0x9
	// this is obvs copying the passed array to a new bigger array
    move-object p3, v2   // v2 len of array is now p3 len of array
    goto :goto_0
    :cond_1 // We were passed an array bigger than 0x9
    array-length v2, p3    // v2 = len of array
    if-gt v2, v0, :cond_2  // if array len > 0x9 goto cond_2
    :goto_0 // v3 is array len, p3 is an array of size 0x9 bytes
    const/16 v2, 0x1a 
    new-array v3, v2, [B // v3 is new array of 0x1a long bytes
    const/16 v4, 0x5a    // v4 is 0x5a
    aput-byte v4, v3, v1 // put 0x5a in position 0x0 of array v3
    const/4 v4, 0x1      // v1 = 0x1
    const/16 v5, 0x71    // v5 = 0x71
    aput-byte v5, v3, v4 // put 0x71 in position 0x1 of array v3
    const/4 v4, 0x2
    aput-byte v1, v3, v4 // put 0x0 in position 0x2 of array v3
    const/4 v4, 0x3
    const/16 v5, 0x11
    aput-byte v5, v3, v4 // put 0x11 in pos 0x3 
    const/4 v4, 0x4
    aput-byte v1, v3, v4   // put 0x0 in pos 0x4 
    const/4 v5, 0x5
    aput-byte p1, v3, v5      // put p1 (0x1f) I think in pos 0x5 (2nd param reg)
    const/4 p1, 0x6
    invoke-static {}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->nextSn()B  // I think this might be the counter?
    move-result v5
    aput-byte v5, v3, p1      // put v5 (from nextsn) in 0x6 let's assume this is the counter
    const/4 p1, 0x7           // pos 7 in p1 now
    const/high16 v5, 0xff0000 // 
    and-int/2addr v5, p2      // add p2 to v5. p2 is 3rd param, an int. Called from RemoterController `send` method. 
    shr-int/lit8 v5, v5, 0x10 // vx, vy, vz - Shift vy right by the positions specified by vz (0x10) and store the result into vx.  (could be encoding grb?)
    int-to-byte v5, v5
    aput-byte v5, v3, p1
    const p1, 0xff00
    and-int v5, p2, p1
    const/16 v6, 0x8
    shr-int/2addr v5, v6
    int-to-byte v5, v5
    aput-byte v5, v3, v6
    and-int/lit16 p2, p2, 0xff
    int-to-byte p2, p2
    aput-byte p2, v3, v0
    const/16 p2, 0xa
    aput-byte v1, v3, p2
    const/16 v0, 0xb
    aput-byte v1, v3, v0
    const/16 v0, 0xc
    aput-byte p0, v3, v0
    const/16 p0, 0xd
    array-length v0, p3
    invoke-static {p3, v1, v3, p0, v0}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
    const/16 p0, 0x16
    aput-byte v1, v3, p0
    new-instance p3, Ljava/util/Random; // I dont see any evidence of random number usage in the HCI logs?
    invoke-direct {p3}, Ljava/util/Random;-><init>()V
    invoke-virtual {p3}, Ljava/util/Random;->nextInt()I
    move-result p3 // p3 is now a random int
    and-int/lit16 p3, p3, 0xff // adds 0xff to the random number and puts it in p3
    int-to-byte p3, p3 // convert p3 to a byte
    const/16 v0, 0x17
    aput-byte p3, v3, v0  // put value of p3 in to array v3 at location v0 (0x17)
    aget-byte p3, v3, v0 // get from byte array v3 at location v0 (0x17) store in p3 (i.e. put it in then take it back out again?)
    // call encrypt1 with the array (v3), our randomnumber + 0xff (p3), p2 is 0xa I think, p0 is 0x16
    invoke-static {v3, p3, p2, p0}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encrypt1([BBII)V
    invoke-static {v3, v4, v0}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->encrypt2([BII)V
    invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->toCrc([B)I
    move-result p0
    const/16 p2, 0x18
    and-int/2addr p1, p0
    shr-int/2addr p1, v6
    and-int/lit16 p1, p1, 0xff
    int-to-byte p1, p1
    aput-byte p1, v3, p2
    const/16 p1, 0x19
    and-int/lit16 p0, p0, 0xff
    int-to-byte p0, p0
    aput-byte p0, v3, p1
    invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->reversal([B)V
    sget-object p0, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->TAG:Ljava/lang/String;
    new-instance p1, Ljava/lang/StringBuilder;
    invoke-direct {p1}, Ljava/lang/StringBuilder;-><init>()V
    const-string p2, " HEX "
    invoke-virtual {p1, p2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    invoke-static {v3, v2}, Lcom/zengge/hagallbjarkan/utils/ConvertUtil;->byte2HexStr([BI)Ljava/lang/String;
    move-result-object p2
    invoke-virtual {p1, p2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    invoke-virtual {p1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
    move-result-object p1
    invoke-static {p0, p1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
    invoke-static {v3}, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->toBytes([B)[[B
    move-result-object p0
    return-object p0
    :cond_2
    new-instance p0, Ljava/lang/RuntimeException;
    const-string p1, "Overflow param."
    invoke-direct {p0, p1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V
    throw p0
.end method

.method private static encrypt1([BBII)V 
// I think this means we have a byte array and two ints. One of the ints might be the length of the array. Returns VOID, so must edit the array in place?
// called from above with encrypt1 with the array (v3), our randomnumber + 0xff (p3), p2 is 0xa I think, p0 is 0x16
// p0 = byte array
// p1 = random number + 0xff
// p2 = 0xa (dec 10)
// p3 = 0x16 (dec 22)
    .locals 1
    :goto_0
    if-gt p2, p3, :cond_0 // If we're past the end of the array(?) jump to cond_0 = return
    aget-byte v0, p0, p2 // Load v0 with a byte from p2 in array p0.
    xor-int/2addr v0, p1 // xor the loaded value with whatever p1 is (random + 0xff? but that wouldnt fit in a byte)
    int-to-byte v0, v0 // convert it to a byte
    aput-byte v0, p0, p2 // put it back where it came from?
    add-int/lit8 p2, p2, 0x1 // Increment the counter by 1
    goto :goto_0 // Go around again
    :cond_0
    return-void
.end method

.method private static encrypt2([BII)V
    .locals 4
    move v0, p1
    :goto_0
    if-gt v0, p2, :cond_0
    aget-byte v1, p0, v0
    sget-object v2, Lcom/zengge/hagallbjarkan/protocol/remoter/RemoterProtocol;->args:[B
    sub-int v3, v0, p1
    aget-byte v2, v2, v3
    xor-int/2addr v1, v2 
    int-to-byte v1, v1
    aput-byte v1, p0, v0
    add-int/lit8 v0, v0, 0x1 // add one to the position and go around again
    goto :goto_0
    :cond_0
    return-void
.end method

.method private static toCrc([B)I
    .locals 3
    const/4 v0, 0x0
    move v1, v0
    :goto_0
    array-length v2, p0
    add-int/lit8 v2, v2, -0x2
    if-ge v0, v2, :cond_0
    aget-byte v2, p0, v0
    add-int/2addr v1, v2
    add-int/lit8 v0, v0, 0x1
    goto :goto_0
    :cond_0
    const p0, 0xffff
    and-int/2addr p0, v1
    return p0
.end method

Attemping to make sense of the decompiled app using jadx

This is a bit more useable. It converts back to something which looks more like code. I compared the bytes I thought the apktool would generate to the same thing here, and I was pretty close, so I think they are the same, but this one is a lot easier to read. I'm going to try using this instead.

Here's the same encoder from above, but decomplied by jadx:

public class RemoterProtocol {
    private static final String TAG = "com.zengge.hagallbjarkan.protocol.remoter.RemoterProtocol";
    private static final byte[] args = {17, 34, 4, 8, -103, 4, 36, 22, 4, -86, -69, -52, -16, 96, 97, -51, -49, Byte.MIN_VALUE, 53, 42};
    private static byte count = 1;

    public static byte[][] encoder(byte b2, byte b3, int i, byte[] bArr) {
        if (bArr == null) {
            bArr = new byte[9];
        } else if (bArr.length < 9) {
            byte[] bArr2 = new byte[9];
            System.arraycopy(bArr, 0, bArr2, 0, bArr.length);
            bArr = bArr2;
        } else if (bArr.length > 9) {
            throw new RuntimeException("Overflow param.");
        }
        System.arraycopy(bArr, 0, r3, 13, bArr.length);
        encrypt1(r3, r3[23], 10, 22);
        encrypt2(r3, 4, 23);
        int crc = toCrc(r3);
        byte[] bArr3 = {90, 113, 0, 17, 0, b3, nextSn(), (byte) ((16711680 & i) >> 16), (byte) ((i & CipherSuite.DRAFT_TLS_DHE_RSA_WITH_AES_128_OCB) >> 8), (byte) (i & 255), 0, 0, b2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) (new Random().nextInt() & 255), (byte) (((65280 & crc) >> 8) & 255), (byte) (crc & 255)};
        reversal(bArr3);
        String str = TAG;
        Log.i(str, " HEX " + ConvertUtil.byte2HexStr(bArr3, 26));
        return toBytes(bArr3);
    }

    private static void encrypt1(byte[] bArr, byte b2, int i, int i2) {
        while (i <= i2) {
            bArr[i] = (byte) (bArr[i] ^ b2);
            i++;
        }
    }

    private static void encrypt2(byte[] bArr, int i, int i2) {
        for (int i3 = i; i3 <= i2; i3++) {
            bArr[i3] = (byte) (bArr[i3] ^ args[i3 - i]);
        }
    }

    private static byte nextSn() {
        byte b2;
        synchronized (RemoterProtocol.class) {
            b2 = count;
            count = (byte) (b2 + 1);
        }
        return b2;
    }

    private static void reversal(byte[] bArr) {
        int i = 2;
        while (i < bArr.length - 1) {
            byte b2 = bArr[i];
            int i2 = i + 1;
            bArr[i] = bArr[i2];
            bArr[i2] = b2;
            i = i2 + 1;
        }
    }

    private static byte[][] toBytes(byte[] bArr) {
        byte[][] bArr2 = (byte[][]) Array.newInstance(byte.class, bArr.length / 2, 2);
        for (int i = 0; i < bArr2.length; i++) {
            byte[] bArr3 = bArr2[i];
            int i2 = i * 2;
            bArr3[0] = bArr[i2];
            bArr3[1] = bArr[i2 + 1];
        }
        return bArr2;
    }

    private static int toCrc(byte[] bArr) {
        int i = 0;
        for (int i2 = 0; i2 < bArr.length - 2; i2++) {
            i += bArr[i2];
        }
        return 65535 & i;
    }
}

What surprises me is how much CRC and encryption stuff is seemingly performed. For all that messing about, I was still able to manually decode the protocol for light colour. There is something going on here that I don't understand, and it's probably to do with those 8 bytes at the start which seem to be static.

  • You've got to pass in a max 9 element array to encoder.
  • It then copies your whole 9 byte array in to an array called r3 starting at position 13(?? the array is only 9 long. Decompile error?)

I took another look at the Smali code since the Java wasn't making much sense. It looks like the decompiler got confused. There are references to encryption libraries in Java which in Smali look like simple bitshifts (but contain the letters "shr" so maybe misinterpreted as a encryption routine?)

 const/16 v2, 0x1a
 new-array v3, v2, [B
  • This new array is then filled up with (* indicates see notes):

Example real message: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 00 10 80 00 00 0d 0e 0b 3b a1 3c 4c 64 00 00 00 00 00 00 00 c8

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 0 1 2 3 4 5 6 7 8 9 A B c d e f 10 11 12 13 14 15 16 17 18 19 1A 5A 71 00 11 00 P1 NEXTSN *FF *FF *FF 00 00 Bp0 * * * * * * * * * 00 *

Note A - byte 8 (starting at 1)

  • 0xFF0000 AND (byte) p2
  • shift right 0x10 = 16 places
  • regardless of the input p2, the answer will always be 0xFF?
  • So what's the point?
  • Could be a code bug, could be a decompile bug

Note B - byte 9

  • Decompiled java talks about an encryption routine, but the smali doesn't?
  • 0xff00 && p2 - which is a byte, so max 0xff? =p5
  • shift right by 0x8
  • Output is 0xFF regardless?
  • Add to array at 0x8
  • Why?

Note C - byte 10

  • p2 AND 0xFF
  • Add to array at v0 which is 0x9

Note D - byte 14

  • Create a new array v0 which is p3 long
  • Call arraycopy (src, srcpos, dest, destpos, num bytes to copy).
  • Which works out to be copy (p3, 0, our working array, 0xd, len(p3)
  • So copy all the bytes from the array passed in at the start in to here starting at 0xD
  • I assume this is the max 9 long array created earlier
  • then set p0 0x16 which is 9 after 0xd

Note E - byte 24

  • Get a random int in to p3
  • Set p3 to be int rand AND 0xff (so last two digits)
  • Store our random number byte in position 0x17 (used for xor)
  • Gets it back out again in to p3
  • Calls "encrypt1" with main array, random & FF, 0xa, 0x16) (line 206)
  • encypt 1: if 0xa > 0x16 (p2) return void (line 278)
  • encrypt1(array, rand, start, stop)
  • 0xa is a const at this point, as is 0x16. These might be array lengths?
  • So in this case we carry on
  • load v0 with value of array[p2 (0x16)]
  • xor it with p1 (0xa)
  • put it back
  • add 1 to p2
  • So encrypt1 xors the array values from p2 0xa to p3 0x16 with a random number which is stored in the main array at 0x17 where it is anded with FF.
  • On to encrypt2. Called with (main array, v4 0x4, v0 0x17 (pos of rand but also len of array?))
  • v0 is 0x4
  • if v0 0x4 > p2 0x17 return. So a loop over bytes 4 to 17?
  • v1 = byte at array[counter v0]
  • load v2 with the array which is created at the top of the file (20 bytes):
0x11   | 17 
0x22   | 34
 0x4   | 4
 0x8   | 8
-0x67   | -103
0x4     | 4
0x24    | 36
0x16    | 22
0x4     | 4
-0x56   | -86
-0x45   | -69
-0x34   | -52
-0x10   | -16
0x60    | 96
0x61    | 97
-0x33   | -51
-0x31   | -49
-0x80   | -128
0x35    | 53
0x2a    | 42
  • What the hell is this? Is it an encryption key? Let's call it that
  • set v3 to zero. 0x4 minus 0x4 (i.e. at the start this is zero)
  • set v2 to the encryption key array
  • set v2 to byte v3 of the encyption key array
  • v0 is counter
  • xor byte v1 with byte v2. v1 is array_p0[v0]. v2 is encryption_array[v0 - p1]
  • put the result in main array[v0]
  • increment v0 by 1

It runs it through "reversal" which starts at position "2" so 3rd byte and loops until one before the end. That is it skips the first 2 elements and the last element. It just swaps bytes around, it doesn't reverse the whole array.

That would give us:

5A 71 11 00 1F 00 FF 00 FF FF 00 00 00 FF 00 00 00 00 00 00 00 00 00 FF FF FF FF

If we want to try and work backwards, The "set the colour" messages are 21 bytes long. 4 bytes of counter 7 bytes of wtf 1 byte of something related to what it's doing 5 bytes of colour 5 bytes of 0 1 byte checksum

0010 8000000d0e0b3b b1 000000 6464 0000000000 b4 0006 8000000d0e0b3b a1 006464 0000 0000000000 a4

SDK

I found an SDK! http://cnwifidevsdk.magichue.net:4000/ble/

I'm not certain yet if it's useful, or even the right one. But here's some things which might be useful:

    public static ZGHBDevice newDevice(byte[] bArr, ScanResult scanResult) {
        int i;
        if (bArr == null || bArr.length < 32) {
            return null;
        }
        try {
            LinkedList linkedList = new LinkedList();
            LinkedList linkedList2 = new LinkedList();
            int i2 = 0;
            do {
                int i3 = bArr[i2] & 255;
                if (i3 == 0) {
                    break;
                }
                int i4 = i2;
                int i5 = i2;
                int i6 = bArr[i2 + 1] & 255;
                int i7 = i3 - 1;
                byte[] bArr2 = new byte[i7];
                System.arraycopy(bArr, i5 + 2, bArr2, 0, i7);
                linkedList.add(Integer.valueOf(i6));
                linkedList2.add(bArr2);
                i = i4 + i3 + 1;
                i2 = i;
            } while (i != bArr.length);
            if (linkedList.size() != linkedList2.size()) {
                return null;
            }
            if (linkedList.contains(22)) {
                if (linkedList.contains(255)) {
                    String str = TAG;
                    Log.i(str, "correct packets");
                    int index = getIndex(linkedList, 22);
                    int index2 = getIndex(linkedList, 255);
                    byte[] bArr3 = (byte[]) linkedList2.get(index);
                    byte[] bArr4 = (byte[]) linkedList2.get(index2);
                    Log.i(str, " buffer : " + e.a(bArr3));
                    if (bArr3.length == 16 || bArr3.length == 29) {
                        int manufacturer = getManufacturer(bArr3);
                        int manufacturer2 = getManufacturer(bArr4);
                        if (manufacturer == -1 || manufacturer2 == -1) {
                            return null;
                        }
                        ZGHBDevice zGHBDevice = new ZGHBDevice(scanResult);
                        setDeviceInfo(zGHBDevice, scanResult.getDevice().getName(), manufacturer, bArr3);
                        if (zGHBDevice.getBleVersion() >= 5) {
                            byte b = bArr3[14];
                            byte b2 = bArr3[15];
                            zGHBDevice.setCheckKeyFlag(b);
                            zGHBDevice.setFirmwareFlag(b2);
                        }
                        int length = bArr4.length - 3;
                        byte[] bArr5 = new byte[length];
                        System.arraycopy(bArr4, 3, bArr5, 0, length);
                        zGHBDevice.setState(bArr5);
                        setDeviceName(linkedList, linkedList2, zGHBDevice);
                        return zGHBDevice;
                    }
                    return null;
                }
                return null;
            } else if (linkedList.contains(255)) {
                int index3 = getIndex(linkedList, 255);
                if (index3 == -1) {
                    return null;
                }
                byte[] bArr6 = (byte[]) linkedList2.get(index3);
                if (bArr6.length != 29) {
                    return null;
                }
                int manufacturer3 = getManufacturer(bArr6);
                if (manufacturer3 == -1) {
                    return null;
                }
                ZGHBDevice zGHBDevice2 = new ZGHBDevice(scanResult);
                setDeviceInfo(zGHBDevice2, scanResult.getDevice().getName(), manufacturer3, bArr6);
                if (zGHBDevice2.getBleVersion() >= 5) {
                    byte b3 = bArr6[14];
                    byte b4 = bArr6[15];
                    zGHBDevice2.setCheckKeyFlag(b3);
                    zGHBDevice2.setFirmwareFlag(b4);
                    byte[] bArr7 = new byte[11];
                    System.arraycopy(bArr6, 16, bArr7, 0, 11);
                    zGHBDevice2.setState(bArr7);
                    byte[] bArr8 = new byte[2];
                    bArr8[0] = bArr[27];
                    bArr8[1] = bArr[28];
                    zGHBDevice2.setRfu(bArr8);
                } else {
                    int length2 = bArr6.length - 16;
                    byte[] bArr9 = new byte[length2];
                    System.arraycopy(bArr6, 16, bArr9, 0, length2);
                    zGHBDevice2.setRfu(bArr9);
                }
                setDeviceName(linkedList, linkedList2, zGHBDevice2);
                return zGHBDevice2;
            } else {
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            return null;
        }
    }

Friday 4 November 2022

I have spent the last week reverse engineering what I think is the protocol from the disassembled Android app. I went through the smali byte code line by line and ported it to Python. It's included in this repo, but its nowhere near functional. Or tested.

Do you know why it's not tested? Well stay tuned...

Let's imagine that you're a manufacter of low cost consumer lighting products. You've got a new product that you want to launch to the market, it's a BT LE controlled light and you're going to need a microcontroller with an RF remote and you're going to need an Android app and an iOS app.

So you go to your head of software engineering and say "We need some control software for this new light we've got coming to the market. You've got 2 weeks, 2 people and no money. Get to work!".

Your engineering team meet and they talk about what's needed. One of the topics of conversation is how BT LE is a pretty unreliable communication mechansim and in order to make things more reliable and also a bit more secure, to stop all those naughty home automation nerds controlling your lights with their software, you should implement some form of encryption and checksum on the conversation. Everyone agrees this is a good idea.

You spend a few hours working out a protocol which is easy enough to be implemented on the low end MCU you're working with and their very poorly documented SDK and you break off and one engineer goes to write the Android and iOS apps and one engineer goes off to write the firmware for the microcontroller.

After a couple of days your engineer working on the firmware comes to a realisation that she's not getting paid enough for this crap, and that since her manager is an idiot and the app writer only knows how to use point-and-click app toolkits, no one is ever going to check that the C++ code. So why bother spending all that time implementing and testing the encryption and CRC checking code, why not just assume it's correct and carry out the instructions? Stupider like a FOX!

So that's what they did.

Once you connect to the device, set the MTU to 200, enable the notification on 0x0015 and perhaps send a couple of handshake packets, you're off to the races and you can send colour changing packets with scant regard for the packet counter at the start or the checksum at the end. Just set them to FF, no one will ever know.

And that is the story of how I wasted two weeks learning to read smali code and trying to make sense of an overly complex protocol.

I don't suppose this is the last chapter of the story, but it is at least a beginning.

Thanks for reading this far. I hope you had fun, I know I did.