Skip to content

Govee lights

Govee_H617A

manufacturer = "0388ec000a0101"
ble_mac = "c9:38:33:33:4d:70"

Handles

┌──────────────┬────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────┬────────────────────────────────────────────────┐
│ Handles │ Service > Characteristics │ Properties │ Data │
├──────────────┼────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────────┤
│ 0001 -> 0009 │ Generic Access ( 00001800-0000-1000-8000-00805f9b34fb ) │ │ │
│ 0003 │ Device Name ( 00002a00-0000-1000-8000-00805f9b34fb ) │ READ WRITE │ u'Govee_H617A_4D70' │
│ 0005 │ Appearance ( 00002a01-0000-1000-8000-00805f9b34fb ) │ READ WRITE │ Unknown │
│ 0007 │ Peripheral Preferred Connection Parameters ( 00002a04-0000-1000-8000-00805f9b34fb ) │ READ │ Connection Interval: 8 -> 10 │
│ │ │ │ Slave Latency: 0 │
│ │ │ │ Connection Supervision Timeout Multiplier: 400 │
│ 0009 │ 2ac9 ( 00002ac9-0000-1000-8000-00805f9b34fb ) │ READ │ '' │
│ │ │ │ │
│ 000a -> 000d │ Generic Attribute ( 00001801-0000-1000-8000-00805f9b34fb ) │ │ │
│ 000c │ Service Changed ( 00002a05-0000-1000-8000-00805f9b34fb ) │ READ INDICATE │ │
│ │ │ │ │
│ 000e -> 0016 │ 00010203-0405-0607-0809-0a0b0c0d1910 │ │ │
│ 0010 │ 00010203-0405-0607-0809-0a0b0c0d2b10 │ NOTIFY READ │ '' │
│ 0014 │ 00010203-0405-0607-0809-0a0b0c0d2b11 │ NOTIFY READ WRITE NO RESPONSE WRITE │ '' │
│ │ │ │ │
│ 0017 -> 0021 │ 02f00000-0000-0000-0000-00000000fe00 │ │ │
│ 0019 │ 02f00000-0000-0000-0000-00000000ff03 │ READ │ '' │
│ 001b │ 02f00000-0000-0000-0000-00000000ff02 │ NOTIFY READ │ u'ntf_enable' │
│ 001f │ 02f00000-0000-0000-0000-00000000ff00 │ READ │ '' │
│ 0021 │ 02f00000-0000-0000-0000-00000000ff01 │ WRITE NO RESPONSE WRITE │ │
│ │ │ │ │
└──────────────┴────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────┴────────────────────────────────────────────────┘

Tests

00002ac9-0000-1000-8000-00805f9b34fb: ????

00010203-0405-0607-0809-0a0b0c0d2b10: Rx
00010203-0405-0607-0809-0a0b0c0d2b11: Tx

02f00000-0000-0000-0000-00000000ff03
02f00000-0000-0000-0000-00000000ff02
02f00000-0000-0000-0000-00000000ff00
02f00000-0000-0000-0000-00000000ff01

Receive UUID

00010203-0405-0607-0809-0a0b0c0d2b10: Receive UUID

Get Auth Key???

Send UUID

00010203-0405-0607-0809-0a0b0c0d2b11: Send UUID

NFT Enable???

>>> python notify.py --address "c9:38:33:33:4d:70" 02f00000-0000-0000-0000-00000000ff02
2024-11-23 20:59:25,319 __main__ INFO: starting scan...
2024-11-23 20:59:25,455 __main__ INFO: connecting to device...
2024-11-23 20:59:26,111 __main__ INFO: Connected
2024-11-23 20:59:26,223 __main__ INFO: Unknown: bytearray(b'ntf_enable')
2024-11-23 20:59:26,223 __main__ INFO: Read[02f00000-0000-0000-0000-00000000ff02]: bytearray(b'ntf_enable')
2024-11-23 20:59:31,263 __main__ INFO: Unknown: bytearray(b'ntf_enable')
2024-11-23 20:59:31,263 __main__ INFO: Read[02f00000-0000-0000-0000-00000000ff02]: bytearray(b'ntf_enable')

Commands

AA010000000000000000000000000000000000AB: Keep Alive sent every 2 secs

