Skip to content

Add SONOFF TP-WGZBA thermostat support - #5201

Open
viys wants to merge 22 commits into
zigpy:devfrom
viys:tp-wgbza
Open

viys wants to merge 22 commits into
zigpy:devfrom
viys:tp-wgbza

Conversation

@viys

@viys viys commented Jul 24, 2026 •

Copy link
Copy Markdown

Proposed change

Add support for the SONOFF TP-WGZBA thermostat.

The quirk replaces the device thermostat and manufacturer-specific private cluster, exposes the device controls and diagnostics as ZHA entities, and encodes the device-specific weekly schedule and temporary-mode commands. It also includes behavior tests for entity creation, system-mode handling, schedule parsing/encoding, and write failure handling.

Additional information

The device uses manufacturer code 0x1286 and private cluster 0xFC11.

The imports include compatibility fallbacks for older Home Assistant/ZHA releases. The primary target is the current quirk builder API, while the fallback was verified with Home Assistant 2026.3.3.

Full-suite testing completed with two unrelated pre-existing Tuya time-payload failures:

  • tests/test_tuya.py::test_moes[MoesHY368_Type1]
  • tests/test_tuya_mcu.py::test_tuya_mcu_set_time[TuyaDoubleSwitchDimmer]

Device diagnostics

Diagnostic dump provided by the reporter:

zha-01KY9HWXPG21KE9QBDGQ6YABRW-SONOFF TP-WGZBA-98d70e73f61912a31a5ea9db77861c38.json

The dump was collected from Home Assistant 2026.3.3 with ZHA 1.0.2. It reports quirk_applied: true, issues: [], and the expected thermostat endpoint 0x0201 plus private cluster 0xFC11. The original JSON should be attached to this section through the GitHub PR page.

Checklist

  • The changes are tested and work correctly
  • pre-commit checks pass / the code has been formatted using Black
  • Tests have been added to verify that the new code works
  • Device diagnostics data has been attached

@codecov

codecov Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.02%. Comparing base (862124c) to head (a699644).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #5201      +/-   ##
==========================================
+ Coverage   93.99%   94.02%   +0.02%     
==========================================
  Files         448      449       +1     
  Lines       15547    16816    +1269     
==========================================
+ Hits        14614    15811    +1197     
- Misses        933     1005      +72     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Cover payload codecs, diagnostic mirrors, virtual reads and writes, and UI command dispatch to keep project coverage above the CI baseline.
@zigpy-review-bot zigpy-review-bot added bot: needs changes PR needs changes per LLM bot: 2.0 migration done PR is on the ZHA/quirks 2.0.0 API (migrated or written for 2.0) per LLM new quirk Adds support for a new device bot: needs maintainer PR/issue needs a maintainer decision (questionable approach, policy) per LLM bot: huge PR PR is quite large and should possibly be split up per LLM labels Jul 24, 2026
@viys

viys commented Jul 29, 2026 •

Copy link
Copy Markdown
Author

@zigpy-review-bot Thanks for pointing this out. I reviewed zigpy/zha#845 and agree that this is an upstream diagnostics tooling issue: ZhaJsonEncoder currently cannot serialize bytes in quirk entity info.

The bytes value in command_args is intentional because it is the raw payload required by the TP-WGZBA temporary-mode command. Changing it to an integer would risk changing the actual ZCL command payload. I propose keeping the quirk payload unchanged and addressing the ZhaJsonEncoder fix upstream in zigpy/zha#845. Once that fix is available, I will rerun the diagnostics import/snapshot tests for this PR.

Please let me know if you would prefer a temporary JSON-safe metadata workaround in this PR.

viys added 3 commits August 21, 2026 10:57
Handle switch-only bind and unbind payloads while preserving cached state when vendor writes fail. Split linkage packing to satisfy complexity checks and correct ReportingConfig typing.
@lgladdy

lgladdy commented Oct 4, 2026

Copy link
Copy Markdown

Just wanted to add that I've been using this as a custom quirk since I installed my two TP-WGZBA's last month, and it's all working well.

@philippop

Copy link
Copy Markdown

@viys thanks for this quirk, it works well here on 7 TP-WGZBA units (ZHA, loaded as a custom quirk). While testing I found a firmware behaviour I would like to report, since you are closest to the device.

