Python async client for Home Theater Direct (HTD) whole-home audio systems. Supports the MCA-66, Lync 6, and Lync 12 over TCP or serial.
This is a maintained fork of hikirsch/python-htd with full Lync support, zone/source naming, DND, and EQ controls.
| Model | Zones | Sources | Kind |
|---|---|---|---|
| MCA-66 | 6 | 6 | mca |
| Lync 6 | 6 | 12 | lync |
| Lync 12 | 12 | 19 | lync |
The device model is auto-detected at connection time.
pip install htd-client-haimportasynciofromhtd_clientimportasync_get_clientasyncdefmain():
# Connect over TCP (most common — via the HTD network gateway)client=awaitasync_get_client(network_address=("192.168.1.100", 10006))
awaitclient.async_connect()
awaitclient.async_wait_until_ready()
# Query all zone stateawaitclient.refresh()
# Control a zoneawaitclient.async_power_on(zone=1)
awaitclient.async_set_volume(zone=1, volume=20)
awaitclient.async_set_source(zone=1, source=1)
# Subscribe to zone state changes (push model)asyncdefon_zone_update(zone):
print(f"Zone {zone} updated")
awaitclient.async_subscribe(on_zone_update)
asyncio.run(main())# Or connect over serial (RS-232, or USB-serial adapter)client=awaitasync_get_client(serial_address="/dev/ttyUSB0")Serial connections — especially cheap USB-serial adapters — are hardened against the failure modes that used to trip them up:
- Opening the port no longer races the device's DTR-triggered reset; the client waits out a settle delay before writing.
- Replies delivered across multiple USB packets are now read to completion instead of being misread as a failed probe.
- A single corrupted or dropped byte resyncs cleanly instead of permanently desyncing the parser into a stream of errors.
# Query names from the controller (results cached on client)awaitclient.async_query_zone_name(zone=1)
awaitclient.async_query_source_name(source=1)
# Read cached nameszone_name=client.get_zone_name(zone=1)
source_name=client.get_source_name(source=1)
# Set names on the controllerawaitclient.async_set_zone_name(zone=1, name="Living Room")
awaitclient.async_set_source_name(source=1, name="Sonos")awaitclient.async_set_bass(zone=1, bass=5)
awaitclient.async_set_treble(zone=1, treble=-2)
awaitclient.async_set_balance(zone=1, balance=0)# Exclude a zone from party mode / all-zone commandsawaitclient.async_set_dnd(zone=1, dnd=True)Factory function that auto-detects the device model and returns the appropriate client.
| Parameter | Type | Description |
|---|---|---|
network_address | (str, int) | (host, port) for TCP connection |
serial_address | str | Serial port path for RS-232 connection |
retry_attempts | int | Command retry count (default: 3) |
| Method | Description |
|---|---|
async_connect() | Connect to the device |
async_wait_until_ready() | Wait for initial handshake to complete |
refresh(zone=None) | Query zone state (all zones if omitted) |
async_subscribe(callback) | Register callback for zone state changes |
async_power_on(zone) | Power on a zone |
async_power_off(zone) | Power off a zone |
power_on_all_zones() | Power on all zones |
power_off_all_zones() | Power off all zones |
async_set_source(zone, source) | Set active source for a zone |
async_set_volume(zone, volume) | Set volume (0–60) |
async_volume_up(zone) | Increment volume |
async_volume_down(zone) | Decrement volume |
async_mute(zone) | Mute a zone |
async_unmute(zone) | Unmute a zone |
async_toggle_mute(zone) | Toggle mute |
async_set_bass(zone, bass) | Set bass |
async_set_treble(zone, treble) | Set treble |
async_set_balance(zone, balance) | Set balance |
async_set_dnd(zone, dnd) | Set Do Not Disturb (Lync only) |
async_query_zone_name(zone) | Query zone name from controller (Lync only) |
async_query_source_name(source) | Query source name from controller (Lync only) |
async_set_zone_name(zone, name) | Set zone name on controller (Lync only) |
async_set_source_name(source, name) | Set source name on controller (Lync only) |
get_zone(zone) | Return cached ZoneDetail for a zone |
get_zone_name(zone) | Return cached zone name |
get_source_name(source) | Return cached source name |
get_zone_count() | Return number of zones for this model |
get_source_count() | Return number of sources for this model |
disconnect() | Close the connection |
Poetry is used to manage dependencies and run tests.
poetry install
poetry run pytestMIT — see LICENSE for details.
- hikirsch/python-htd — original library by Adam Kirschner
- kingfetty/python-htd — Lync protocol support