330502 RED GREEN BLUE 00FFAE54000000000000000000(31 ^ RED ^ GREEN ^ BLUE)
Set to Red

  • Aa

    Get Value

    • 01: is_on
      • On: AA0101
      • Off: AA0100
    • 04: get_brightness
      • 1%: AA0401
      • 100%: AA0464
    • 05: Get Mode
      • 0407: Scene Mode Scene 7 romantic
    • 06: get_device_fw_version
      • Version: 3.02.24
    • 0703: hw_version
      • Version: 3.01.01
    • 0e: ???
      • ???: 0f
    • 11: ???
      • ???: 001e0f0f
    • 12: ???
      • ????: 00640000800a
    • 23: Current Colors?
      • Red: 0000000080
    • 40: ???
      • ???: 000f
    • a3: ???
      • ???: 01
    • a5: Number of Segments
      • 1501: 15 Segments starting at 01
    • a501: Get Segment 1
      • 64ff8d0b 64aa5511 64ff8d0b: (Brightness | Red | Green | Blue) 15 segments
    • a502: Get Segment 2
      • 64ff8d0b 64aa5511 64aa5511
    • a503: Get Segment 3
      • 64ff0000 64ff8d0b 64ff8d0b
    • a504: Get Segment 4
      • 64ff8d0b 64ff8d0b 64ff8d0b
    • a505: Get Segment 5
      • 64ff8d0b 64ff8d0b 64ff8d0b
    • b1: Auth_key?
      • No data
    • ee: ???
      • ???: 000a01
    • ff: ???
      • No data
  • 33: Write
    • 0100: Power Off
    • 0101: Power On
    • 04: Brightness
      • 1%: 01
      • 100% 64
    • 0504: Set Scene
      • 00: Sunrise
      • 01: Sunset
        ...
    • 051501: Segment Color Mode
      • 00FFFF 0000000000 3200: RRGGBB padding Number of segments

aa23ff:
- Command: Notify (aa23): ff00000080000000800000008000000080
- Unexpected response: b'\xaa#\xff\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x00\x80'

aa1200:
- Command: Notify (aa12): 00640000800a
- Unexpected response: b'\xaa\x12\x00d\x00\x00\x80\n'

aa1100:
- Command: Notify (aa11): 001e0f0f
- Unexpected response: b'\xaa\x11\x00\x1e\x0f\x0f'

330914:
- Unexpected response: b'3\t'

330914
330914

aa0400
aa0501
aa0f00
aa1100
aa1200
aa23ff
aa4100

Script

import asyncio
from bleak import BleakScanner, BleakClient

# UUIDs for the target device characteristics
NOTIFY_UUID = "00010203-0405-0607-0809-0a0b0c0d2b10"
WRITE_UUID = "00010203-0405-0607-0809-0a0b0c0d2b11"
TARGET_NAME = "Govee_H617A"

scenes = {
    "Sunrise":      "00",
    "Sunset":       "01",
    "Nightlight":   "02",
    "Movie":        "04",
    "Dating":       "05",
    "Romantic":     "07",
    "Illumination": "3f",
    "Cheerful":     "40",
}

#Commands for H617A
command_list = {
    #Write Commands
    "turn_on":        "330101",
    "turn_on2":       "3301ff",
    "turn_off":       "330100",
    "turn_off2":      "3301f0",

    "set_brightness": "3304", #01-64

    "set_scene": "330504", # Scene number
    "set_color": "33051501",  #00-Fe red, 00-Fe green, 00-Fe blue
    "set_music_mode": "3305130663",

    #"set_color":      "33050d", #00-Fe red, 00-Fe green, 00-Fe blue
    
    "set_auth":       "33b2",

    #Read Commands
    "is_on":                  "aa01", # Padding, checkbyte
    "get_brightness":         "aa04",
    "get_rgb":                "aa0501",
    "get_device_fw_version":  "aa06", 
    "hw_version":             "aa0703",
    "get_auth_key":           "aab1",

    #Multi Command
   
    #"setcolor":   "",
    #"setlevel":   "",
    "getinfo":   "818a8b",
}

async def notification_handler(sender, data):
    """Callback for handling notifications."""
    hex_data = data.hex()
    resp = None
    current_command = "Unknown"

    for command, hex_info in command_list.items():
        if hex_data.startswith(hex_info):
            current_command = command
            resp = hex_data[len(hex_info):]
            break

    if current_command != "is_on":
        print(f"Notify[2b10]: {current_command} {resp}")

