#!/usr/bin/env python3
"""
Hoymiles WFBLE DTU - BLE WiFi provisioning
Enumerates GATT services, then writes SSID+password to the WiFi config characteristic.
"""
import asyncio
from bleak import BleakClient

DTU_MAC = "24:19:72:61:9D:62"
SSID = "AhoySmartHome"
PASSWORD = "106granada"

WRITE_CHAR   = "53300001-0023-4bd4-bbd5-a6920e4c5653"
NOTIFY_CHAR1 = "53300004-0023-4bd4-bbd5-a6920e4c5653"
NOTIFY_CHAR2 = "53300005-0023-4bd4-bbd5-a6920e4c5653"

# Candidate packet formats to try
def payloads(ssid, pw):
    ssid_b, pw_b = ssid.encode(), pw.encode()
    return [
        # Format 1: cmd=0x28, len+ssid, len+pw  (S-Miles style)
        ("cmd=0x28", bytes([0x28, len(ssid_b)]) + ssid_b + bytes([len(pw_b)]) + pw_b),
        # Format 2: len+ssid+len+pw no command byte
        ("no-cmd",   bytes([len(ssid_b)]) + ssid_b + bytes([len(pw_b)]) + pw_b),
        # Format 3: ssid\x00pw null-separated
        ("null-sep", ssid_b + b'\x00' + pw_b),
        # Format 4: cmd=0x01 (common IoT config command)
        ("cmd=0x01", bytes([0x01, len(ssid_b)]) + ssid_b + bytes([len(pw_b)]) + pw_b),
    ]

async def main():
    responses = []

    def handler(sender, data):
        print(f"  [NOTIFY from {sender}] hex={data.hex()} str={data}")
        responses.append(data)

    print(f"[*] Connecting to {DTU_MAC}...")
    async with BleakClient(DTU_MAC, timeout=15.0) as client:
        print(f"[+] Connected")

        # Dump all descriptors too
        for service in client.services:
            for char in service.characteristics:
                for desc in char.descriptors:
                    print(f"  Descriptor: {char.uuid} -> {desc.uuid} ({desc.handle})")

        await client.start_notify(NOTIFY_CHAR1, handler)
        await client.start_notify(NOTIFY_CHAR2, handler)
        print(f"[*] Subscribed to notify chars\n")

        # Read current auth token
        auth_token = await client.read_gatt_char(WRITE_CHAR)
        print(f"[*] Auth token: {auth_token.hex()} | {auth_token}\n")

        # Step 1: Write auth token back (authenticate)
        print(f"[*] Step 1: Writing auth token back to authenticate...")
        await client.write_gatt_char(WRITE_CHAR, auth_token, response=True)
        await asyncio.sleep(2)
        print(f"    Responses: {[r.hex() for r in responses]}\n")
        responses.clear()

        # Step 2: Now try WiFi config
        ssid_b, pw_b = SSID.encode(), PASSWORD.encode()
        wifi_payload = bytes([0x28, len(ssid_b)]) + ssid_b + bytes([len(pw_b)]) + pw_b
        print(f"[*] Step 2: Writing WiFi config {wifi_payload.hex()}")
        await client.write_gatt_char(WRITE_CHAR, wifi_payload, response=True)
        await asyncio.sleep(5)
        print(f"    Responses: {[r.hex() for r in responses]}")

asyncio.run(main())
