Skip to main content

edifier_es300

Control an Edifier ES300 speaker over Wi-Fi, without the Edifier Home app. Ships an asyncio library and a click-based command-line interface.

  • Library (edifier_es300): stdlib-only, fully async.
  • CLI (python -m edifier_es300): thin wrapper over the library; needs click.

Requirements

  • Python 3.13+
  • click (CLI only — the library has no third-party dependencies)
uv sync        # or: pip install click

Library usage

Everything is async. The ES300 connection is an async context manager — reuse a single connection for a burst of commands (the device drops an idle socket after a few seconds).

import asyncio
from edifier_es300 import ES300, Source, EqPreset, LightEffect, LightColor


async def main():
  async with ES300("192.168.1.123", 8080) as device:
    print(await device.status())  # parsed Status (see below)

    await device.volume(20)  # 0..30
    await device.play()  # resume
    await device.pause()  # pause
    await device.play_pause_toggle()  # toggle
    await device.next_track()
    await device.previous_track()

    await device.input_source(Source.AIRPLAY)

    await device.light_switch(True)
    await device.brightness(60)  # 0..100
    await device.light_effect(LightEffect.BREATHING)
    await device.light_color(LightColor.YELLOW)

    await device.eq_preset(EqPreset.VOCAL)
    await device.eq_custom((10, 5, 0, 0, 0, -5))  # 6 gains, tenths of a dB (-30..30)

    await device.timer_shutdown(30)  # sleep timer in minutes (0 = off)
    await device.shutdown()  # power off (no remote power-on; physical button to wake)


asyncio.run(main())

Internally the context manager holds the socket open and runs a background task that reads every inbound frame. Each command carries a generated id; the device echoes it in its reply, and the background task hands the reply to the awaiting call — so await device.volume(20) returns only once that command's ack comes back.

Discovery

ES300.discover() broadcasts on the LAN and returns a list of ready-to-use ES300 objects (host, port, and name filled in):

speakers = await ES300.discover(seconds=3.0)
for speaker in speakers:
    print(speaker)            # "EDIFIER ES300  192.168.1.123:8080"

async with speakers[0] as device:
    await device.volume(15)

Return values & errors

  • Command methods (volume, play, input_source, eq_custom, …) return the device's raw ack frame (a FrameData dict). They raise CommandFailed if the device acks with a non-success message, or EndOfStream if the socket drops before the ack arrives.
  • status() returns a Status | None (None only if the device stays silent).

Live callbacks

Register callbacks to react to unsolicited frames the device pushes while a connection is held open. Both are usable as decorators and hold the callback by weak reference (drop your reference and it stops firing):

async with ES300("192.168.1.123") as device:
    @device.status_callback
    async def on_status(status: Status):
        print("state changed:", status.volume)

    @device.heartbeat_callback
    async def on_heartbeat(frame):
        print("heartbeat")

    await asyncio.sleep(60)  # callbacks fire as frames arrive

Status

str(status) renders a human-readable dump (this is what the CLI status prints):

playing: - / - (status <PlayerStatus.PLAYING: 1>)
volume : 6 / 30
source : <Source.USB: 2>
effect : <LightEffect.STATIC: 1>
color  : <LightColor.YELLOW: {'r': 255, 'g': 170, 'b': 60}>
eq     : <EqPreset.CLASSIC: 0> gains=[20, 10, 0, -5, 5, 10]
battery: 40% (<BatteryStatus.DISCONNECTED: 2>)
timer  : off

Fields: volume, max_volume, song, lyric, player_status, input_source, light_effect, sound_index, eq_selected_index, eq_gains, battery, and raw (the full status frame for anything not surfaced).

Enums

Enum Values Notes
Source BLUETOOTH=0, AUX=1, USB=2, AIRPLAY=3 input source
EqPreset CLASSIC=0, MONITOR=1, GAME=2, VOCAL=3, CUSTOMIZED=4 CUSTOMIZED is the editable slot
LightEffect STATIC=1, BREATHING=2, WATERFLOW=3 ambient LED effect
LightColor YELLOW, WHITE value is the RGB dict; hardware only does these two
BatteryStatus CONNECTED=1, DISCONNECTED=2 external power state (read-only)

Source, EqPreset, and LightEffect are IntEnums, so methods also accept a plain int. Setting eq_custom gains automatically selects EqPreset.CUSTOMIZED.

CLI usage

python -m edifier_es300 [--host IP] [--port N] COMMAND [ARGS]
  • --host — speaker IP. Omit it to auto-discover the first speaker on the LAN.
  • --port — control-channel TCP port (default 8080).
Command Args Description
discover — list speakers on the LAN (name ip:port)
status — dump volume / source / light / EQ / battery
volume LEVEL (0..30) set volume
play / pause — resume / pause playback
play-pause — toggle play/pause
next-track / previous-track — skip track
shutdown — power the speaker off (no remote power-on)
timer-shutdown MINUTES (0..1440) sleep timer (0 = off; app presets 5/15/30/60/180)
light on | off LED strip on/off
light-brightness LEVEL (0..100) LED brightness
light-effect static | breathing | waterflow LED effect
light-color yellow | white LED color
source bluetooth | aux | usb | airplay input source
eq-preset classic | monitor | game | vocal | customized EQ preset
eq G1..G6 exactly 6 custom gains, tenths of a dB (-30..30 = -3.0..+3.0 dB)

Examples

python -m edifier_es300 discover
python -m edifier_es300 status                       # auto-discover, then dump state
python -m edifier_es300 --host 192.168.1.123 volume 22
python -m edifier_es300 source airplay
python -m edifier_es300 light-effect breathing
python -m edifier_es300 light-color yellow
python -m edifier_es300 eq-preset vocal
python -m edifier_es300 eq -- 10 5 0 0 0 -5          # use -- so negatives aren't read as options

Note: negative EQ gains look like CLI options, so prefix the gain list with --.

Metadata

Release files for edifier-es300 1.0.3

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

Source distribution (sdist)

Source distribution for edifier-es300 1.0.3
File Size Uploaded
edifier_es300-1.0.3.tar.gz 16.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for edifier-es300 1.0.3
File Interpreter ABI Platform
edifier_es300-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 30.4 kB

Release files / edifier_es300-1.0.3.tar.gz

Download URL edifier_es300-1.0.3.tar.gz
Size 16.4 kB
Tags Source
SHA-256 checksum
How to use checksums
879738f8fc4e29037c975e932bf23723896690dd4f5c873884dc06ec03a1d331
BLAKE2b-256 checksum
How to use checksums
57a8bb3db492e4265c9d51fbb04e34496c393ed00666acb028c79fab53b68b2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / edifier_es300-1.0.3-py3-none-any.whl

Download URL edifier_es300-1.0.3-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6bb8d24092e9c8b6856698431744d5c1709d68ea4e78576df4110cf28f7b3aad
BLAKE2b-256 checksum
How to use checksums
17b7d505bda825fc3e6c5f3f90e49873b7e50c05af5e888c4c1606d00df51c58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

1.0.3 This release

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.2.0

2 release files

0.1.1

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