def finalze_message(message):
    #Do checksum math
    check = 0
    bmessage = bytearray.fromhex(message)

    for x in bmessage:
        check ^= int(x)

    #Pad Data untill its 19 bytes lone
    return bmessage.ljust(19, b"\x00") + check.to_bytes(1, 'little')

def segemnts2num(seg_array):
    out = 0
    #Set Segments by Binary

    for x in seg_array:
        out |= (1 << x)

    return (out & 0xff7f).to_bytes(2, 'little').hex()


async def write_loop(client, timeout=2):
    """Send data to the write characteristic every 2 seconds."""
    while client.is_connected :
        try:
            await client.write_gatt_char(WRITE_UUID, finalze_message(command_list["is_on"]))
            #print(f"Sending Keep Alive payload to {WRITE_UUID}: {KEEP_ALIVE_MESSAGE}")
        except Exception as e:
            print(f"Error sending Keep Alive payload: {e}")
            break
        await asyncio.sleep(timeout)

async def write_and_read(client, data):
    try:
        full_message = finalze_message(data)
        # Write the payload to the characteristic
        print(f"TX[2b11]: {full_message.hex()}")
        await client.write_gatt_char(WRITE_UUID, full_message)

        #response = await client.read_gatt_char(WRITE_UUID)
        #print(f"RX[2b11] data: {response.hex()}")
        #return response
    except Exception as e:
        print(f"Error in write_and_read: {e}")
        #return None


async def find_and_subscribe():
    print("Scanning for BLE devices...")
    devices = await BleakScanner.discover()

    # Search for the target device
    # Search for the target device
    target_device = next((d for d in devices if TARGET_NAME in d.name), None)

    if not target_device:
        print(f"No device found with name containing '{TARGET_NAME}'")
        return

    async with BleakClient(target_device.address) as client:
        try:
            print(f"Connected to {target_device.name} ({target_device.address})")

            # Check if the notify characteristic exists
            services = client.services

            # Correct way to get characteristics
            characteristics = []
            for service in services:
                characteristics.extend(service.characteristics)

            # Check for required characteristics
            characteristic_uuids = [char.uuid for char in characteristics]
            if NOTIFY_UUID not in characteristic_uuids or WRITE_UUID not in characteristic_uuids:
                print(f"Required characteristics not found in device services.")
                return

            # Subscribe to notifications
            print(f"Subscribing to notifications on {NOTIFY_UUID}...")
            await client.start_notify(NOTIFY_UUID, notification_handler)

            # Start write loop in parallel
            print(f"Starting Keep Alive Loop to {WRITE_UUID}...")
            write_task = asyncio.create_task(write_loop(client))

            await write_and_read(client, command_list["get_brightness"])
            await write_and_read(client, command_list["set_brightness"] + "01")
            #await write_and_read(client, command_list["set_scene"] + scenes["Romantic"])
            #33 051501 6496c8 0000000000 3200 00000000002a
            #          RRGGBB            SEG
            #                            ff7f = all segments

            sleep_time = 5
            await asyncio.sleep(sleep_time)
            await asyncio.sleep(sleep_time)
            #Set all to white
            await write_and_read(client, command_list["set_color"] + "FFFFFF" + "0000000000" + "ff7f")
            #Set 0th segment of 6 LEDs to red
            await asyncio.sleep(sleep_time)
            await write_and_read(client, command_list["set_color"] + "FF0000" + "0000000000" + segemnts2num([0]))
            await asyncio.sleep(sleep_time)
            #Set 1th and last segment of 6 LEDs to green
            await write_and_read(client, command_list["set_color"] + "00FF00" + "0000000000" + segemnts2num([1,14]))
            await asyncio.sleep(sleep_time)
            

            print("Running. Press Ctrl+C to stop.")
            while True:
                await asyncio.sleep(1)

        except KeyboardInterrupt:
            print("Stopping...")
        except Exception as e:
            print(f"Error during operation: {e}")
        finally:
            print("Cleaning up...")
            if client.is_connected:
                try:
                    await client.stop_notify(NOTIFY_UUID)
                except Exception as e:
                    print(f"Error stopping notifications: {e}")
            print("Disconnected.")

# Run the async function
try:
    asyncio.run(find_and_subscribe())
except KeyboardInterrupt:
    print("Program stopped by user.")