TP-WGZBA (fw 1.0.4, date code 20260814): reported temperature steps up 0.5–1.6 °C when the relay switches off

While the heating relay is on, local_temperature reads low. Within 1–5 s of the relay switching off it jumps up, then decays over 30+ min. The size grows with relay on-time: +0.1 °C after 8 min, +0.5 after 22 min, +1.0 after 46 min, +1.2 after 66 min, +1.6 after 12.5 h.

Measured against an SNZB-02P mounted directly beside the thermostat (66 min heating run, values read from the device once a minute, every 10 s around relay-off):

Phase TP-WGZBA SNZB-02P Difference
Before heating 21.2 21.5 −0.3
End of heating 21.0 21.6 −0.6
10 s after relay-off 22.2 21.6 +0.6
30 min after 21.7 21.6 +0.1

The reference sensor did not move, and humidity was flat. So far I have seen the step on 2 of the 7 units; the other 5 have not had a long heating run yet.

It looks like a self-heating correction that grows with relay on-time and is removed in one step at relay-off. While heating, the device then regulates on a value that is about 0.3 °C lower than its own idle baseline, and right after relay-off it reads about 0.9 °C higher. With a ±0.2 °C control threshold that is larger than the control band.

In external temperature mode (value fed from the SNZB-02P) the reported temperature follows the fed value exactly and there is no step at relay-off (30 min heating run). So that is a workaround, but it needs one extra sensor per room.

Requests for the firmware:

  1. Make the correction adjustable through an attribute, including 0 to switch it off.
  2. Remove it gradually after relay-off, matching the real cooling of the housing, instead of in one step.
  3. Expose the uncorrected sensor value as a read-only attribute.

I can provide the logs at 10 s resolution. If this PR is the wrong place, tell me where SONOFF prefers such reports and I will move it.

@viys

viys commented Oct 8, 2026 •

Copy link
Copy Markdown
Author

@philippop thanks for the detailed report and the side-by-side measurements, very helpful. This is related to the device's internal temperature compensation for heat generated while the relay is active. We've shared your findings with our firmware team and plan to optimize the compensation strategy in the next update so the transition after relay-off is smoother. Your suggestions have also been forwarded for evaluation.

@jeverley

Copy link
Copy Markdown
Contributor

Thanks for the quirk @viys.

I've observed that with my device on firmware 0x00001004 (4100) the setpoint drops to exactly 5C at 00:00 local every night, even though the schedule read back from the device has 16C for the 00:00 slot.
No overrides, schedule group unchanged, and daytime transitions fire correctly.

