Skip to content

Repository files navigation

Cloud2MQTT

MQTT bridge for cloud-locked smart home devices.
MQTT-Brücke für Cloud-gefangene Smart-Home-Geräte.

English · Deutsch

Small gateway service that translates devices locked into a vendor cloud into your own MQTT broker. First target: Levoit air purifiers via the VeSync cloud.

Devices like Levoit speak no native MQTT — they are reachable only through their manufacturer's cloud. This gateway polls that cloud, normalises the values and publishes them to MQTT, retained. Commands travel the same road in the opposite direction.

Cloud2MQTT dashboard: status bar, device cards with readings and controls

The status bar shows poller, cloud, broker and connection from every view. Below it one card per device — and only the controls the model actually has: the 200S has a night light, the 300S have particulate readings and an auto mode instead.

What it does

  • Polls the VeSync cloud at a fixed interval and publishes every device as one retained JSON, plus each reading on its own topic
  • Commands over MQTT (cmnd/<property>) — the web interface uses the very same path, there is no second way in
  • Only what the model can do: the gateway asks each device for its capabilities; fields a model lacks are omitted, never sent as null
  • Home Assistant discovery, optional and switchable — turning it off removes the entities again instead of leaving ghosts behind
  • Topic export for EisBär SCADA V3/V4 (channel CSV + payload profile XML)
  • Diagnostics bundle: report, configuration without credentials and the log in one ZIP — account, broker and device names replaced by placeholders
  • Interface in English and German, light and dark, remembered per browser

Running it

# 1. Create the configuration (configdir/ is gitignored)
mkdir configdir
cp src/config.example.yaml configdir/config.yaml
#    -> fill in your VeSync account and broker address

# 2. Build and start
docker compose up -d --build

# 3. Watch the log
docker logs -f cloud2mqtt

# 4. Stop
docker compose down

Then open http://your-host:8088 and set the password for the interface.

You can also hand the first password in through the environment variable CLOUD2MQTT_PASSWORD; it is stored as a hash on first start, after which the line can disappear from your compose file again. Without a password set, the API serves no data at all — not even to another program on your network.

Where the configuration lives

Environment Path on the host Mount inside the container
Local ./configdir/ /config (a directory, :rw)
Synology DSM /volume1/docker/cloud2mqtt/ /config (a directory, :rw)

Why a directory and not the file itself? The interface writes the configuration with os.replace, which is atomic. On a single-file mount that fails with OSError: Device or resource busy — renaming onto the exact mount point of a single file is structurally blocked, on every filesystem, DSM included. So both environments mount a directory with :rw.

MQTT

Everything lives under the configured base topic and is published retained:

cloud2mqtt/<group>/<device>/state          one complete JSON per device
cloud2mqtt/<group>/<device>/status/<field> every reading on its own topic
cloud2mqtt/<group>/<device>/cmnd/<field>   commands go here
cloud2mqtt/status                          availability (Last Will)
cloud2mqtt/health/cloud/state              online / degraded / offline

Commands live in their own subtree on purpose, so the gateway's own status publish cannot come straight back at it as a command.

The full topic list with data types and switch values, split by model, is in docs/MQTT_REFERENZ.md; the wire format itself is specified in docs/MQTT_FORMAT.md. What your own installation currently serves is shown by the topic export in the interface — generated from your running devices, not from a maintained list.

What else the interface does

Topic export: general CSV, EisBär files and the diagnostics bundle

The topic export saves you the typing — as a general CSV, or for EisBär SCADA as payload XML plus channel CSV. Next to it the diagnostics bundle: report, configuration without credentials, and the log, with placeholders instead of your account, broker address and device names.

Log window with level filters and search

The log runs live, filterable by level and searchable — the same lines that end up in the diagnostics bundle. It is in English whichever language the interface is set to: it leaves the house in that bundle, and it sits next to uvicorn and paho in docker logs.

Update check with switch, button and a disclosure of what is fetched

The update check is the only connection to anyone besides VeSync and your own broker — so what it fetches is spelled out next to it, and next to that the switch.

A note on trust

This gateway holds your VeSync credentials, so it is worth saying plainly what it talks to:

  • the VeSync cloud, to read your devices and send commands
  • your own broker, at the address you configured
  • Docker Hub, and only to read the public list of versions — switchable off, and explained in the interface

Nothing else. The diagnostics bundle does not send itself anywhere; you download it and decide.

Cloud2MQTT speaks HTTP. On your own network that is fine; across the internet put a reverse proxy with TLS in front of it, or the password travels in the clear.

One thing VeSync imposes, not us: the account usually allows a single active session. Polling can log the phone app out, and the phone app can log the gateway out. That is not a bug in this gateway.

Architecture in one sentence

A core (MQTT, poll loop, topics) plus interchangeable adapters/: an adapter translates a cloud device into a target-neutral dict, and the core knows nothing about any specific cloud. Adding another vendor cloud means writing a new adapter — the core stays untouched.


Part of the CheetahLab workbench — cheetahlab.de

About

Cloud2MQTT pulls the readings of your Levoit air purifiers from the VeSync cloud and publishes them to your own MQTT broker — as JSON and as individual values, retained. You can control them the same way: through this interface or over MQTT, both run through the same code.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages