Skip to main content

Python library and CLI for controlling Blustream audio devices

Project description

Blustream

Open-source toolkit for controlling Blustream audio devices — starting with the DMP168 digital audio matrix processor — across three home-automation surfaces.

Components

This repository is a monorepo housing three deliverables that share a single protocol contract under spec/:

  1. Python library + CLI (blustream/). A clean async Python API and a blustream console script. Ships to PyPI on v* tags.
  2. Control4 driver (control4/dmp168/). A Lua driver packaged as a .c4z archive that exposes the matrix as a standard Control4 audio matrix. Ships as a GitHub release with the .c4z attached on c4-v* tags.
  3. Home Assistant integration (custom_components/blustream/). A HACS integration (domain blustream) built to the HA Integration Quality Scale Gold tier. Ships as a GitHub release on hacs-v* tags, which HACS installs from the repository tree. It depends on the published blustream PyPI library rather than re-embedding the protocol code.

The protocol primitives shared between the library and the driver are generated from spec/protocol.yaml via the codegen tooling under spec/codegen/. The Python and Lua emitters write into blustream/devices/dmp168/_generated.py and control4/dmp168/src/generated.lua respectively, and CI fails when those committed files drift from what the spec would produce.

Independent versioning

Each component releases on its own cadence using a distinct tag prefix. The prefixes are disjoint so a tag for one component cannot accidentally trigger the wrong release workflow.

Component Tag prefix Workflow Artifact
Python library v* .github/workflows/release-pypi.yml blustream on PyPI (sdist + wheel)
Control4 driver c4-v* .github/workflows/release-c4z.yml GitHub release with attached .c4z
HA integration hacs-v* .github/workflows/release-hacs.yml GitHub release; HACS installs from tree

For example, a library bug-fix release tags v0.2.1, while a driver-only update tags c4-v0.3.0. The shared spec/ directory is the coordination point when a protocol change requires both components to move in lockstep.

Installation

Python library + CLI

Once published, the library installs from PyPI:

pip install blustream

For local development from a checkout:

pip install -e ".[dev]"

Or run the CLI directly from the checkout without installing:

uvx --from . blustream --host 192.0.2.100 status

Control4 driver

Download the .c4z from the latest GitHub release tagged c4-v* and install it through Composer Pro. See docs/control4-driver-plan.md for the dealer-load workflow.

Home Assistant integration

The integration installs through HACS as a custom repository (it is not yet in the default HACS store):

  1. In HACS, open the overflow menu → Custom repositories.
  2. Add https://github.com/caidurbin/blustream with category Integration.
  3. Install Blustream Audio Matrix, then restart Home Assistant.
  4. Add it from Settings → Devices & Services (Add Integration → Blustream), or accept the DHCP/zeroconf discovery prompt.

Requires Home Assistant ≥ 2026.5.0. HACS installs the integration from the repository tree at each hacs-v* release tag; it depends on the published blustream PyPI library, which Home Assistant installs automatically.

Library Usage

import asyncio
from blustream import DMP168

async def main():
    # Connect to device
    device = DMP168(host='192.0.2.100', port=23)
    await device.connect()

    # Read status
    status = await device.get_status()
    print(f"Temperature: {status.temperature}°C")
    print(f"DSP Usage: {status.dsp_usage}%")

    # Control device
    await device.power_on()
    await device.set_output_volume(1, 75, unit='percent')
    await device.route_input_to_output(input_ch=2, output=1)

    # Context manager support
    async with DMP168(host='192.0.2.100') as device:
        status = await device.get_status()
        await device.power_on()

    # Disconnect
    await device.disconnect()

# Run the async function
asyncio.run(main())

CLI Usage

Subcommand names are derived from the command registry. Use blustream --help to list all commands and blustream <command> --help for parameter details.

Get Device Status

blustream --host 192.0.2.100 status

Power Control

blustream --host 192.0.2.100 power-on
blustream --host 192.0.2.100 power-off

Volume Control

# Set output 1 volume to 75%
blustream --host 192.0.2.100 output-volume --output 1 --level 75

# Set output 1 volume to -10 dB
blustream --host 192.0.2.100 output-volume --output 1 --level -10 --unit dB

# Increase output 1 volume by one step
blustream --host 192.0.2.100 output-volume --output 1 --increase-level

# Decrease output 1 volume by one step
blustream --host 192.0.2.100 output-volume --output 1 --decrease-level

Mute Control

# Mute output 1
blustream --host 192.0.2.100 output-mute --output 1 --mute

# Unmute output 1
blustream --host 192.0.2.100 output-mute --output 1 --no-mute

Routing

# Route input 2 to output 1
blustream --host 192.0.2.100 route --input 2 --output 1

Presets

# Save current configuration to preset 1
blustream --host 192.0.2.100 preset-save --preset 1

# Recall preset 1
blustream --host 192.0.2.100 preset-recall --preset 1

# Get preset status
blustream --host 192.0.2.100 preset-status --preset 1

JSON Output

# Get status as JSON
blustream --host 192.0.2.100 status --json

