AC Infinity Bluetooth 69 PRO controllers SUCCESSFUL

It was already on at speed 4, but went to speed 8 after running the script. I hadn’t noticed because the fan is in another room. Very cool! Looking through some of your earlier posts is the “8” in the command (send “write “0xa5 0x00 0x00 0x06 0x00 0x67 0x02 0xd8 0x00 0x03 0x10 0x01 0x02 0x12 0x01 0x08 0x2b 0x3b”\r”) setting the speed?

that is correct
a = 10

also the portion before that 0x02 0x12 , change to 0x01 0x11 to turn off

Awesome, I think I can fumble around now and poke at it. Thanks again for your time. And, Infinity if you are listening just make this easier!

1 Like

i cracked the advertised packet data yesterday!

#!/usr/bin/expect -f

set prompt "#"
set address 34:85:18:6a:52:52
spawn bluetoothctl
expect -re $prompt
send "connect $address\r"
expect "Connection successful"
send "menu gatt\r"
send "select-attribute /org/bluez/hci0/dev_34_85_18_6A_52_52/service002e/char0032\r"
send "notify on\r" 
sleep 1
send "exit\r"
send "exit\r"
send "quit\r"
expect eof

/config/./FILENAME.sh | grep 1e | awk ‘NR==1 { print"0x" $15 $16 }’ | xargs printf “%d\n” - vpd

/config/./FILENAME.sh | grep 1e | awk ‘NR==1 { print"0x" $13 $14 }’ | xargs printf “%d\n” - humidity

/config/./FILENAME.sh | grep 1e | awk ‘NR==1 { print"0x" $11 $12 }’ | xargs printf “%d\n” - Temperature in C

/config/./FILENAME.sh | grep 1e | awk 'NR==1 { print "0x" $11 $12 }' | xargs printf "%d\n" | xargs -n 1 bash -c echo $(($1 * 9/5 + 3200 ))' args | xargs -n 1 bash -c echo $(($1 /100 ))' args” - Temperature in Fahrenheit

1 Like

Nice work. I’d like to get this working as well, but I don’t have the same attribute you used (…service002e/char0032). I tried all of the attributes from my list (see my previous post), but nothing worked. I have the bluetooth controller, are you using the wifi controller? Any ideas on how to investigate?

check this thread for the 67

or heres the code for the 67, it doesnt give vpd but ou can calculate for that with temp and humidity

#!/usr/bin/expect -f

set prompt "#"
set address DA:6E:91:88:26:2C 
spawn bluetoothctl
expect -re $prompt
send "connect $address\r"
expect "Connection successful"
send "list-attributes\r"
send "menu gatt\r"
send "select-attribute /org/bluez/hci0/dev_DA_6E_91_88_26_2C/service001b/char001e\r"
send "notify on\r"
send "select-attribute /org/bluez/hci0/dev_DA_6E_91_88_26_2C/service001b/char001c\r"
send "write \"0xa5 0x00 0x00 0x03 0x03 0x49 0x79 0xd7 0x00 0x01 0x02 0x03 0x20 0x78 0xe9\"\r" 
send "select-attribute /org/bluez/hci0/dev_DA_6E_91_88_26_2C/service001b/char001e\r"
send "read\r"
sleep 2
send "quit\r"
expect eof

Temp in C

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n"

this will show temp in F from terminal

/config/./FILENAME.sh | grep a5 | awk 'NR==2 { print "0x" $16 $17 }' | xargs printf "%d\n" |xargs -n 1 bash -c 'echo $(($1 * 9/5 + 3200 ))' args | xargs -n 1 bash -c 'echo $(($1 /100))' args

Humidity

/config/./FILENAME.sh | grep '20 01\| a5' | awk 'NR==1 {print "0x" $18};NR==2 {print $3}' | awk 'NR%2{printf "%s",$0;next;}1' | xargs printf "%d\n"

call these proof of concept strings…

That works for me, thanks. You may have already figured this out, but the reported speed appears to be in the 5th data pair of the second line. I set the speed to 0, 1 ,2 and this was the data.

a5 10 00 11 03 49 50 8e 00 01 02 05 02 08 a2 0c …IP…
b0 03 05 00 00 00 00 00 20 01 00 ed bb

a5 10 00 11 03 49 50 8e 00 01 02 05 02 08 a3 0c …IP…
b2 03 05 00 01 00 00 00 20 01 00 b0 0a

a5 10 00 11 03 49 50 8e 00 01 02 05 02 08 a6 0c …IP…
b4 03 05 00 02 00 00 00 20 01 00 d9 b2

Now I just need to extract it with one of your fancy command line proof of concepts.

1 Like

yea im working with it in python

edit ->stoned…i did not see it had sped registered. that may be nice for when its called to support set speed

import asyncio
from bleak import *
from bleak import BleakClient
from crccheck.crc import Crc16CcittFalse

address = "34:85:18:6a:52:52"

