Water Level in My Tank (ME201WZ Zigbee Sensor)

Hi everyone, I’m Sigalou! I wanted to share my hands-on experience with the Moray ME201WZ Zigbee ultrasonic water level sensor, which I’ve been using to monitor the water level in my underground tank. I’ve integrated it with Zigbee2MQTT and Home Assistant, and I thought it might interest some of you here.

citerneok
As usual, I’m sharing a detailed write-up of my experience with this product and how I set it up.

I’m over the moon! For less than €50, I now have a fully autonomous system that monitors my tank’s water level, running on Zigbee and seamlessly integrated into my home automation setup.

It’s perfect!

Feel free to reach out if you have any questions or need help with your setup.

9 Likes

Hey Sigalou, thanks for taking the time to review the unit and for the photos on how to install it.

We have this same unit at work and it’s currently compatible with ZHA, using HA core V 2025.4.3
I’ll come back and update on how well it functions after testing for a few days.

1 Like

Hi Luthia04

Does it really works in ZHA ? I didn’t find anything about this in zigbee.blakadder.com
(but it is in zigbee2mqtt ME201WZ

What ZHA quirk does it use?

How do you setup the Installation height / Liquid depth max ?

I can report that I just connected this device successfully to ZHA - see below. The steep drop in liquid depth is from not having it on the tank after successfully pairing. Look to the right and you can see a subtle slope. That’s me opening a bunch of consumers to see if the level updates.

After a few minutes it looks like the depth is work, the fill % maybe not. Settings show up in the device’s UI. The two distance settings are called “Water Tank Sensor Height from sensor to liquid level” and “Water Tank Sensor Height from sensor to tank bottom”. Once I make the first number be larger than the second number the percentage changes from 100%. My hunch is now that the first config option is just mislabeled and that one of the measures is sensor height from bottom and the other is max water height from bottom.

I had the toughest time getting the device into pairing mode. The initial blink when connection power is slow. Simply holding the button for 5 seconds didn’t make it a fast blink. I found instructions to “press once, then light will dim, then hold for 5s”. I tried that… Didn’t seem to work. In the end, I feel like I was just tapping slow and fast and suddenly got a fast blink and by HA immediately picked it up…

2 Likes

Hi There,

today I installed the very same sensor.
It shows values but they don’t seem to really change when the level rises or falls.

Can you confirm the mislabeling and show your values? I don’t see the full name in your screenshot.

Thanks in advance

I can confirm they are mislabeled.

This device is weird. It was working when the values were switched up. I did a few power cycles and deleting from the network and re-adding it etc but NOW it is no longer needed to switch up the values. No idea why, it’s just weird.

I was struggling with the level value always showing up 100 percent. Once I switched the values it shows up correctly.

I do however get quite a few errors in the logs because of this device. It’s not particularly far away from the next Router or Controller. Maybe 10m the most.

Logger: homeassistant
Source: /usr/src/homeassistant/homeassistant/runner.py:112
First occurred: June 7, 2025 at 9:25:29 PM (364 occurrences)
Last logged: 10:38:01 AM

Error doing job: Task exception was never retrieved (None)
Traceback (most recent call last):
File “/usr/local/lib/python3.13/site-packages/zigpy_znp/api.py”, line 1120, in request_callback_rsp
return await callback_rsp
^^^^^^^^^^^^^^^^^^
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File “/usr/local/lib/python3.13/site-packages/zigpy_znp/api.py”, line 1117, in request_callback_rsp
async with asyncio_timeout(timeout):
~~~~~~~~~~~~~~~^^^^^^^^^
File “/usr/local/lib/python3.13/asyncio/timeouts.py”, line 116, in aexit
raise TimeoutError from exc_val
TimeoutError

Or this one:

Logger: zigpy.zcl
Source: runner.py:154
First occurred: June 7, 2025 at 10:09:09 PM (13 occurrences)
Last logged: 10:35:57 AM

[0x57BC:1:0xef00] Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 829, in _send_request_raw response = await asyncio.shield( ^^^^^^^^^^^^^^^^^^^^^ …<13 lines>… ) ^ asyncio.exceptions.CancelledError The above exception was the direct cause of the following exception: Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 127, in _limit_concurrency yield File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 373, in request await send_request() File “/usr/local/lib/python3.13/site-packages/zigpy/application.py”, line 844, in request await self.send_packet( …<14 lines>… ) File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 933, in send_packet await self._send_request_raw( …<11 lines>… ) File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 822, in _send_request_raw async with asyncio_timeout( ~~~~~~~~~~~~~~~^ EXTENDED_DATA_CONFIRM_TIMEOUT ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if extended_timeout ^^^^^^^^^^^^^^^^^^^ else DATA_CONFIRM_TIMEOUT ^^^^^^^^^^^^^^^^^^^^^^^^^ ): ^ File “/usr/local/lib/python3.13/asyncio/timeouts.py”, line 116, in aexit raise TimeoutError from exc_val TimeoutError During handling of the above exception, another exception occurred: Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/zcl/init.py”, line 425, in reply return await self._endpoint.reply( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ …<9 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/endpoint.py”, line 304, in reply return await self.device.reply( ^^^^^^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 558, in reply return await self.request( ^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 371, in request async with self._limit_concurrency(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/contextlib.py”, line 235, in aexit await self.gen.athrow(value) File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 120, in _limit_concurrency async with self._concurrent_requests_semaphore(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 32, in aexit await self.context_manager.aexit(exc_type, exc, traceback) File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 184, in aexit self.release() ~~~~~~~~~~~~^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 160, in release raise ValueError(“Semaphore released too many times”) ValueError: Semaphore released too many times
[0x57BC:1:0xef00] Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/zcl/init.py”, line 425, in reply return await self._endpoint.reply( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ …<9 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/endpoint.py”, line 304, in reply return await self.device.reply( ^^^^^^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 558, in reply return await self.request( ^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 371, in request async with self._limit_concurrency(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/contextlib.py”, line 221, in aexit await anext(self.gen) File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 120, in _limit_concurrency async with self._concurrent_requests_semaphore(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 32, in aexit await self.context_manager.aexit(exc_type, exc, traceback) File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 184, in aexit self.release() ~~~~~~~~~~~~^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 160, in release raise ValueError(“Semaphore released too many times”) ValueError: Semaphore released too many times
[0x57BC:1:0x0000] Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/zcl/init.py”, line 425, in reply return await self._endpoint.reply( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ …<9 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/endpoint.py”, line 304, in reply return await self.device.reply( ^^^^^^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 558, in reply return await self.request( ^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 371, in request async with self._limit_concurrency(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/contextlib.py”, line 221, in aexit await anext(self.gen) File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 120, in _limit_concurrency async with self._concurrent_requests_semaphore(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 32, in aexit await self.context_manager.aexit(exc_type, exc, traceback) File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 184, in aexit self.release() ~~~~~~~~~~~~^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 160, in release raise ValueError(“Semaphore released too many times”) ValueError: Semaphore released too many times
[0x57BC:1:0x0000] Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 829, in _send_request_raw response = await asyncio.shield( ^^^^^^^^^^^^^^^^^^^^^ …<13 lines>… ) ^ asyncio.exceptions.CancelledError The above exception was the direct cause of the following exception: Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 127, in _limit_concurrency yield File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 373, in request await send_request() File “/usr/local/lib/python3.13/site-packages/zigpy/application.py”, line 844, in request await self.send_packet( …<14 lines>… ) File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 933, in send_packet await self._send_request_raw( …<11 lines>… ) File “/usr/local/lib/python3.13/site-packages/zigpy_znp/zigbee/application.py”, line 822, in _send_request_raw async with asyncio_timeout( ~~~~~~~~~~~~~~~^ EXTENDED_DATA_CONFIRM_TIMEOUT ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if extended_timeout ^^^^^^^^^^^^^^^^^^^ else DATA_CONFIRM_TIMEOUT ^^^^^^^^^^^^^^^^^^^^^^^^^ ): ^ File “/usr/local/lib/python3.13/asyncio/timeouts.py”, line 116, in aexit raise TimeoutError from exc_val TimeoutError During handling of the above exception, another exception occurred: Traceback (most recent call last): File “/usr/local/lib/python3.13/site-packages/zigpy/zcl/init.py”, line 425, in reply return await self._endpoint.reply( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ …<9 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/endpoint.py”, line 304, in reply return await self.device.reply( ^^^^^^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 558, in reply return await self.request( ^^^^^^^^^^^^^^^^^^^ …<11 lines>… ) ^ File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 371, in request async with self._limit_concurrency(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/contextlib.py”, line 235, in aexit await self.gen.athrow(value) File “/usr/local/lib/python3.13/site-packages/zigpy/device.py”, line 120, in _limit_concurrency async with self._concurrent_requests_semaphore(priority=priority): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 32, in aexit await self.context_manager.aexit(exc_type, exc, traceback) File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 184, in aexit self.release() ~~~~~~~~~~~~^^ File “/usr/local/lib/python3.13/site-packages/zigpy/datastructures.py”, line 160, in release raise ValueError(“Semaphore released too many times”) ValueError: Semaphore released too many times

Just wanted to share I’m using this in a different use case to most people. I have a large Koi Pond and am using this to auto top up water levels during dry spells, and auto empty during particulary rainy spells. It measures my pond water level and then turns on taps/hoses or pumps/hoses to adjust the pond level accordingly. Works great

Interesting, I found this thread after posting the same problem in another forum and someone pointed me here.

Just got this sensor, installed it and it seemed to work at first - as soon as I input the distance between sensor to tank bottom (Height from sensor to tank bottom) - in my scenario 94cm - it pointed to 100%.

Then I saw that every time I change “Height from sensor to tank bottom” it also changes “Height from sensor to liquid level” to the same number (in my case, this is 35cm).

Then I thought - these might be inverted and since one should not be smaller than the other, it is self correcting.

Anyway, I closed the water intake just to see how it would react and after a while, I went back to the attic to check the water reserve - it was at half and the sensor was at 92%.

Does any1 found a way of making it work?

Thanks!

Hi everyone!

I ’ ve been having problems trying to read something on my sensor . It’ s installed on a cylindrical tank with water at 1.75 m and the sensor is installed about 35 cm from the top of the water. I only receive 188% on the liquid level and 0 m on water. I have uninstalled and installed the sensor more than 100 times, and even with the Zigbee antenna about 1 meter away. From time to time, I receive an error saying “Z2M Exception while calling from Zigbee converter. Value 3 is not allowed, expected one of = 1, 0, 2.”
I ’ m using the latest Hass.io running on RPi 5 with Zigbee2MQTT . First, I used the Sonoff Zigbee dongle, and now I ’ m using the ZBT-2 antenna .
Thank you in advance for any insight on this .