Skip to main content

ecowitt-modbus Python library

CI PyPI Python License

ecowitt-modbus is an asynchronous, transport-independent Python library for communicating with Fine Offset / Ecowitt weather sensors over Modbus RTU (including RTU-over-TCP, the common way these sensors are bridged onto a network).

The library was developed as the backend for a corresponding Home Assistant integration. As it is kept independent of Home Assistant, it can also be used by other Python applications and projects.

Supported devices

Device Specification Default address Comments
Fine Offset / Ecowitt WN90LP Modbus RTU V1.0.6 0x90 (144) All-in-one weather sensor array with no moving parts (light, UV, temperature, humidity, wind, rain, pressure)
Fine Offset / Ecowitt WN69LP Modbus RTU V1.0.2 0x24 (36) 7-in-1 sensor array with a mechanical anemometer and tipping-bucket rain gauge

Both are the wired RS-485 members of the Ecowitt range. The LP models are the ones with a Modbus interface; their wireless counterparts (the WS90 and WS69, which report to an Ecowitt gateway instead) have none and are not addressable here.

If you are looking for the WS90: the WN90LP is its wired version — the same sensor with the solar panel replaced by an RS-485 connection. Ecowitt originally published its Modbus specification as WS90ModbusRTU and has since renamed it to WN90LP ModbusRTU, with the register map unchanged.

The two supported devices share almost no register layout. They are modelled separately and only present a common interface, not common internals.

Usage

Construct the model you have with a modbus_connection.ModbusUnit, confirm the right device answered, then poll it:

from ecowitt_modbus import WN90LP

device = WN90LP(unit)
await device.async_probe()
await device.async_update()

print(device.sensors.temperature)   # 26.2
print(device.sensors.wind_speed)    # 1.2

An application that lets a user pick the model can look the class up by name instead of branching:

from ecowitt_modbus import SUPPORTED_MODELS

device = SUPPORTED_MODELS["WN69LP"](unit)

async_probe() raises NotThisDeviceError when something answers but is not the model asked for. How much it can confirm depends on the device — see Identifying a device below.

Data provided by the library

Every supported model reports light, UV index, temperature, humidity, wind speed, gust speed, wind direction, rainfall, and absolute pressure. Beyond that they differ:

WN90LP WN69LP
Rainfall resolution 0.1mm, plus a 0.01mm counter 0.254mm (0.01in tipping bucket)
UV index encoding tenths whole numbers
Battery / supply voltage archived history only live registers
30-minute rolling history yes, 11 parameters at one sample per minute no
Recent-rainfall register no yes
Firmware version not reported reported (specification V1.0.2 and later)

Both models also expose their RS-485 settings, and accept validated writes to the two settable identity registers (baud rate and device address).

The WN90LP's history block is 330 registers wide and changes once a minute, so it is polled separately from the live readings:

await device.async_update_history()
print(device.history.battery_voltage)

Identifying a device

The two models differ in how confidently a caller can tell what it is talking to, which matters for any application that needs to recognise a device again later:

  • The WN90LP reports a fixed device code and a 32-bit device ID. async_probe() is a positive identification, and device.serial_number is stable across changes of host, port, and device address.
  • The WN69LP reports neither. device.serial_number is always None, and async_probe() can only reject a responder whose readings fall outside the physical ranges the specification documents (for example over 100% humidity). That rules out most unrelated devices, but it cannot distinguish a WN69LP from another device whose registers happen to decode plausibly at the same addresses.

Scope

The library reads live weather readings, RS-485 communication settings, and — on the WN90LP — the 30-minute rolling history the sensor archives internally.

It does not implement:

  • the WN90LP's "on-demand measurement" command registers (0x9C92-0x9C9A), which duplicate the live readings with tighter timing at the cost of a write-then-wait-then-read sequence,
  • the WN69LP's automatic reporting mode (register 0x162), which has the device push readings unprompted rather than answer polls,
  • either device's write-only command registers (rainfall reset, software reset),
  • the recovery command that reads or sets the baud rate and address when they are unknown, which is a non-Modbus framed exchange.

The library does not create or own the Modbus transport. Applications provide a modbus_connection.ModbusUnit and may use any backend modbus-connection supports (pymodbus, tmodbus, ...).

Testing and validation

Decode logic (scale, offset, and invalid-value handling) is cross-checked against the manufacturers' own worked examples in the WS90 Modbus RTU V1.0.6 and WN69LP Modbus RTU V1.0.2 specifications. The WN90LP model is additionally verified against a live sensor.

Software tests run against an in-memory mock Modbus backend (via modbus-connection's pytest plugin) — no real device or server is needed to run the test suite. The register images those tests use are exported as ecowitt_modbus.testing so consuming applications can reuse them.

Documentation, development and contribution guidelines

Run script/run_checks.sh before opening a pull request — it mirrors the CI workflow (formatting, lint, compile, tests, build) in one command. script/format_code.sh applies formatting and safe lint fixes only. script/query.py is a standalone CLI for querying a real sensor over TCP or serial; run it with --help for usage.

Contributions target the develop branch; main only ever receives fast-forwards from develop (enforced in CI).

This library is one of three repositories for Ecowitt Modbus support in Home Assistant:

  • ha-ecowitt-modbus — a HACS-installable custom integration, vendoring this library.
  • ha-core-ws90 — a home-assistant/core fork adding this as a built-in integration (branch ecowitt-ws90-integration).

Metadata

Release files for ecowitt-modbus 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ecowitt-modbus 0.3.0
File Size Uploaded
ecowitt_modbus-0.3.0.tar.gz 24.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ecowitt-modbus 0.3.0
File Interpreter ABI Platform
ecowitt_modbus-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.2 kB

Release files / ecowitt_modbus-0.3.0.tar.gz

Download URL ecowitt_modbus-0.3.0.tar.gz
Size 24.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7852f808469ebd573cb1bf38c1f4ac44408d81b54c401787fae6bf5589f96d0e
BLAKE2b-256 checksum
How to use checksums
c989c9afb78faf94e6298b15dfe0b7a837f0f59342ecc0b1a5fa2c53de500728
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / ecowitt_modbus-0.3.0-py3-none-any.whl

Download URL ecowitt_modbus-0.3.0-py3-none-any.whl
Size 22.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42c9455b59d024700f0a63c7c1602acced37502fb915d652bfb37f7230f7734e
BLAKE2b-256 checksum
How to use checksums
994460a7a721406b8970aaf9e810e6a425cccdd6001058f96aa413824af0df73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page