A Python library to connect to Deye 德业 Cloud and control Deye dehumidifier devices.
Supported devices:
- DYD-B12A3
- DYD-D50A3
- DYD-D50B3
- DYD-E12A3
- DYD-G25A3
- DYD-N20A3
- DYD-L48A3
- DYD-T22A3
- DYD-U20A3
- DYD-U20Air
- DYD-V58A3
- DYD-W20A3
- DYD-W20A3-京鱼座
- DYD-X20A3
- DYD-Z12A3
- DYD-Z20B3
- DYD-Z20B3-天猫精灵
- DYD-S12A3
- DYD-F20C3
- JD121EC
- JD201FC
- TM208FC
- DY-612S
- DY-620S
- DY-8220C
- DY-890C
- DY-890T
- DY-6138A
- DY-8138C
- DY-8158C
- DY-8158T
- DY-Y16A3
- DY-SC60Y
- DYD-P40
- A10
- DYD-P30
- DYD-ES25A3
- DYD-B13A3
- DYD-RT12
- DY-C65DZ/A
For devices not in the above list, consider adding your own definitions here.
This library also includes a command-line tool for testing and interacting with Deye devices.
The CLI tool is automatically installed when you install the library:
pip install libdeye
# or
uv add libdeye# List devices (prints platform and CLASSIC / FOG / COMBO transport)
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD devices
# List all available product types
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD products
# Get device state (works for Classic, Fog, and Combo)
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD get --device-id YOUR_DEVICE_ID
# Set device state
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD set --device-id YOUR_DEVICE_ID --power on --target-humidity 50
# Set device mode and fan speed (enum names, not display labels)
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD set --device-id YOUR_DEVICE_ID --mode AUTO_MODE --fan-speed HIGH
# Set additional device features
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD set --device-id YOUR_DEVICE_ID --anion on --oscillating on --water-pump off --child-lock off
# Monitor device state changes in real-time
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD monitor --device-id YOUR_DEVICE_ID
# Get MQTT information for Classic platform
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD classic-mqtt
# Get MQTT information for Fog platform
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD fog-mqtt
# Enable debug logging
deye-cli --debug --username YOUR_USERNAME --password YOUR_PASSWORD devices
# Print authentication token (useful for saving to .env file)
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD print-token
# Force refresh the authentication token
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD refresh-tokenCredentials can be provided via command-line arguments, process environment
variables, or a .env file. Precedence (highest to lowest):
- Command-line arguments (
--username,--password,--token,--device-id) - Process environment variables (
DEYE_USERNAME,DEYE_PASSWORD, ...) - Values from a
.envfile
This matches the default behavior of python-dotenv and 12-factor app conventions:
.env is a local default, and already-set environment variables are not overwritten.
You can export variables in your shell:
export DEYE_USERNAME=your_username
export DEYE_PASSWORD=your_password
deye-cli devicesOr store them in a .env file to avoid typing them in each command:
# Create a .env file in your working directory
echo "DEYE_USERNAME=your_username" > .env
echo "DEYE_PASSWORD=your_password" >> .env
# Now you can run commands without specifying credentials
deye-cli devices
# You can also specify a different .env file location
deye-cli --env-file /path/to/your/.env devicesThe supported variable names (used by both the environment and .env files) are:
DEYE_USERNAME=your_phone_number_or_username
DEYE_PASSWORD=your_password
# Optional: store auth token to avoid login each time
DEYE_AUTH_TOKEN=your_auth_token
# Optional: default device for get / set / monitor
DEYE_DEVICE_ID=your_device_id
With DEYE_DEVICE_ID configured, you can omit --device-id:
# Get device state without specifying device-id
deye-cli get
# Set device state without specifying device-id
deye-cli set --power on --target-humidity 50
# Monitor device state changes
deye-cli monitorTo avoid sending your username and password with each request, you can use an authentication token:
# Get your authentication token
deye-cli --username YOUR_USERNAME --password YOUR_PASSWORD print-token
# Copy the token and add it to your .env file
echo "DEYE_AUTH_TOKEN=your_token_here" >> .env
# Now you can use the token instead of username/password
deye-cli devices
# If your token expires, you can refresh it
deye-cli --token YOUR_TOKEN refresh-tokendevices: List devices on the account, including IoT platform and command transport (CLASSIC,FOG, orCOMBO)products: List all available product typesget: Query current state viaDeyeClient(Classic MQTT poll, Fog HTTP GET, or Combo MQTT poll)set: Send a command viaDeyeClient.apply(power, mode, fan speed, humidity, anion, water pump, oscillating, child lock, plus optional Fog sleep / UV / temperature / prompt sound / screen display / timed-off hour)monitor: Subscribe to MQTT state and availability updatesprint-token: Print the authentication token for use in .env filerefresh-token: Force refresh the authentication tokenclassic-mqtt: Get MQTT broker credentials for Classic / Combo devicesfog-mqtt: Get MQTT broker credentials for Fog devices (inbound state only; Fog commands use HTTP)
set --mode and set --fan-speed take DeyeDeviceMode / DeyeFanSpeed
enum names, for example MANUAL_MODE, CLOTHES_DRYER_MODE, AUTO_MODE,
LOW, HIGH. Humidity range depends on the product (often 25-80 or 26-90).
For more options, run:
deye-cli --helpCallers use DeyeClient and DeyeDevice. The library selects Classic
MQTT, Fog HTTP, or Combo MQTT from each device-list entry. Do not construct
platform MQTT clients yourself.
Call refresh() or ensure_connected() before subscribe().
Send commands with device.apply(command, baseline=...). Fog devices
with cached ProtocolVersion == 0 send the official companion snapshot
for each changed property (not a union of every cached key); otherwise
only changed fields are posted.
DeyeDeviceCommand.to_json matches official
FogDeviceManager.sendCommand: every PropertyParam Integer is
skipped when null, including child lock and anion. Unset library fields
stay None and are omitted. Classic to_bytes treats unset
switches as off.
The product JSON (uvLight, tone, displayScreen,
hasDelayer) only shows or hides those controls, the same way it
gates anion and oscillating. Use get_product_feature_config (uv,
prompt_sound, screen_display, timed_off) to decide whether a
product advertises them.
Fog GET and device-list payloads may omit any Fog key. Optional controls
stay None and are not posted. Diagnostic flags Demisting,
WaterTank, and Fan default to off. ProtocolVersion == 0 companions
copy cached non-null Integers for that command's official key set
(display, tone, and timer stay single-key). Sleep is
DeyeDeviceMode.SLEEP_MODE. to_json_diff treats omitted keys as
absent on the baseline, so the first set value is published without the
caller inventing a placeholder.
import asyncio
import aiohttp
from libdeye import DeyeClient
async def main() -> None:
async with aiohttp.ClientSession() as session:
client = DeyeClient.from_credentials(
session, "<phone_number>", "<password>"
)
await client.authenticate()
devices = await client.list_devices()
if not devices:
print("No devices found")
return
device = devices[0]
print(f"Device: {device.name} (ID: {device.device_id})")
print(f"Transport: {device.transport.name}")
state = await device.refresh()
print(
f"Current humidity: {state.environment_humidity}% "
f"(Target: {state.target_humidity}%)"
)
def on_state_update(state) -> None:
print(
f"Device state updated. Current humidity: "
f"{state.environment_humidity}%"
)
unsubscribe = device.subscribe(on_state=on_state_update)
command = state.to_command()
command.target_humidity = 40
await device.apply(command, baseline=state)
await asyncio.sleep(30)
unsubscribe()
client.disconnect()
if __name__ == "__main__":
asyncio.run(main())