Add full support for Tuya BAC-002-ALZB (schedule_text, improved off behavior, calibration -9/+9)#11004
Add full support for Tuya BAC-002-ALZB (schedule_text, improved off behavior, calibration -9/+9)#11004Koenkk merged 8 commits intoKoenkk:masterfrom
Conversation
|
Could you take a look at the merge conflicts? |
t add src/devices/tuya.ts git rebase --continue
|
Thanks! |
|
Thanks for the review! |
|
After this update, I can't turn off my BAC device. It changes to cool instead of off. |
|
Was this tested in a 2-pipe cooling only configuration as well? Confirming the same. These changes make Zigbee2MQTT unable to turn off the airconditioning entirely. @Koenkk Can this be reverted until the author fixes these changes? |
|
Mine is also a 2-pipe, for both hot and cold use. |
|
Reverted in #11209
|
|
I’ve already identified and fixed the issue. I’ll open a PR as soon as I can. In the meantime, I’m sharing the external converter in case anyone wants to use it until the next release. I’ve tested the external converter myself and also shared it with a few other users, and so far it appears to be working well. I’ve also updated the deadzone range to go from 0 to 5; previously it started at 1. The file is provided in .mjs format so it can be merged directly later on. |
|
Thanks for the detailed review, I’ll take a look at it. The reason for the power-on before power-off is that on the BAC-002 units I tested, the device would not turn off when sending an OFF command directly, as it did not “wake up” first. Regarding OFF handling, it’s a bit tricky because by default ON/OFF is independent from the system mode, and including it inside the mode causes issues. In the previous external converter I made, the main complaints were precisely that ON/OFF was independent from the mode, matching the original Tuya behavior. Just to confirm: when you mention deadzone, you are referring to the temperature calibration/compensation, correct? For reference, I’ve been using calibration values in the -9 to +9 range on my devices for about a year without observing any issues or unexpected behavior. If your converter works well for everyone, we can base the merge on your implementation. Thanks a lot. |
Based on your feedback, I've added a device specific setting to do this: "Wake before power off".
That's why the device-specific setting is there. If you want a separate on/off switch entity, you flip that setting in Z2M.
Let's merge this all together and re-submit the pull request. All pre-existing use-cases are now covered. Attaching the updated version with a device-setting for "Wake before poweroff": |
|
I can confirm that the @robvanoostenrijk version, works. |
Thanks a lot for the work, I really appreciate the effort you’ve put into this 👍 I’ve been testing this myself and together with a few other users. Overall, it works well, but we’ve noticed a couple of practical issues. The main one is that the wake_before_power_off on/off switch does not appear in the UI. This means users would need to touch YAML or rely on a modified external converter to control it, which could be a problem for many users. Based on your feedback and your external implementation, I tried an alternative approach: instead of powering on before powering off, I’m now sending two consecutive power-off commands. With this approach, the thermostat consistently turns off, and it does so without the LED flashing that was mentioned in earlier comments. Another issue I personally ran into with this converter was that, once the device was turned off, it would not always turn back on. To address this, I added a small piece of logic where, if the device is off, it briefly powers on first and then switches to the requested mode. With this change in place, the behavior seems stable. In any case, if the device is off and you set any system mode, the LED will turn on anyway, so I don’t think this causes any real negative side effects. If you can confirm that this external behaves correctly for you as well, I think we’re in a good position to move forward and use this as the final solution. |
|
Hmm, thats weird. The moment I click either
Obviously I'd like to support your scenario if possible, so lets do some debugging. The switch should definitely work as part of default Z2M functionality. Note when changing the option in Z2M (at least in Windfront), you'll see a green pop-up bottom right with "Options (####)" |
|
After extensive testing on six BAC-002 thermostats, I believe the root cause of the unreliable on/off behavior is related to 2-pipe Cooling & Heating configurations, which are neither classic 4-pipe setups nor cooling-only devices. During testing, I also encountered inconsistent behavior in my own Zigbee2MQTT setup (after a clean reinstall, the wake_before_power_off option suddenly became selectable again), which suggests part of the issue may be related to state desynchronization rather than pure converter logic. What this version does Keeps the default behavior unchanged when wake_before_power_off is disabled Adds an optional wake-before logic when wake_before_power_off is enabled: Ensures the device always powers off reliably Ensures the device always powers on reliably This includes intentionally duplicated DP1 commands in some transitions Although duplicating commands may look sub-optimal, it is currently the only solution that works consistently across all tested devices in this specific configuration. Why the duplicated calls exist Some BAC-002 units ignore OFF commands when internally asleep Some units ignore mode changes if DP1 was not asserted shortly before The duplicated calls guarantee a state transition edge, which appears to be required by certain firmwares This issue has been reported by multiple users (not only myself), mainly in Home Assistant + Zigbee2MQTT setups, including reports in Spanish community forums. Renaming clarification Renamed “Cooling and Heating 4-pipes” to “Cooling and Heating” This avoids confusion for users with 2-pipe Cooling & Heating installations Functionality remains identical; this is a label clarification only Summary Default behavior: unchanged Optional behavior: improves reliability No breaking changes No impact unless the option is explicitly enabled At this point, the exact firmware-level cause is still unclear, but this solution has proven to be stable and repeatable across all my tested devices. I would really appreciate your feedback on this approach. |
|
Ok, I took mod_6 and created a branch for it here: Principally the same, just some cosmetic updates to align the options with what you explained above.
Let me know if this resolves all of the mentioned issues and we can resubmit this upstream. As external converter: |
|
Thanks, I’ve tested it and for me it looks perfect 👍 The only remaining thing I’d like to ask is about the Deadzone minimum value. Right now it’s set to 1, but it would be great if the minimum could be 0 instead. There are quite a few users requesting a deadzone value of 0, and in fact on newer devices currently being shipped, the minimum deadzone is already 0 and it works fine in practice. Anyone who prefers a deadzone of 1 can still explicitly set it to 1, but allowing 0 as the minimum gives more flexibility and matches current device behavior. line 44 Other than that, everything looks good to me. |
|
I've updated the Interesting because the manual states |
|
Good morning, I just wanted to add that I have always used a deadzone value of 0 since I’ve had this device, and I’ve never experienced any issues with it. The only real limitation, in my opinion, is that it must not be a decimal value, which is another request some users have mentioned — but integer values work perfectly fine. On another note, while reviewing the code in more depth, I noticed something I wanted to ask about. On line 66 you are checking meta.options?.… in the if, but on lines 72, 85 and 92 the if conditions don’t use meta.options., only use options? Shouldn’t this be consistently using meta.options in all cases? I just wanted to double-check in case this was intentional. |
|
Well spotted! The function signature for I've now updated (and tested) this appropriately. In the previous build, the power transition fix should not have worked for your device though, because the option would never evaluate to Updated external converter attached. |
|
I’m going to test this new version now. What happened on my side is that at first it seemed to work fine, but as I kept testing I noticed that sometimes it failed, almost as if the Wake before power off option was disabled. That inconsistency is what made me look into the code more closely and suspect this part in the first place. I assume this should now work consistently, but I’ll run some tests to confirm. Thanks a lot for the quick update and the explanation. |
|
I’ve tested it quite a bit now, and it behaves consistently and correctly. From my side it looks ready to be submitted upstream and included in 2.7.3. |
Thank you for testing. PR created: |






What does this PR do?
This PR adds full native support for the Tuya BAC-002-ALZB FCU thermostat.
Improvements included
Adds single-line unified weekly schedule_text (12 segments), readable and writable from Home Assistant or MQTT.
Fixes the long-standing issue where the thermostat would not turn off on first attempt.
Adds correct temperature calibration range −9 to +9°C, useful when using an external sensor.
Uses the modernExtend tuyaBase with forceTimeUpdates to ensure proper time sync.
Provides stable system_mode → Tuya DP mapping and state consolidation.
Adds whiteLabel and full fingerprint support for _TZE200_dzuqwsyg and _TZE204_dzuqwsyg.
Compatibility requirements
Works on Zigbee2MQTT 2.7.1 or newer (uses modernExtend and unified Tuya datapoints).
Notes
Tested extensively on the actual device.
Provides schedule parsing identical to older versions but in a cleaner, safer, more automation-friendly format.