Python CLI for Deye Cloud REST APIs, designed to control and monitor Deye photovoltaic inverters directly from the terminal. Includes an integrated HTTP REST API server.
⚠️ DISCLAIMER
This software is provided "as is", without warranty of any kind, express or implied.
The author is not responsible for any damages, malfunctions, data loss, or issues arising from the use of this utility.
Use at your own risk and responsibility. It is recommended to thoroughly test commands before applying them in production environments.
| Tool | Notes |
|---|---|
python3 ≥ 3.6 |
available on any modern Linux |
requests |
optional — for HTTP calls (falls back to curl if not installed) |
git clone <repo-url> deyecli
cd deyecli
chmod +x deyecli.pyOptionally, install the requests library for better HTTP support:
pip install requestsOptionally, add a symlink to your PATH:
ln -s "$PWD/deyecli.py" ~/.local/bin/deyecliThe script reads parameters from multiple sources, in descending order of precedence:
CLI flag > config file > environment variable > built-in defaults
Default location: ~/.config/deyecli/config
Override with the DEYE_CONFIG=/another/path variable.
mkdir -p ~/.config/deyecli
cp config.example ~/.config/deyecli/config
$EDITOR ~/.config/deyecli/configExample of a completed config file:
# Base URL — change region prefix if necessary
DEYE_BASE_URL=https://eu1-developer.deyecloud.com
# Application credentials (required for every call)
# Obtain them from https://developer.deyecloud.com
DEYE_APP_ID=<your-app-id>
DEYE_APP_SECRET=<your-app-secret>
# Login credentials — provide ONE of username, email, or mobile
DEYE_EMAIL=user@example.com
# Plaintext password — the script converts it to SHA-256 before sending
DEYE_PASSWORD=yourpassword
# Company ID for business token (leave empty for personal account)
DEYE_COMPANY_ID=
# Access token — automatically updated by the 'token' command
DEYE_TOKEN=
# Default device serial number used by config commands
DEYE_DEVICE_SN=
# Weather location for solar-charge-cron
DEYE_WEATHER_LAT=44.0637
DEYE_WEATHER_LON=12.4525
# Solar charge modulation defaults
DEYE_SOLAR_FORECAST_HOURS=12
DEYE_SOLAR_MIN_RADIATION=200
DEYE_SOLAR_LOW_CHARGE_CURRENT=5
DEYE_SOLAR_DEFAULT_CHARGE_CURRENT=
DEYE_SOLAR_PEAK_START=
DEYE_SOLAR_PEAK_END=
DEYE_SOLAR_RAMP_EXPONENT=4
DEYE_SOLAR_CRON_MINUTE=5
DEYE_SOLAR_CRON_FILE=~/.config/deyecli/solar-charge.cronSecurity: The file is read with a line-by-line parser, without
evalorsource. It is safe to insert values with special characters (JWT tokens, passwords with%, etc.).
All config file keys can be passed as environment variables:
DEYE_APP_ID=xxx DEYE_APP_SECRET=yyy DEYE_EMAIL=me@example.com \
DEYE_PASSWORD=mypassword ./deyecli.py tokenPOST /v1.0/account/token
Performs login and obtains an accessToken. If the call is successful the token is automatically saved in DEYE_TOKEN in the config file (via jq).
./deyecli.py tokenRequired parameters:
| Variable | Flag | Description |
|---|---|---|
DEYE_APP_ID |
--app-id |
Deye application ID |
DEYE_APP_SECRET |
--app-secret |
Deye application secret |
DEYE_PASSWORD |
--password |
Plaintext password (SHA-256 hashed) |
DEYE_EMAIL or DEYE_USERNAME or DEYE_MOBILE |
--email / --username / --mobile |
Login identifier |
Optional parameters:
| Variable | Flag | Description |
|---|---|---|
DEYE_COUNTRY_CODE |
--country-code |
Required when using DEYE_MOBILE |
DEYE_COMPANY_ID |
--company-id |
To obtain a business token |
Example:
./deyecli.py token
# → POST https://eu1-developer.deyecloud.com/v1.0/account/token?appId=...
# ✔ DEYE_TOKEN saved to /home/user/.config/deyecli/configResponse (excerpt):
{
"code": "1000000",
"success": true,
"accessToken": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": "5183999",
"tokenType": "bearer"
}Note: Deye returns the token with a
"Bearer "prefix. The script removes it before saving and before each subsequent call, avoiding the double prefixBearer Bearer ....
POST /v1.0/config/battery
Reads battery configuration parameters for a hybrid inverter with storage.
The device serial number can be provided in three equivalent ways:
# 1. Positional argument
./deyecli.py config-battery 2401110313
# 2. Explicit flag
./deyecli.py config-battery --device-sn 2401110313
# 3. From config file (DEYE_DEVICE_SN)
./deyecli.py config-batteryRequired parameters:
| Variable | Flag | Description |
|---|---|---|
DEYE_TOKEN |
--token |
Access token |
DEYE_DEVICE_SN |
--device-sn or positional arg |
Hybrid inverter serial number |
Response example:
{
"code": "1000000",
"success": true,
"battCapacity": 100,
"battLowCapacity": 20,
"battShutDownCapacity": 10,
"maxChargeCurrent": 50,
"maxDischargeCurrent": 50
}Warning: The
deviceSnmust be that of a hybrid inverter (with battery), not the collector/logger. To find the correct serial number, use the Deye app or the/v1.0/device/listAPI.
POST /v1.0/config/system
Reads the current system work mode, energy management pattern, and power limits configured for a device.
# Positional argument
./deyecli.py config-system 2401110313
# Explicit flag
./deyecli.py config-system --device-sn 2401110313
# From config file (DEYE_DEVICE_SN)
./deyecli.py config-systemRequired parameters:
| Variable | Flag | Description |
|---|---|---|
DEYE_TOKEN |
--token |
Access token |
DEYE_DEVICE_SN |
--device-sn or positional arg |
Device serial number |
Response example:
{
"code": "1000000",
"success": true,
"systemWorkMode": "SELLING_FIRST",
"energyPattern": "BATTERY_FIRST",
"maxSellPower": 6000,
"maxSolarPower": 8000,
"zeroExportPower": 0
}Possible systemWorkMode values:
| Value | Description |
|---|---|
SELLING_FIRST |
Sells excess energy to the grid |
ZERO_EXPORT_TO_LOAD |
No export, only covers loads |
ZERO_EXPORT_TO_CT |
Zero export measured via CT (current transformer) |
POST /v1.0/order/battery/parameter/update
Sends a control command to set the value of a single battery parameter. The call returns an orderId representing the queued command; the device executes it as soon as it comes online.
# Set maximum charge current to 50 A
./deyecli.py battery-parameter-update \
--param-type MAX_CHARGE_CURRENT \
--value 50
# With explicit device SN
./deyecli.py battery-parameter-update \
--device-sn 2401110313 \
--param-type MAX_DISCHARGE_CURRENT \
--value 40
# Device SN as positional argument
./deyecli.py battery-parameter-update 2401110313 \
--param-type BATT_LOW \
--value 15Required parameters:
| Variable | Flag | Description |
|---|---|---|
DEYE_TOKEN |
--token |
Access token |
DEYE_DEVICE_SN |
--device-sn or positional arg |
Device serial number |
| — | --param-type <TYPE> |
Parameter type (see table below) |
| — | --value <integer> |
Value to set |
Parameter types (--param-type):
| Value | Description |
|---|---|
MAX_CHARGE_CURRENT |
Maximum charge current (A) |
MAX_DISCHARGE_CURRENT |
Maximum discharge current (A) |
GRID_CHARGE_AMPERE |
Grid charge current (A) |
BATT_LOW |
Low battery threshold / minimum SoC (%) |
API Note: The JSON key is
paramterType(typo in the Deye API, missing the seconde). The script handles this automatically.
Response example:
{
"code": "1000000",
"success": true,
"orderId": 987654,
"connectionStatus": 1,
"collectionTime": 1711093038,
"requestId": "abc123"
}connectionStatus: 0 = Offline, 1 = Online
An orderId other than null confirms the command has been queued. When the device comes online, it executes it and updates the parameters.
Analyzes Open-Meteo hourly forecast and generates a day-specific crontab to modulate MAX_CHARGE_CURRENT in the morning.
Goal: keep battery charging slower in early sunny hours, so more production can be exported to the grid before peak hours.
How it works:
- Detects sunny morning slots from
is_day,direct_radiation, and weather code. - Auto-detects peak window from forecast hour with max direct radiation (unless manually forced).
- Applies a ramp before peak:
charge = low + (max - low) * t^exp
where t goes from 0 to 1 and exp is --ramp-exponent.
4. During peak hours, restores full/default charge current.
5. Writes cron entries with date guard and optional direct install via crontab.
Curve behavior (--ramp-exponent):
1= linear2= gentle early slope, later acceleration4= default (flatter morning, steeper near peak)6+= very flat, sharp rise near peak
Examples:
# Preview computed slots and generated cron (without writing file)
./deyecli.py solar-charge-cron --print-slots --dry-run
# Generate cron file and print full content
./deyecli.py solar-charge-cron --print-crontab
# Install generated crontab immediately
./deyecli.py solar-charge-cron --install-crontab
# Use a flatter curve to delay battery fill more aggressively
./deyecli.py solar-charge-cron --ramp-exponent 6 --install-crontabMain options:
| Option | Description |
|---|---|
--lat, --lon |
Forecast coordinates (fallback: DEYE_WEATHER_LAT/LON) |
--hours |
Forecast horizon (1-48) |
--min-radiation |
Radiation threshold for sunny slots (W/m²) |
--low-charge-current |
Morning minimum current (A) |
--default-charge-current |
Default/peak current (A), auto-detected if omitted |
--peak-start, --peak-end |
Peak window override (hour, 0-23); auto-detected if omitted |
--ramp-exponent |
Ramp curve exponent (DEYE_SOLAR_RAMP_EXPONENT, default 4) |
--minute |
Cron minute for generated entries |
--cron-file |
Output cron file path |
--print-slots |
Print weather/decision table |
--print-crontab |
Print generated cron content after write |
--dry-run |
Print cron to stdout without writing file |
--show-config |
Print effective solar-charge-cron config |
--install-crontab |
Install generated cron with crontab <file> |
Notes:
- Generated cron commands read config via
DEYE_CONFIG='<config-file>'. --device-snis not required in generated cron lines: it is read fromDEYE_DEVICE_SNin config.- Command logs are appended to
/tmp/deyecli.log.
# 1. Initial configuration
cp config.example ~/.config/deyecli/config
$EDITOR ~/.config/deyecli/config # insert APP_ID, APP_SECRET, EMAIL, PASSWORD
# 2. Login — token is automatically saved to config
./deyecli.py token
# 3. Insert your inverter's serial number in the config
# (DEYE_DEVICE_SN=<serial>)
# 4. Read battery configuration
./deyecli.py config-batteryAll options can appear before or after the command name:
| Flag | Variable | Description |
|---|---|---|
--base-url <url> |
DEYE_BASE_URL |
API base URL |
--app-id <id> |
DEYE_APP_ID |
Application ID |
--app-secret <secret> |
DEYE_APP_SECRET |
Application secret |
--username <name> |
DEYE_USERNAME |
Login username |
--email <email> |
DEYE_EMAIL |
Login email |
--mobile <number> |
DEYE_MOBILE |
Mobile number |
--country-code <code> |
DEYE_COUNTRY_CODE |
International prefix (required with --mobile) |
--password <pass> |
DEYE_PASSWORD |
Plaintext password (SHA-256 hashed before sending) |
--company-id <id> |
DEYE_COMPANY_ID |
Company ID for business token |
--token <bearer> |
DEYE_TOKEN |
Access token |
--device-sn <sn> |
DEYE_DEVICE_SN |
Device serial number |
--print-query |
DEYE_PRINT_QUERY |
Print all outgoing HTTP requests |
-h, --help |
Show help |
Example:
./deyecli.py station-list --print-query
# → POST https://eu1-developer.deyecloud.com/v1.0/station/list| Region | Base URL |
|---|---|
| EU1 (default) | https://eu1-developer.deyecloud.com |
For other regions, update DEYE_BASE_URL in the config or pass --base-url.
If this tool is useful to you, buy me a coffee ☕