Skip to content

caidurbin/blustream

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Home Assistant integration for the Blustream DMP168 multi-zone audio matrix

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors