Home Assistant custom integration that polls a Tuya Wi-Fi ultrasonic tank sensor through the Tuya Cloud OpenAPI. It is aimed at the ME201W level sensor (category ywcgq). The stock Home Assistant tuya integration maps liquid level, depth, and state for that product, and it does not expose battery.
This integration:
- Reads the same live datapoints already used in Home Assistant
- Adds battery percentage (DP 5,
battery_percentage) - Lets you slow the poll down, or pause it, without reinstalling
Each poll is a request to Tuya Cloud. It does not change how often the sensor wakes its Wi-Fi radio. That schedule belongs to the device firmware.
Source of truth: github.com/HammondAutomationHub/HomeAssistant_TuyaWaterTank
Requires Home Assistant 2025.1.0 or newer. Configure it in the UI. Do not add it to configuration.yaml.
- In Home Assistant, open HACS → Integrations.
- Open the three-dot menu and choose Custom repositories.
- Repository:
https://github.com/HammondAutomationHub/HomeAssistant_TuyaWaterTank - Category: Integration. Add it.
- In the HACS integration list, find Tuya Water Tank and choose Download.
- Restart Home Assistant.
- Go to Settings → Devices & services → Add integration and choose Tuya Water Tank.
The repository layout is the normal HACS integration layout: hacs.json at the root, content_in_root set to false, and the code in custom_components/tuya_water_tank/. HACS copies that folder into Home Assistant's custom_components.
After the custom repository is added, HACS can update the integration from GitHub.
- With no GitHub Release, HACS tracks the default branch
main. A new commit onmainshows up as an update. The version HACS displays is the start of the commit hash. - For a readable version, publish a GitHub Release whose tag matches
versionincustom_components/tuya_water_tank/manifest.json(1.0.0). HACS then uses that tag as the version, and the next release shows as an update. - Do not turn on
content_in_root. The files HACS installs are only the integration folder. - Download the update in HACS, then restart Home Assistant.
Copy custom_components/tuya_water_tank into the Home Assistant config directory's custom_components folder, then restart. The result should be:
config/custom_components/tuya_water_tank/manifest.json
An optional local clone path, if you keep a copy on the machine that started this project:
C:\Users\plex\Documents\GitHub\Tuya_HACS
That folder is only a convenience copy. Clone this GitHub repository there if you want a local tree, then copy custom_components\tuya_water_tank into Home Assistant. Do not point HACS at that Windows path.
Use the Tuya IoT developer account you already have. This integration does not ship with an Access ID, secret, or device id.
- Open iot.tuya.com and sign in.
- Cloud → Development → Create Cloud Project. Note the data center. The integration has to use the same one. A mismatch looks like a rejected secret.
- On the project, subscribe the APIs that can read a device and its status (IoT Core, including device details and device status).
- Link the Smart Life (or Tuya Smart) app that owns the tank: project → Devices → Link Tuya App Account, then scan the QR code from the app.
- Open the linked device and copy its Device ID. The ME201W used while writing this integration had an id shaped like
ebc455bbc216ca3705aodl. That value is an example of the format. Paste the id from your own device list. - On the project overview, copy the Access ID / Client ID and the Access Secret.
In Home Assistant, the config flow asks for:
| Field | What to paste |
|---|---|
| Data center | The project's data center. The form suggests Western America. That is the right choice when the console URL is us.platform.tuya.com and region=AZ. The OpenAPI host is https://openapi.tuyaus.com. Eastern America, Central Europe, Western Europe, China, India, and Singapore are separate choices. |
| Access ID | Project Access ID |
| Access Secret | Project Access Secret |
| Device ID | The tank's id from the device list |
| Name | Friendly name. Default Water Tank. |
| Poll interval | Seconds between cloud polls. Default 300. Allowed range 30–86400. |
Add the integration again for a second tank. The same cloud credentials can be reused. The config entry is unique per device id.
If Tuya later rejects the secret, Home Assistant starts a reauthentication prompt. Enter the Access ID, secret, and data center again. The device id stays.
Default entity ids are prefixed so they do not take the ids the stock Tuya integration creates for a device named Water Tank:
| Entity | Datapoint | Notes |
|---|---|---|
sensor.tuya_water_tank_liquid_level |
liquid_level_percent (DP 22) |
Percent, state_class: measurement |
sensor.tuya_water_tank_depth |
liquid_depth (DP 2) |
Distance. Native unit meters (scale 2). Home Assistant converts to feet when the unit system is US customary. |
sensor.tuya_water_tank_liquid_state |
liquid_state (DP 1) |
Enum: normal, lower_alarm, upper_alarm |
sensor.tuya_water_tank_battery |
battery_percentage (DP 5) |
device_class: battery, percent |
sensor.tuya_water_tank_alarm_max |
max_set (DP 7) |
Alarm maximum percent, read-only |
sensor.tuya_water_tank_alarm_min |
mini_set (DP 8) |
Alarm minimum percent, read-only |
sensor.tuya_water_tank_installation_height |
installation_height (DP 19) |
Meters, scale 2, read-only |
sensor.tuya_water_tank_max_depth |
liquid_depth_max (DP 21) |
Meters, scale 2, read-only |
number.tuya_water_tank_poll_interval |
— | Seconds between polls. Configuration entity. |
switch.tuya_water_tank_polling |
— | On = scheduled polling. Off = paused. Configuration entity. |
sensor.tuya_water_tank_developer_license |
— | Diagnostic. unknown, valid, expiring, or expired. Attributes include the extension link. |
sensor.tuya_water_tank_license_days_remaining |
— | Diagnostic. Days until the date entered under Configure. Available only when that date is set. |
Unique ids are {device_id}_{datapoint} (and {device_id}_poll_interval, {device_id}_polling_enabled), so renaming the device does not create a second set of entities.
A second tank whose preferred entity id is already taken gets Home Assistant's usual _2 suffix.
Disable the stock Water Tank device so Home Assistant is not reading the same sensor twice:
Settings → Devices & services → Tuya → Water Tank → Disable
You can instead disable only the stock level, depth, and state entities. Battery is provided by this integration.
If the stock device is disabled and you want the shorter ids, rename the entities in Settings → Devices & services → Entities:
sensor.water_tank_liquid_levelsensor.water_tank_depthsensor.water_tank_liquid_statesensor.water_tank_battery
Use those entity ids in automations. Do not target the Tuya device id.
The interval is stored on the config entry. Change it in either place. They write the same option, and the change applies without a reinstall or a restart:
- The number entity
number.tuya_water_tank_poll_intervalon the device page (enable configuration entities if it is hidden). Type a number of seconds. - Settings → Devices & services → Tuya Water Tank → Configure. The options flow sets the interval and Polling enabled.
Pause scheduled polling with switch.tuya_water_tank_polling or by turning Polling enabled off in the options flow. The last good values stay. tuya_water_tank.poll_now still works while paused. A paused tank is still checked once a day for an expired developer license.
Polling hits the Tuya Cloud OpenAPI on this interval. A slower interval means fewer cloud queries. That is the useful knob when the site is on battery or solar in winter: try 900 (15 minutes), 1800 (30 minutes), or 3600 (1 hour), or pause polling. This does not put the ME201W's Wi-Fi radio to sleep and it does not change the sensor's own report schedule.
The last good values stay on the entities. A warning is written to the log, and the next try waits longer: the configured interval, then double, then double again, up to 3600 seconds. After a successful poll the interval returns to the configured value. Entities are not flipped to unavailable on every failed request once a reading exists.
A brand-new install with no saved reading, and a cloud that cannot be reached, stays in setup retry until the first successful poll. A later restart can show the last saved reading if a new poll fails.
An expired developer license is the exception on a first install: the config entry still loads with empty tank values so the license sensor and the repair are visible. After a reading exists, later license errors keep that reading.
tuya_water_tank.poll_now fetches immediately.
action: tuya_water_tank.poll_nowPass entity_id to limit the poll to one tank. Omit it to poll every tank configured with this integration. Use an entity id from the table above, not a device id.
Settings → Devices & services → Tuya Water Tank → three-dot menu → Download diagnostics
The download includes last success, last error, last poll time, the configured interval, whether polling is enabled, the current backoff, and the developer-license status (developer_license, license_expires_on, license_days_remaining, license_checked_at, license_detail, extension_url). It does not include the Access Secret, access token, or request signature.
A Tuya IoT Core trial lasts about six months. Tuya extends it from the console. There is no OpenAPI for the trial end date. The expire_time field on a token response is the two-hour access token, not the subscription. The project id in the console URL is not an expiry date.
The integration records license status from the calls it already makes:
- A successful device poll means Tuya is still accepting this project's cloud calls. The sensor becomes
validwhen no expiry date is stored, or when that date is more than 30 days away. - Error
28841002, or a message that the cloud development plan, trial, or subscription has expired, sets the status toexpired. - While scheduled polling is on, that check rides along with each tank poll. An extra call is skipped when one already happened in the last day.
- While scheduled polling is paused, a separate probe runs every 86400 seconds (once a day) so a winter pause does not hide an expired trial. Authentication failures and ordinary network errors do not change the license status.
Enter the console date under Settings → Devices & services → Tuya Water Tank → Configure → License expires. That field is optional. Within 30 days the status is expiring. A date that has already passed is expired even while cloud calls still succeed, so the extension can be requested before Tuya turns the API off. Days remaining can be negative. Clear the date to rely on the API result alone.
sensor.tuya_water_tank_developer_license is always available. Its attributes are extension_url, days_remaining, expires_on, last_checked, and detail. sensor.tuya_water_tank_license_days_remaining is available only after a date is saved.
When the status is expiring or expired, Home Assistant opens a repair under Settings → System → Repairs. Warning for expiring, error for expired. Learn more opens the IoT Core page for the data center saved on the config entry. For Western America that page is:
https://us.platform.tuya.com/cloud/products/detail?abilityId=1442730014117204014&abilityAuth=0&tab=1
The console adds &id= plus the cloud project id while that project is open. That id is not the Access ID and not the device id, and this integration does not store it. The same page shows the current trial end date. Type that date into Configure → License expires.
The same page from the console menu: Cloud → Cloud Services → IoT Core → View Details → Extend Trial Period. Tuya reviews the request. Community reports are often approved in under a day, and the extension is free for up to another six months. After approval, the next tank poll or daily probe clears the expired API flag if the calls succeed. Entering the Access ID and Access Secret again is not required when those credentials are still valid.
The repair goes away when the status returns to valid or unknown, and when the config entry is removed.
- Data center. The config flow suggests Western America (
https://openapi.tuyaus.com), which is Tuya console regionAZonus.platform.tuya.com. Change it if the project was created in another data center. The project id in the console URL is not the Access ID and not the device id. - Depth unit.
liquid_depth,installation_height, andliquid_depth_maxare meters with scale 2. The integration divides the raw integer by 100.device_class: distancelets Home Assistant display feet on a US customary unit system and meters on a metric system. Feet are not hardcoded. - Battery. DP 5 is treated as a percentage from 0 to 100 even when the cloud specification labels the unit
V. The unit string is ignored. Values outside 0–100 are clamped. It is not converted from a voltage. - Alarm setpoints and heights are read-only sensors. This version does not write them back to Tuya.
- One product family. Unknown datapoints are ignored. Other Tuya devices in the account are left alone. This is not a replacement for the official Tuya integration and it is not a Zigbee or Zigbee2MQTT bridge.
Unit tests cover depth scale conversion, battery mapping (including a V unit), the documented HMAC-SHA256 signing vectors, and developer-license status (error 28841002, expiry phrases, the 30-day window, and the Western America extension link). They do not need a running Home Assistant or cloud credentials.
python -m pip install pytest
python -m pytestMIT. See LICENSE.