Skip to main content

aiodisklavier

Async Python client for the Yamaha Disklavier ENSPIRE local HTTP API.

Talks to the piano directly over your own network. Verified against firmware 5.24.00 on a Disklavier ENSPIRE PRO grand.

Install

pip install aiodisklavier

Use

import asyncio

import aiohttp

from aiodisklavier import Disklavier, SongGroup


async def main() -> None:
    async with aiohttp.ClientSession() as session:
        piano = Disklavier("192.168.1.50", session)

        info = await piano.async_get_current_info()
        print(info.song_title, info.playback_status, info.position_seconds)

        # Fuzzy title search runs on the piano itself.
        await piano.async_play_search("Clair de lune")

        # Play one song and stop, rather than continuing through the library.
        await piano.async_play_song(24, SongGroup.DOWNLOADED_SONGS, single=True)


asyncio.run(main())

Finding the piano is a plain SSDP M-SEARCH for urn:schemas-upnp-org:device:Disklavier:1; the library exposes that device type as UPNP_DEVICE_TYPE.

What it covers

Area Methods
State async_get_static_info, async_get_current_info, async_get_master_state
Transport async_play, async_pause, async_stop, async_play_pause, async_next_song, async_previous_song, async_restart_song, async_seek
Volume async_set_volume, async_volume_up, async_volume_down
Power async_turn_on, async_turn_off, async_set_power
Voicing async_set_quiet_mode, async_set_repeat
Playback async_play_song, async_play_search, async_play_genre, async_play_album, async_play_playlist, async_play_playlist_item
Browsing async_get_songs, async_get_albums, async_get_songs_in_album, async_get_playlists, async_get_playlist_items
Radio async_get_radio_channels, async_play_radio, async_stop_radio
Notifications async_notify, async_snapshot_playback, async_restore_playback, async_play_test_chord
Library async_refresh_library

Firmware behaviours worth knowing

These are properties of the piano, not of this library, and each is easy to get wrong. The full reasoning, with provenance for every claim, is in docs/enspire-api.md.

  • There is no stop state. stop leaves playback_status reading pause at position zero. Use CurrentInfo.is_stopped rather than looking for a stop constant.
  • Waking takes about twelve seconds, during which power_status reads wakeup and the piano ignores commands. The HTTP API answers normally while asleep, so reachability tells you nothing about power state.
  • Empty libraries are an error, not an empty list — HTTP 200 carrying {"status": "error", "error_info": "no song"}. The browse methods translate that envelope back into the empty list it denotes, so callers just see [].
  • State reads can come back truncated while a song is playing, because the daemon rewrites those files in place. Reads retry automatically. Payloads may also carry a trailing \n\0, which is stripped rather than retried.
  • State lags a command. Reading current_info straight after a load_song or reselect returns the previous song. Allow a short settle before trusting a post-command read.
  • Radio's interaction with transport commands is not established. There is reason to think playback behaves differently while a radio channel is playing, but it has not been exercised on hardware — treat transport during radio as unknown.
  • async_play_test_chord makes a sound — a C major triad for one second. It goes to the MIDI daemon rather than the sequencer, so it will not disturb a loaded song.

Two APIs, one preferred

The piano exposes a versioned open API at /api/1.0/<command> and an internal, unversioned set of endpoints under /ctrl/ that its own web UI drives. This library uses the open API wherever possible and drops to /ctrl/ only for what the open API cannot do: seeking, repeat and shuffle, the extended state block, reindexing, and the test chord.

The open API takes some finding: nothing the piano normally serves links to it, and neither the phone app nor the piano's own web UI calls it. The one client-side trail is /ctrl/api_test.html, a test harness Yamaha ships on the device — that is where the /api/1.0/ form is visible. /api/api.php?_com=<command> is the same surface by another name, verified equivalent down to the error codes.

Security

The piano's API is plaintext HTTP with no authentication (unless a passcode is set on the piano), and SSDP discovery answers are unauthenticated multicast — any host on the LAN can observe or impersonate the piano. The client hardens itself against a hostile device: response bodies are read against a size ceiling, redirects are refused, and device-supplied strings are treated as data. The transport itself still has no confidentiality or integrity, so keep this traffic on a trusted network and do not expose the piano or this client across an untrusted one.

Development

python3 -m venv .venv
.venv/bin/pip install -e ".[test]"
.venv/bin/python -m pytest

Tests run against a real aiohttp test server that imitates the piano, so no hardware is needed and the suite does not depend on any mocking library's grip on aiohttp internals. Several tests encode behaviour found only on real hardware — those are commented as such, because they look arbitrary otherwise.

Licence

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiodisklavier-0.1.1.tar.gz (49.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiodisklavier-0.1.1-py3-none-any.whl (21.5 kB view details)

Uploaded Python 3

File details

Details for the file aiodisklavier-0.1.1.tar.gz.

File metadata

  • Download URL: aiodisklavier-0.1.1.tar.gz
  • Upload date:
  • Size: 49.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for aiodisklavier-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7616e081db0403d4f09cee742c5b356e7f04f1b65acf602a5f17011aef005552
MD5 24e1f47d673f0012f31f3faaf302466f
BLAKE2b-256 c032e1c915354af1ca6ef212796a8b66d82e720446d77c95fc193614ad847699

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiodisklavier-0.1.1.tar.gz:

Publisher: publish.yml on reubenbijl/aiodisklavier

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aiodisklavier-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: aiodisklavier-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for aiodisklavier-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a2247bfc06a5f1b1a9dea91fe871acf3e3833792acffd7f00e6e315c8c3b12f6
MD5 f9c7d45970982444ab837458c90a3591
BLAKE2b-256 37f8fca5c58f2b3fc8fc1366a2b54b3a4bc98c8c3899f84981e4a72c80009f29

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiodisklavier-0.1.1-py3-none-any.whl:

Publisher: publish.yml on reubenbijl/aiodisklavier

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

This release

0.1.1 This release

2 files

0.1.0

2 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