async def main(address):
    async with BleakClient(address) as client:

        aciuni = bytes.fromhex("a5000008013bb191")
        header = bytes.fromhex("00031001")
        power = bytes.fromhex("0111")
        direction = bytes.fromhex("01")
        velocity = bytes.fromhex("04") 
        params = bytes.fromhex("ff01")
        
        crcinst = Crc16CcittFalse()
        crcinst.process(header)
        crcinst.process(power)
        crcinst.process(direction)
        crcinst.process(velocity)
        crcinst.process(params)
        model_number = await client.write_gatt_char("70d51001-2c7f-4e75-ae8a-d758951ce4e0",aciuni + header + power + direction +  velocity + params + crcinst.finalbytes())

asyncio.run(main(address))

get advertised data

import sys
import time
import platform
import asyncio
import logging
from bleak import BleakClient

logger = logging.getLogger(__name__)

ADDRESS = (
    "34:85:18:6a:52:52"
    if platform.system() != "4GRDH"
    else "70d51002-2c7f-4e75-ae8a-d758951ce4e0"
)

CHARACTERISTIC_UUID = "70d51002-2c7f-4e75-ae8a-d758951ce4e0"


async def run_ble_client(address: str, char_uuid: str, queue: asyncio.Queue):
    async def callback_handler(sender, data):
        await queue.put((time.time(), data))
        item = await client.read_gatt_char(char_uuid)
        print("Read:", item)

    async with BleakClient(address) as client:
        logger.info(f"Connected: {client.is_connected}")
        await client.start_notify(char_uuid, callback_handler)
        await asyncio.sleep(.5)
        await client.stop_notify(char_uuid)
        await queue.put((time.time(), None))

async def run_queue_consumer(queue: asyncio.Queue):
    while True:
        epoch, data = await queue.get()
        if data is None:
            logger.info(
                "Got message from client about disconnection. Exiting consumer loop..."
            )
            break
        else:
            logger.info(f"Received callback data via async queue at {epoch}: {data}")

async def main(address: str, char_uuid: str):
    queue = asyncio.Queue()
    client_task = run_ble_client(address, char_uuid, queue)
    consumer_task = run_queue_consumer(queue)
    await asyncio.gather(client_task, consumer_task)
    logger.info("Main method done.")

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    asyncio.run(
        main(
            sys.argv[1] if len(sys.argv) > 1 else ADDRESS,
            sys.argv[2] if len(sys.argv) > 2 else CHARACTERISTIC_UUID,
        )
    )

Sorry for such a simple question but the 69 pro controller is supporting both Bluetooth and wireless. Is the MAC address for the 802.11 wireless or the BT? If BT, how do you obtain it ?

hi, you can find out googling the command:
gatttool - tool for Bluetooth Low Energy device

1 Like

Thank you very much. Do you install it on the Home Assistant OS?

Yes, I just started to have a look yesterday, so I will try first the shell_command in an automation, then probably will try the python library as I’m not too sure how to do it yet. good luck!

Oh also you can find the mac with a bluetooth scanner app on your phone.

Would you be able to ELI5 how to use this to populate the sensor data into HA? I don’t really care if I can control the controller since it’s just running through an automation itself, I’d just like to be able to get all of the data from the controller onto my dashboard

For those of you still following this thread, I took the information provided and have built a custom integration for AC Infinity Controllers. It’s working with my Controller 69 Pro, but should also work for a Controller 67.

I’m at the point where I could use some beta testers. You can add this as a custom repository in HACS to make sure you get updates and I push features and fixes:

5 Likes

I just tried and got

2023-07-15 14:07:19.754 ERROR (MainThread) [custom_components.ac_infinity.config_flow] Unexpected error
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/ac_infinity_ble/device.py", line 159, in update
    await events[CallbackType.NOTIFICATION].wait()
  File "/usr/local/lib/python3.11/asyncio/locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/config/custom_components/ac_infinity/config_flow.py", line 66, in async_step_user
    await controller.update()
  File "/usr/local/lib/python3.11/site-packages/ac_infinity_ble/device.py", line 158, in update
    async with async_timeout.timeout(5):
  File "/usr/local/lib/python3.11/site-packages/async_timeout/__init__.py", line 129, in __aexit__
    self._do_exit(exc_type)
  File "/usr/local/lib/python3.11/site-packages/async_timeout/__init__.py", line 212, in _do_exit
    raise asyncio.TimeoutError
TimeoutError

That’s the same error @jayrama got - is this a controller 69 Pro or something else?

I just released v1.0.2 on HACS which should hopefully fix some issues. If it’s not showing, navigate to the integration and click the 3 dots on the top to choose “redownload”

1 Like

It worked perfect ! Thank so much for doing this

You’re a hero @hunterjm !
image

Installed through HACS, restated HA, device was found instantly!
Powered on the second unit, instantly picked up as well. So grateful for your work there!

I do have three requests though:
1/ An icon would be cool
2/ Even a small blurb in the README just telling people to add it as a custom repository, just for the real newbies it’d probably help
3/ Somewhere we can donate to you and buy you a beverage or similar just to say thanks for the integration!