The same behaviour is reported on Zigbee2MQTT (Koenkk/zigbee2mqtt#33317), so I suspect a possible a firmware bug and not the quirk.

@viys

viys commented Oct 10, 2026

Copy link
Copy Markdown
Author

@jeverley Thanks for the report. We've confirmed this is a firmware bug: at midnight, the end of the previous day's last slot can override the new 00:00 slot, so the setpoint falls back to 5°C until the next transition. We'll fix it as soon as possible.

@viys

viys commented Oct 12, 2026

Copy link
Copy Markdown
Author

@jeverley @philippop Quick update: both issues you reported are fixed in firmware 1.0.5, which we're planning to release this week.

  • @philippop: the temperature compensation while the relay is active has been reworked, so the reported temperature shouldn't jump 0.5–1.6 °C when the relay switches on.
  • @jeverley: the setpoint no longer falls back to 5 °C at midnight. The 00:00 slot now holds its scheduled temperature.

Thanks to you both for the detailed reports and measurements, they made these a lot easier to track down.

@philippop

Copy link
Copy Markdown

@viys thank you, that was fast. I did not expect a fix within a week.

I will repeat the same measurement on 1.0.5 (SNZB-02P beside the thermostat, 60 min heating, 10 s sampling around relay-off) and post the before/after numbers here.

One question: how will 1.0.5 be distributed for ZHA? The public index at zigbee-ota.sonoff.tech/releases/upgrade.json had no TP-WGZBA entry when I last checked. Will the image be added there, or is it only available through a SONOFF bridge?

@viys

viys commented Oct 12, 2026

Copy link
Copy Markdown
Author

@philippop Thanks a lot for your support! A SONOFF bridge isn't the only way to get this OTA update. You can also update over Bluetooth with the eWeLink app:

  1. In the eWeLink app, go to Profile -> Pilot features -> Bluetooth OTA Update and search for the device.
  2. Turn on the thermostat's Bluetooth advertising, either:
    • from ZHA or Z2M, or
    • locally on the device: make sure it's not in Off mode, wake the screen, then press the button once and press and hold it for 3 seconds. This turns on Bluetooth advertising temporarily.

We'll also submit the 1.0.5 OTA image to both ZHA and Z2M this week.

@jeverley

Copy link
Copy Markdown
Contributor

Hi @viys , I've uncovered a bug in the current quirk version's device work mode, issue outlined below:

Device work mode shows Manual after any system_mode read, while the device stays on Schedule

Reading Thermostat system_mode (Manage Zigbee device > Clusters > Thermostat > system_mode > Read attribute) switches Device work mode and its state sensor from Schedule to Manual straight away. The thermostat itself stays in Schedule mode and keeps following its schedule.

The same happens 6 to 15 seconds after every HA start or ZHA reload, when ZHA reads the climate attributes. I've seen it 17 times on two thermostats over four days, always with no user or automation context in the logbook.

Cause: system_mode is remapped twice on the read path.

  1. SonoffTPWGZBAThermostatCluster.read_attributes_raw maps the device's Schedule (0x01) to HA Heat (0x04) in the read record.
  2. zigpy's read_attributes caches that record value through _update_attribute.
  3. _update_attribute treats the value as the raw device mode and mirrors TPWGZBASystemMode(4) (Manual) into the private cluster, so device_work_mode becomes Manual.

Reports from the device go straight to _update_attribute with the raw value, so only reads are affected.

Suggested fix: convert in one place only. For example, drop the system_mode remap from read_attributes_raw and let _update_attribute do the single conversion it already does for the cache.

@viys

viys commented Oct 20, 2026 •

Copy link
Copy Markdown
Author

@jeverley Thanks for reporting this. I’ve fixed the duplicate system_mode conversion in the quirk. Reading system_mode should now leave Device work mode unchanged. The fix has been pushed.

@cooki371

cooki371 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Hi, and thanks for working on this quirk! I tried it on my TP-WGZBA (firmware 0x00001005) and most of it works nicely: display brightness, frost protection, the weekly schedule (apply and fetch both round-trip) and the switches I tried.

Three things don't work for me, though. The device rejects the writes with Status.UNSUPPORTED_ATTRIBUTE (134). This is with commit 36d73e3 as a custom quirk, paired via ZHA, work mode Manual, Home Assistant Core 2026.10.4 (Home Assistant OS 18.3), ZHA as bundled with Core.

  1. default_display_page (0x002A on cluster 0xFC11)

    • The select stays unknown. Writing "Ambient Temperature" fails with:
      Failed to write attribute default_display_page=<DefaultDisplayPage.Ambient_Temperature: 0>
  2. Valve anti-stuck: day, start time and working duration

    • The entities stay unknown. Writing any of the three fails with:
      Failed to write attribute valve_anti_stuck_schedule=unknown
  3. Holiday mode switch

    • Turning it on fails with:
      Failed to write attribute holiday_schedule=unknown
    • The holiday duration and target temperature numbers accept values, but the mode itself can't be activated.

Nothing changed on the device after the failed writes (holiday mode stays off, work mode still Manual).

One more thing from the startup log: radar_occupancy_timeout can't be created because my unit has no cluster 1030 (0x0406) on endpoint 1.

Additional question:
Is there also an option for the relay contact type to set it to always open to basically deactivate it. I don't need the boiler relay to work right now, so this would turn the clicking noise a bit down.

Happy to test anything else or provide more logs if that helps.

@viys

viys commented Oct 30, 2026

Copy link
Copy Markdown
Author

@cooki371 Thanks for testing and reporting this. The display default page, valve anti-stuck schedule, and holiday mode controls are newly added features. They will be supported in firmware version 1.0.6 (0x00001006), which has not been released yet and is planned for a future release.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot: huge PR PR is quite large and should possibly be split up per LLM bot: needs changes PR needs changes per LLM bot: needs maintainer PR/issue needs a maintainer decision (questionable approach, policy) per LLM bot: 2.0 migration done PR is on the ZHA/quirks 2.0.0 API (migrated or written for 2.0) per LLM new quirk Adds support for a new device

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants