Skip to main content

Python client library for the JBL MA-series (MA510/MA710/MA7100HP/MA9100HP) IP control protocol

Project description

jbl-ma-control

A pure-Python client library for the JBL MA-series AV receivers (MA510, MA710, MA7100HP, MA9100HP) over their IP control protocol.

It implements every command and option in Harman's IP control interface and commands for JBL MA510/MA710/MA7100HP/MA9100HP (document v1.7): power, input selection, volume, mute, surround decoding, equalization, room EQ, Dolby/DTS options, streaming status, IR passthrough, reboot, factory reset and more.

  • No third-party runtime dependencies.
  • Fully type-hinted (ships py.typed).
  • Thorough error handling with a typed exception hierarchy.
  • Framing layer is transport-independent and unit-tested; the client is tested end-to-end against an in-process mock AVR.

Installation

pip install jbl-ma-control

Quick start

from jbl_ma import JBLClient, InputSource, SurroundMode

# The AVR always listens on TCP port 50000.
with JBLClient("192.168.1.50") as avr:
    model = avr.initialize()          # identify ourselves; returns the Model
    print("Connected to", model.name)

    avr.power_on()
    avr.set_input_source(InputSource.HDMI1)
    avr.set_volume(35)                # 0..99
    avr.set_surround_mode(SurroundMode.ALL_STEREO)
    avr.set_bass(4)                   # dB, -12..12
    avr.unmute()

Async

For asyncio applications (including Home Assistant) use AsyncJBLClient, which exposes the same method names as coroutines:

import asyncio
from jbl_ma import AsyncJBLClient, InputSource

async def main():
    async with AsyncJBLClient("192.168.1.50") as avr:
        await avr.initialize()
        await avr.power_on()
        await avr.set_input_source(InputSource.HDMI1)
        await avr.set_volume(35)

asyncio.run(main())

Concurrent coroutines sharing one AsyncJBLClient are safe — transactions are serialized internally so request/response pairs never interleave.

Manual connection management

The client is a context manager; you can also manage the connection manually:

avr = JBLClient("192.168.1.50", connect=False)
avr.connect()
try:
    print(avr.get_volume())
finally:
    avr.close()

Protocol notes

Every transmission uses the frame format from the PDF:

Controller -> AVR : <0x23><CmdID><DataLen><Data...><0x0D>
AVR -> Controller : <0x02 0x23><CmdID><RspCode><DataLen><Data...><0x0D>
  • After connecting you should call initialize() so the AVR registers this controller. It returns the connected Model.

  • Call heartbeat() periodically to keep the AVR out of its auto-standby timer; initialize() works as a heartbeat too.

  • reboot() is acknowledged and then the unit restarts, dropping the connection. On the MA510 (verified on hardware) it comes back up in standby rather than powered on — call power_on() afterwards if you need it on, and allow time for it to reboot before reconnecting.

  • The AVR also emits unsolicited frames when its state changes from the front panel or IR remote. Pass an on_event callback to receive them:

    def on_change(frame):
        print("state change:", frame.command, frame.data)
    
    avr = JBLClient("192.168.1.50", on_event=on_change)
    

API overview

Area Methods
System initialize(), heartbeat(), reboot(), factory_reset(), get_software_version()
Power get_power(), set_power(), power_on(), standby()
Display get_display_dim(), set_display_dim()
Source get_input_source(), set_input_source()
Volume get_volume(), set_volume(), get_mute(), set_mute(), mute(), unmute()
Sound get_surround_mode(), set_surround_mode(), get_dolby_audio_mode(), set_dolby_audio_mode(), get_dialog_enhanced(), set_dialog_enhanced(), get_drc(), set_drc()
EQ get_treble(), set_treble(), get_bass(), set_bass(), get_room_eq(), set_room_eq()
Party mode get_party_mode(), set_party_mode(), get_party_volume(), set_party_volume()
Streaming get_streaming_state()
IR remote send_ir()

All enums (InputSource, SurroundMode, DisplayDim, RoomEQ, DolbyAudioMode, PowerState, StreamingServer, PlayState, SoftwareModule, Model, IRCommand) and value ranges are exported from the top-level jbl_ma package.

Model availability: some options only exist on certain models (for example HDMI 5/6, Phono, party mode, DTS Neural:X and Dirac Live). The library exposes every documented value; sending one an attached model does not support results in the AVR replying with ParameterNotRecognizedError or CommandInvalidError.

Error handling

All errors derive from jbl_ma.JBLError:

from jbl_ma import JBLClient, JBLCommandError, CommandInvalidError, RoomEQ

with JBLClient("192.168.1.50") as avr:
    try:
        avr.set_room_eq(RoomEQ.DIRAC_LIVE)
    except CommandInvalidError:
        print("No room-correction filter has been uploaded yet.")
    except JBLCommandError as exc:
        print("AVR rejected the command, code", hex(exc.code))
Exception Meaning
JBLConnectionError Socket refused / reset / closed
JBLTimeoutError No complete response within the timeout
JBLProtocolError A malformed frame was sent or received
CommandNotRecognizedError Response code 0xC1
ParameterNotRecognizedError Response code 0xC2
CommandInvalidError Response code 0xC3 (command invalid at this time)
InvalidDataLengthError Response code 0xC4

Low-level framing

The jbl_ma.protocol module can encode and decode frames without any I/O, which is useful for testing or building your own transport:

from jbl_ma import encode_command, decode_responses, Command

frame = encode_command(Command.MASTER_VOLUME, b"\x28")   # set volume to 40
responses, leftover = decode_responses(b"\x02\x23\x06\x00\x01\x28\x0d")

Development

pip install -e ".[dev]"
pytest

License

MIT

Project details


Download files

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

Source Distribution

jbl_ma_control-1.1.0.tar.gz (22.5 kB view details)

Uploaded Source

Built Distribution

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

jbl_ma_control-1.1.0-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file jbl_ma_control-1.1.0.tar.gz.

File metadata

  • Download URL: jbl_ma_control-1.1.0.tar.gz
  • Upload date:
  • Size: 22.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jbl_ma_control-1.1.0.tar.gz
Algorithm Hash digest
SHA256 fe28dc28fd2594fe77eae0d775359e219385c666b6eb2361688040e292f05393
MD5 b072298cd1a29f7c561f3eb92672f217
BLAKE2b-256 c31985b314aa6becaf930932f0713f265b32519d77a21556ff5da6e092720427

See more details on using hashes here.

File details

Details for the file jbl_ma_control-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: jbl_ma_control-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jbl_ma_control-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 93d12b91bdb9cff6592476a7976afd3c4821236d3c39f72898626881ac1b4c4b
MD5 aa60305e603b836524bc112a196a1370
BLAKE2b-256 8009356ab67835cd89690f48ff7b446b54e25c3d9ec7e239d2b8b525a5f376a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page