Govee H617A - BLE LED Strip Lights - Reverse Engineering

TL;DR
Controlling the Govee H617A LED strip locally over Bluetooth is possible.

Background:
I own a set of Govee H617A LED strips, and unfortunately, I didn’t realize when purchasing that they were Bluetooth-only, not Wi-Fi enabled. This meant they could only be controlled via the Govee app using a BLE connection, and therefore, only while my phone was in proximity.

I explored various Govee BLE integrations for Home Assistant, but none of them worked with this model. Meanwhile, I already had other lights that were smart-enabled and fully automated. The idea of needing to manually open an app or flick a switch every time just didn’t sit right with me.

So I decided to reverse engineer the Govee H617A aiming for seamless control using Home Assistant, and ideally without relying on any flaky cloud-based hacks.

The (Frustrating) Journey

Initial Assumptions:

I started by assuming Govee had a standard BLE implementation. I dug through community integrations, GitHub issues, and even unpacked code from existing Home Assistant custom components.

Nothing worked.

Each attempt either failed to connect, or wouldn’t trigger the light. So I did what any obsessed tinkerer would do: I went full reverse-engineer mode.

Enter the BLE Sniffer

To get actual insight into what the Govee app was sending, I bought a Nordic nRF52840 dongle. It works with Nordic’s nRF Sniffer firmware and Wireshark, allowing full inspection of BLE packets.

If you want to replicate this setup, here’s what you’ll need:

  • nRF52840 dongle flashed with the Sniffer firmware
  • Wireshark with the Nordic BLE plugin (shows ATT, GATT, L2CAP layers)
  • The Govee app on your phone
  • A little patience and a lot of coffee

The Capture Process

  1. Kill the Govee App so the light disconnects from your phone/app.
  2. Start Wireshark, with the dongle set to scan on the right BLE channel.
  3. Open the Govee app and start toggling your light ON and OFF.
  4. Watch for traffic: you’ll see packets with Write Without Response (0x52), these are your key targets.
  5. Export the capture to .pcapng or JSON for inspection. Or just use Wireshark to trawl through the capture, but I found it to be cumbersome.

Realization: It’s Not 0x12
Most documentation and other BLE devices use Write Request (0x12) with response packets. Govee instead uses 0x52 ,no response, just fire and forget. These packets are raw binary, but fairly consistent…

  • The payload is 19 bytes.
  • The last byte is always a checksum: sum of previous bytes modulo 256.

That’s what I uncovered:

  • Turn On: 33 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33
  • Turn Off: 33 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32

The device expects these exact sequences. Anything else is ignored (at least i think they are).

Applying this to Home Assistant
I created 2x python scripts govee_turn_on.py and govee_turn_off.py and put them in /config/python/ on my HA instance

govee_turn_on.py

import asyncio
from bleak import BleakClient

MAC_ADDRESS = "YOUR_MAC_HERE"
CHAR_WRITE_UUID = "00010203-0405-0607-0809-0a0b0c0d2b11"

def build_packet(data):
    checksum = sum(data) & 0xFF
    return bytes(data + [checksum])

async def turn_on():
    async with BleakClient(MAC_ADDRESS) as client:
        pkt = build_packet([0x33, 0x01, 0x01] + [0x00]*5)
        await client.write_gatt_char(CHAR_WRITE_UUID, pkt)

asyncio.run(turn_on())

and
govee_turn_off.py

import asyncio
from bleak import BleakClient

MAC_ADDRESS = "YOUR_MAC_HERE"
CHAR_WRITE_UUID = "00010203-0405-0607-0809-0a0b0c0d2b11"

def build_packet(data):
    checksum = sum(data) & 0xFF
    return bytes(data + [checksum])

async def turn_off():
    async with BleakClient(MAC_ADDRESS) as client:
        pkt = build_packet([0x33, 0x01, 0x00] + [0x00]*5)
        await client.write_gatt_char(CHAR_WRITE_UUID, pkt)

asyncio.run(turn_off())

Then added a reference to them in the configuration yaml

shell_command:
  govee_on: python3 /config/python/govee_turn_on.py
  govee_off: python3 /config/python/govee_turn_off.py

Now I can use them in automations via the shell commands, and I’m (kinda) happy. Like I said, this isn’t an ideal solution. There isn’t an entity for the device. But if you want to do anything other than turning them on and off (set colors, scenes etc.) this process is repeatable to sniff what the app is sending, and go from there.

6 Likes

Thanks for sharing! I did pick up a couple of H617A during prime days and just trying to get the on / off functionality working but no luck using your code. I was able to find the MAC address using BLE Scanner but seems like the pkt length you are using is missing a few bytes from the 19 bytes you mention. I did try increasing the pkt calc to *16…

Was able to get it to work by changing this line in both scripts:

pkt = build_packet([0x33, 0x01, 0x01] + [0x00]*16)

and then hardcoded the checksum for each:

turn_on:
checksum = 0x33 & 0xFF

turn_off:
checksum = 0x32 & 0xFF

Thank you for doing the heavy lifting on this. At some point, I may dig deeper into exploring the different effects.

You guys are awesome.