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
- 01: is_on
- 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.")