Open-source toolkit for controlling Blustream audio devices — starting with the DMP168 digital audio matrix processor — across three home-automation surfaces.
This repository is a monorepo housing three deliverables that share a single
protocol contract under spec/:
- Python library + CLI (
blustream/). A clean async Python API and ablustreamconsole script. Ships to PyPI onv*tags. - Control4 driver (
control4/dmp168/). A Lua driver packaged as a.c4zarchive that exposes the matrix as a standard Control4 audio matrix. Ships as a GitHub release with the.c4zattached onc4-v*tags. - Home Assistant integration (
custom_components/blustream/). A HACS integration (domainblustream) built to the HA Integration Quality Scale Gold tier. Ships as a GitHub release onhacs-v*tags, which HACS installs from the repository tree. It depends on the publishedblustreamPyPI 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.
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.
Once published, the library installs from PyPI:
pip install blustreamFor 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 statusDownload 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.
The integration installs through HACS as a custom repository (it is not yet in the default HACS store):
- In HACS, open the overflow menu → Custom repositories.
- Add
https://github.com/caidurbin/blustreamwith category Integration. - Install Blustream Audio Matrix, then restart Home Assistant.
- 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.
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())Subcommand names are derived from the command registry. Use blustream --help
to list all commands and blustream <command> --help for parameter details.
blustream --host 192.0.2.100 statusblustream --host 192.0.2.100 power-on
blustream --host 192.0.2.100 power-off# 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 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# Route input 2 to output 1
blustream --host 192.0.2.100 route --input 2 --output 1# 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# Get status as JSON
blustream --host 192.0.2.100 status --jsonGlobal 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
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.)
- DMP168: Digital audio matrix processor (16 inputs, 8 outputs)
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
pytest tests/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 bustedThen, from the repo root:
luacheck . # lint all Lua source
busted --pattern=_spec control4/ # run Lua unit testsCommits 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 gitThen 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 scanCI 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).
This project is licensed under the Apache License 2.0.
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.
See FUTURE_ENHANCEMENTS.md for planned features and improvements.