Command Line Options

Global flags must appear before the subcommand (e.g. blustream --yes reboot, not blustream reboot --yes).

  • --device: Device type (default: dmp168)
  • --host: Device hostname or IP address (default: localhost)
  • --port: TCP port (default: 23)
  • --timeout: Connection timeout in seconds (default: 5.0)
  • --verbose, -v: Enable verbose output
  • --debug: Enable debug output
  • --json: Output results as JSON
  • --yes, -y: Skip confirmations

Architecture

The library is designed with extensibility in mind:

  • Base Framework: Abstract base classes for devices and connections
  • Device Implementations: Device-specific code in separate modules
  • Command Registry: Commands are registered with metadata for introspection
  • Connection Layer: Pluggable connection implementations (TCP/IP, serial, etc.)

Supported Devices

  • DMP168: Digital audio matrix processor (16 inputs, 8 outputs)

Development

Project Structure

blustream/
├── blustream/              # Main package
│   ├── base/                # Base classes and interfaces
│   ├── connection/          # Connection implementations
│   ├── devices/             # Device-specific implementations
│   │   └── dmp168/          # DMP168 device
│   └── cli/                 # CLI implementation
├── tests/                   # Test suite
├── main.py                  # CLI entry point
└── README.md               # This file

Running Tests

pytest tests/

Lua development

The Control4 driver lives under control4/ and targets Lua 5.1 (the version the Control4 Composer sandbox runs). CI lints all Lua sources with luacheck (see lint-control4.yml) and runs the unit tests with busted (see test-control4.yml).

To install the toolchain locally:

# macOS
brew install lua@5.1 luarocks
luarocks --lua-version=5.1 install luacheck
luarocks --lua-version=5.1 install busted

# Debian / Ubuntu
sudo apt-get install lua5.1 liblua5.1-0-dev luarocks
sudo luarocks --lua-version=5.1 install luacheck
sudo luarocks --lua-version=5.1 install busted

Then, from the repo root:

luacheck .                              # lint all Lua source
busted --pattern=_spec control4/        # run Lua unit tests

Commit hooks (secret scanning + linting)

Commits are gated by pre-commit, which runs betterleaks (a secret scanner — gitleaks' feature-frozen successor) alongside ruff, ruff-format, luacheck and a large-file guard. The secret scanner uses the shared .gitleaks.toml, which adds custom IPv4/IPv6/MAC rules so a real device identifier can't be committed — example identifiers must use the IETF documentation ranges listed in docs/secret-scanning-allowlist.md.

Install once per clone:

# macOS
brew install betterleaks pre-commit

# Debian / Ubuntu (betterleaks: see its release page or `go install`)
pipx install pre-commit
go install github.com/betterleaks/betterleaks@v1.4.1

pre-commit install          # wire the hooks into git

Then commits are scanned automatically. To run everything on demand:

pre-commit run --all-files                       # all hooks, whole tree
betterleaks detect --no-git --config .gitleaks.toml   # just the secret scan

CI enforces the same checks server-side so the gate holds even without the local hook installed: the secret scan runs in secret-scan.yml, and ruff / luacheck / the tests run in the per-component lint-* and test-* workflows. ruff-format and the large-file guard are local-only (CI does not enforce whole-tree formatting, so legacy files aren't reformatted until touched).

License

This project is licensed under the Apache License 2.0.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. Before your first commit, install the commit hooks (brew install betterleaks pre-commit && pre-commit install) so the secret scan and linters run locally.

Future Enhancements

See FUTURE_ENHANCEMENTS.md for planned features and improvements.

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

blustream-0.3.0.tar.gz (81.6 kB view details)

Uploaded Source

Built Distribution

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

blustream-0.3.0-py3-none-any.whl (47.1 kB view details)

Uploaded Python 3

File details

Details for the file blustream-0.3.0.tar.gz.

File metadata

  • Download URL: blustream-0.3.0.tar.gz
  • Upload date:
  • Size: 81.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for blustream-0.3.0.tar.gz
Algorithm Hash digest
SHA256 eb01a6acd1bb6f7a5243e616d4b44040d7f8b83e08949f214d72bb373e9e7f29
MD5 51670bba8b9e2dae1fbc12a0cd75a092
BLAKE2b-256 7282007588c46eaeca093a22b2c5af0cae0db4b32d5e532540354c1ed9482169

See more details on using hashes here.

Provenance

The following attestation bundles were made for blustream-0.3.0.tar.gz:

Publisher: release-pypi.yml on caidurbin/blustream

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

File details

Details for the file blustream-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: blustream-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 47.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for blustream-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f9b14e895738f15fc185839799ed37b19279b9f87b400e225cef91a623bcd495
MD5 44504645aa4e870ec250f5b308c992f1
BLAKE2b-256 8c9217d735aeedeb94604783485b760b8f68160e9527c1abb4977b7962c6d5de

See more details on using hashes here.

Provenance

The following attestation bundles were made for blustream-0.3.0-py3-none-any.whl:

Publisher: release-pypi.yml on caidurbin/blustream

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

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