Excellent news, despite the long line to get into HACS I was able to get merged in!
Will update the README and original post to reflect this soon
Excellent news, despite the long line to get into HACS I was able to get merged in!
Will update the README and original post to reflect this soon
I installed QuietCool BLE v0.2.7 through HACS. Home Assistant successfully discovers my QuietCool fan and creates the device and entities, but all entities remain unavailable and the fan cannot be controlled.
I enabled debug logging and restarted Home Assistant. The logs show that the Bluetooth connection succeeds, notifications are registered, and the integration sends the Login request. My controller responds to the request, but authentication fails.
Relevant log output:
QuietCool D4:8C:49:B7:18:AE: opening BLE connection
QuietCool D4:8C:49:B7:18:AE: BLE link up, authenticating
QuietCool BLE Login: registering notify
QuietCool BLE Login → {"Api": "Login", "PhoneID": "e54b349627dc5879"}
QuietCool BLE: firmware 3.9+ QQ prefix detected on Login response
QuietCool BLE Login ← {'A': 13, 'R': 'Fail', 'P': 'No'}
QuietCool D4:8C:49:B7:18:AE: login result=False
QuietCool D4:8C:49:B7:18:AE: poll failure #37, next interval 300s
The QuietCool mobile app can communicate with and control the fan normally.
It appears the BLE connection itself is working, but my controller is rejecting the Login request or PhoneID authentication method.
Is there any additional Bluetooth traffic, firmware information, or diagnostic logging I can provide to help add support for this controller?
Thanks a lot for reporting this feedback. Appreciate you tagging some logs as well. Let me take a look at what could have gone wrong and get back to you.
What’s the model number fan you have and firmware associated?
Thanks My fan model is the AFG SMT PRO-2.0 and it is running firmware version 4.1. It has 2 speeds. Home Assistant discovers the fan, but all entities remain unavailable.
Thanks Doug. We do have confirmation this is working with that model and that firmware with a previous user. Looks like the code is working as intended from what I can see.
Can you give the repairing a try?
My feeling is the fan might only support pairing via bluetooth with one device at a time if that makes sense?
Thank you i will try and let you know
I tried the re-pairing test. I force-closed the QuietCool app, deleted the QuietCool BLE integration entry, restarted Home Assistant, put the fan into Pair Mode using the physical button, and re-added the discovered fan. The integration still creates the device/entities, but they remain unavailable and the fan cannot be controlled. I did not reopen the QuietCool app during the test.
Thanks Doug! Appreciate you trying again. I just pushed out 0.2.10 (and x.8, x.9) that hopefully attempts to fix the issue. It may have been a situation where the previous working person was already paired before, and this needed a fix for newly paired people.
A couple steps once you’re ready:
If it works, you should see the entities come alive. If it still fails, the logs now capture the exact pairing responses, send those over and I’ll dig in.
Thanks again for being patient and helping test this on real hardware!
I will be unable to push more fixes until Monday, but give it a go and lemme know. Sorry for the trouble
Thank you i appreciate your help i will let you know if it works
Hi, I updated to 0.2.10 and restarted Home Assistant. I enabled debug logging, deleted the existing QuietCool BLE integration, put the fan into Pair Mode, and tried to add it again as instructed.
Unfortunately, I’m now unable to add the fan at all. Home Assistant discovers the fan, but the integration fails during the pairing/setup process and will not complete adding the device.
I captured the debug logs from the failed pairing attempt and can send them over. Please let me know if there’s anything specific in the logs you need me to look for or any additional testing you want me to do. Thanks again and no rush whenever you get to it
Have a good weekend
2026-07-08 14:17:14.135 WARNING (SyncWorker_0) [homeassistant.loader] We found a custom integration quietcool_ble which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you experience issues with Home Assistant
2026-07-08 17:03:06.485 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: _async_poll invoked
2026-07-08 17:03:06.486 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: opening BLE connection
2026-07-08 17:03:06.723 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: BLE link up, authenticating
2026-07-08 17:03:06.723 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: registering notify
2026-07-08 17:03:07.067 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login → {“Api”: “Login”, “PhoneID”: “e54b349627dc5879”}
2026-07-08 17:03:07.730 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 51517b0a092241223a0931332c0a092252223a09
2026-07-08 17:03:07.730 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE: firmware 3.9+ QQ prefix detected on Login response
2026-07-08 17:03:07.730 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: incomplete chunk, buffering (20 bytes so far)
2026-07-08 17:03:07.730 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 224661696c222c0a092250223a09224e6f220a7d
2026-07-08 17:03:07.730 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login ← {‘A’: 13, ‘R’: ‘Fail’, ‘P’: ‘No’} (buffer 40 bytes, qq=True)
2026-07-08 17:03:07.735 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: login result=False
2026-07-08 17:03:07.777 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: poll failure #37, next interval 300s
2026-07-08 17:03:07.777 DEBUG (MainThread) [custom_components.quietcool_ble.coordinator] QuietCool D4:8C:49:B7:18:AE: scheduling next poll in 300s
Thanks Doug, and no worries at all! Quick heads-up though, the logs you sent are actually from before the update (they’re timestamped July 8th, and they show the old background connection retrying rather than a fresh pairing attempt). So they’re from the original setup, not the 0.2.10 re-add.
Here’s a more reliable way to grab the ones I need:
logger:
default: warning
logs:
custom_components.quietcool_ble: debug
Heads up: the in-app “Enable debug logging” button won’t work this time, since the pairing fails there’s no integration page to click it on, and the logging has to already be running during the add.
If it’s easier, you can just send the whole log from around the moment you hit Submit, and I’ll pull out what I need.
Whenever you get a chance, really appreciate you sticking with it!
2026-07-13 08:17:18.018 INFO (MainThread) [homeassistant.components.mqtt.client] MQTT client initialized, birth message sent
2026-07-13 08:19:27.029 INFO (MainThread) [habluetooth.wrappers] D4:8C:49:B7:18:AE - ATTICFAN_d48c49b718ae: Found 1 connection path(s), preferred order: esp32-bluetooth-proxy-cd6cfc (B0:CB:D8:CD:6C:FE) (RSSI=-75) (failures=0) (in_progress=0) (slots=3/3 free) (score=-75.0)
2026-07-13 08:19:27.687 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Pair: registering notify
2026-07-13 08:19:27.952 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Pair → {“Api”: “Pair”, “PhoneID”: “e217d8b06da7b61c”}
2026-07-13 08:19:28.271 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 7b0a0922417069223a092250616972222c0a0922
2026-07-13 08:19:28.271 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Pair: incomplete chunk, buffering (20 bytes so far)
2026-07-13 08:19:28.272 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 526573756c74223a092253756363657373220a7d
2026-07-13 08:19:28.272 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Pair ← {‘Api’: ‘Pair’, ‘Result’: ‘Success’} (buffer 40 bytes, qq=False)
2026-07-13 08:19:28.274 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool pair: V1 Pair response: {‘Api’: ‘Pair’, ‘Result’: ‘Success’}
2026-07-13 08:19:28.308 INFO (MainThread) [habluetooth.wrappers] D4:8C:49:B7:18:AE - ATTICFAN_d48c49b718ae: Found 1 connection path(s), preferred order: esp32-bluetooth-proxy-cd6cfc (B0:CB:D8:CD:6C:FE) (RSSI=-75) (failures=0) (in_progress=0) (slots=2/3 free) (score=-75.0)
2026-07-13 08:19:28.574 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: registering notify
2026-07-13 08:19:29.094 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login → {“Api”: “Login”, “PhoneID”: “e217d8b06da7b61c”}
2026-07-13 08:19:29.336 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 51517b0a092241223a0931332c0a092252223a09
2026-07-13 08:19:29.336 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE: firmware 3.9+ QQ prefix detected on Login response
2026-07-13 08:19:29.336 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: incomplete chunk, buffering (20 bytes so far)
2026-07-13 08:19:29.338 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 224661696c222c0a092250223a0922596573220a
2026-07-13 08:19:29.338 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: incomplete chunk, buffering (40 bytes so far)
2026-07-13 08:19:29.338 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (1 bytes): 7d
2026-07-13 08:19:29.339 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login ← {‘A’: 13, ‘R’: ‘Fail’, ‘P’: ‘Yes’} (buffer 41 bytes, qq=True)
2026-07-13 08:19:29.386 INFO (MainThread) [habluetooth.wrappers] D4:8C:49:B7:18:AE - ATTICFAN_d48c49b718ae: Found 1 connection path(s), preferred order: esp32-bluetooth-proxy-cd6cfc (B0:CB:D8:CD:6C:FE) (RSSI=-75) (failures=0) (in_progress=0) (slots=3/3 free) (score=-75.0)
2026-07-13 08:19:29.823 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=15: registering notify
2026-07-13 08:19:30.104 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=15 → {“A”: 15}
2026-07-13 08:19:30.333 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (5 bytes): 51517b0a7d
2026-07-13 08:19:30.333 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE: firmware 3.9+ QQ prefix detected on A=15 response
2026-07-13 08:19:30.333 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=15 ← {} (buffer 5 bytes, qq=True)
2026-07-13 08:19:30.336 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool pair: V2 PairMode response: {}
2026-07-13 08:19:30.336 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=14: registering notify
2026-07-13 08:19:30.564 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=14 → {“A”: 14, “PhoneID”: “e217d8b06da7b61c”}
2026-07-13 08:19:30.722 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 51517b0a092241223a0931342c0a092252223a09
2026-07-13 08:19:30.722 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE: firmware 3.9+ QQ prefix detected on A=14 response
2026-07-13 08:19:30.723 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=14: incomplete chunk, buffering (20 bytes so far)
2026-07-13 08:19:30.723 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (8 bytes): 224661696c220a7d
2026-07-13 08:19:30.723 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE A=14 ← {‘A’: 14, ‘R’: ‘Fail’} (buffer 28 bytes, qq=True)
2026-07-13 08:19:30.723 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool pair: V2 Pair response: {‘A’: 14, ‘R’: ‘Fail’}
2026-07-13 08:19:30.760 INFO (MainThread) [habluetooth.wrappers] D4:8C:49:B7:18:AE - ATTICFAN_d48c49b718ae: Found 1 connection path(s), preferred order: esp32-bluetooth-proxy-cd6cfc (B0:CB:D8:CD:6C:FE) (RSSI=-75) (failures=0) (in_progress=0) (slots=3/3 free) (score=-75.0)
2026-07-13 08:19:31.127 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: registering notify
2026-07-13 08:19:31.331 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login → {“Api”: “Login”, “PhoneID”: “e217d8b06da7b61c”}
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 51517b0a092241223a0931332c0a092252223a09
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE: firmware 3.9+ QQ prefix detected on Login response
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: incomplete chunk, buffering (20 bytes so far)
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (20 bytes): 224661696c222c0a092250223a0922596573220a
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login: incomplete chunk, buffering (40 bytes so far)
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE notify chunk (1 bytes): 7d
2026-07-13 08:19:31.539 DEBUG (MainThread) [custom_components.quietcool_ble.api] QuietCool BLE Login ← {‘A’: 13, ‘R’: ‘Fail’, ‘P’: ‘Yes’} (buffer 41 bytes, qq=True)
2026-07-13 08:19:31.578 WARNING (MainThread) [custom_components.quietcool_ble.config_flow] Pairing not persisted for D4:8C:49:B7:18:AE after V1 and V2 attempts — the fan acknowledged pairing but did not store the PhoneID. It may need to be paired from the QuietCool app.
Thanks again i really appreciate you working with me
Thanks again Doug, and huge thanks for grabbing those logs, they were exactly what I needed. Turns out the fan was rejecting my pair command because I was sending the phone ID under the wrong field name for your firmware.
I just pushed 0.2.11 with the corrected command. Whenever you get a chance:
Full honesty: this fixes a real bug your logs caught, but I can’t promise it fully solves it, fresh pairing on the 4.x firmware hasn’t been confirmed working by anyone yet, so this might get us one step closer rather than all the way. If it still fails, your logs will now show how the fan responds to the corrected command, which is the piece I need. Just send those over.
Really appreciate you sticking with this one!
BTW, my feature complete ESPHome native firmware is almost ready and can be already tested. I’m doing the final polishing and testing before the wider announcement.
If you need the full BLE protocol documentation, see here.
This provides a native Home Assistant integration via Wi-Fi, and the mobile Smart Control app still works at the same time via BLE.
Web installer can flash it in 2 minutes. You only need to initiate pairing, either via the Smart Control app or using the Pair button on the controller, and it will replace your OEM firmware with the ESPHome version. Rollback to OEM is also easy if you find any issues (single button in HA).
It preserves existing settings and presets from the original firmware.
Amazing! Thank you for sharing this, incredible work. Let me cross check the protocol and see if there are some fixes to make
Hi kentoe,
I updated to 0.2.12 and tried pairing again. The fan acknowledges the pairing command but does not persist the Phone ID.
I saw that 0.2.12 allows entering an existing Phone ID to skip pairing and log in. I checked the QuietCool iPhone app, including the Fan Info screen, but I don’t see the Phone ID displayed anywhere.
Do you know how I can retrieve the Phone ID being used by the QuietCool iPhone app so I can enter it into the integration?
Thanks again for your help.
Hey Doug, thanks for continuing to try with me. Do you mind enabling the debug logs again with the configuration yaml.
Are you pairing using the pair button via the QuietCool app pairing button? Or are you pairing using the physical pair button on the device?
Feel free to try the opposite of what you’ve been doing. Obviously the attic is frustrating, others had success using the pair button via the app.
Feel free to share more debug logs with me. If your logger: config is still in place, just grab the log around the moment you hit Submit.
As far as the PhoneID goes I think others have found it via Bluetooth sniffing tools and the like, I don’t think it’s in the app itself. I added it from some feedback on the Github Issues tickets requests.
AFAIK, there is no way to get the pair ID from the app. It’s stored on the device in NVS and the only way to receive it is by connecting USBTTL adapter to the RX/TX/G pads and reading the raw firmware or from third-party clients that you pair with and that print their own new pair IDs in the logs or in the UI.
So if you didn’t pair any third-party clients with the controller, you can’t easily get any pair IDs.
I tried pairing again using Pair Mode from the QuietCool app and captured debug logs.
The V1 Pair command returns Success with PhoneID bbae162c7d125251.
The subsequent Login using the same PhoneID returns:
{'A': 13, 'R': 'Fail', 'P': 'Yes'}
The integration then tries the V2 flow. A=15 returns an empty response {}, and A=14 pairing returns:
{'A': 14, 'R': 'Success'}
So the fan acknowledges both V1 and V2 pairing as successful, but login with the PhoneID still fails.
QuietCool BLE Pair → {"Api": "Pair", "PhoneID": "bbae162c7d125251"}
QuietCool BLE Pair ← {'Api': 'Pair', 'Result': 'Success'}
QuietCool BLE Login → {"Api": "Login", "PhoneID": "bbae162c7d125251"}
QuietCool BLE Login ← {'A': 13, 'R': 'Fail', 'P': 'Yes'}
QuietCool BLE A=15 ← {}
QuietCool pair: V2 PairMode response: {}
QuietCool BLE A=14 → {"A": 14, "P": "bbae162c7d125251"}
QuietCool BLE A=14 ← {'A': 14, 'R': 'Success'}
QuietCool pair: V2 Pair response: {'A': 14, 'R': 'Success'}
I might be wrong without seeing your exact fan, but here’s a theory that could fit.
The thing that stands out in your log is that Pair returns Result: Success, yet Login with that same PhoneID comes straight back as Fail. If the id had really been saved, that next login should have matched it. So it looks like the fan may be accepting the Pair command and replying OK without actually writing the id to its flash.
If that’s what’s happening, it’s probably not the 50-pair limit, since that tends to answer Beyond rather than Success. A more likely explanation would be the fan’s small NVS config storage being full, so the save fails quietly while the command still reports OK. The same behavior came up in the awkaplan/quietcool-esphome thread, which was based on the same AFG SMT PRO-2.0 fan: the firmware kept accepting new pairings but stopped saving them, and logins failed until someone switched back to a pair-id that had worked before (Changes to use the new JSON API in newer quietcool firmware by kentonr · Pull Request #2 · awkaplan/quietcool-esphome · GitHub).
An easy way to check whether it’s the fan and not Home Assistant: try adding a second phone in the QuietCool app. If that also fails to pair, it points more at the fan’s storage than anything on the HA side.
One thing to note about the fix in that thread: it only worked because they still had a pair-id that had registered before. The QuietCool app doesn’t show the id it uses, and reading the ones already stored on the fan means dumping its flash over USB-TTL, so unless you kept a working id from an earlier Home Assistant setup (that one lives in HA’s own config, not on the fan), that route isn’t really practical.
So the realistic option is a factory reset, which frees up room anyway. The pair-ids themselves are tiny; what actually fills that storage is the rolling sensor logs and the duplicated preset data the firmware keeps, and a reset clears all of it, which is what makes space for a new pairing to save. In the QuietCool app that’s Fan Settings > Reset to Factory Default, or you can hold the Test/Speed button on the unit for a few seconds, though that varies by model so the app route is easier. It removes all stored phones, so you’d re-pair the QuietCool app afterwards, and resets your settings.
There’s some background on why this storage tends to fill up here, in case it’s useful: GitHub - CrazyCoder/quietcool-esphome-native: ESPHome replacement firmware for the QuietCool IT-AF-SMT Smart Attic Fan Control - native Home Assistant integration, Wi-Fi + BLE (stock app keeps working), web UI/REST, autonomous Smart Mode, reversible back to stock · GitHub
Or if you are brave enough, you can try the complete native ESPHome firmware from my repository (it cleans NVS bloat on the first start but keeps your existing pair IDs). Then you can try the pair option in my firmware, and it will store your new pair ID. Then you can revert to stock per the docs if you want and the pair ID will persist. Use on your own risk =)