Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/commitlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Commit message lint

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Lint commits in PR
uses: wagoid/commitlint-github-action@v4
with:
configFile: commitlint.config.js
34 changes: 34 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Release

on:
push:
branches:
- main
- beta

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build

- name: Run tests
run: npm test

- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no -- commitlint --edit "$1"
21 changes: 21 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"branches": [
"main",
{
"name": "beta",
"prerelease": true,
"channel": "beta"
}
],
"plugins": [
["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }],
"@semantic-release/changelog",
["@semantic-release/npm", { "npmPublish": false }],
["@semantic-release/github"],
["@semantic-release/git", {
"assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]
]
}
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Contributing

Thanks for contributing to lox-audioserver! Below are short guidelines to keep contributions consistent with automated releases and commit linting.

## Commit messages
We use Conventional Commits. Some examples:

- `feat: add blablabla`
- `fix(parser): prevent crash on missing metadata`
- `chore: update dependencies`
- `docs: update README`
- `refactor!: change API contract` (the `!` marks a breaking change)

## Branch strategy
- `beta`: for unstable or testing releases. Open PRs to `beta` to test features in a pre-release.
- `main`: stable releases. Only merge into `main` when code is tested and approved.

## Releases
- `semantic-release` runs automatically for pushes to `beta` and `main`.
- `beta` produces prereleases like `2.2.0-beta.1`.
- `main` produces normal semver releases like `2.2.0`.

## Pull Request flow
1. Create a feature branch from `beta` or `main` (preferably `beta` for new features).
2. Open a PR and ask for reviews.
3. Ensure all CI checks are green (lint, tests, commitlint).
4. Merge to `beta` for test deployments. When stable, open a PR from `beta` to `main` or cherry-pick the relevant commits.
143 changes: 74 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,94 +5,101 @@ player backends (required) and, optionally, media providers while keeping the Mi
happy. It exposes the same HTTP/WebSocket surface as the original firmware so existing apps,
Touch/Miniservers, and integrations can keep talking to it without modification.

The project currently ships with working Bang & Olufsen BeoLink and Music Assistant support, but the modular design makes it straightforward to plug in other systems.

## Features

- 🎧 **Zone backends**: Music Assistant, Beolink, and a stub Sonos implementation that
demonstrates how to integrate additional clients.
- 📻 **Media providers (optional)**: Music Assistant provider with full library/radio/playlist
support. If no provider is configured, the built-in dummy provider returns empty lists so
clients remain responsive.
- 🧩 **Extensible core**: Clean separation between request routing, providers, and zone
backends to make future integrations easy.
- Zone backends
- 🎧 Music Assistant backend — Controls Music Assistant players; supports multiple players per server. (Set `maPlayerId` per zone in the admin UI.)
- 🔊 BeoLink backend — Integrates with Bang & Olufsen BeoLink devices. Typically one device per zone via its IP.
- 📦 Sonos / Example backend — Stub/sample implementation to demonstrate how to integrate additional clients; extend this for real Sonos support.

- Media providers (optional)
- 📻 Music Assistant provider — Full library, radio and playlist browsing and playback via Music Assistant.
- 🧪 BeoLink provider — Only radio support.
- ⚙️ Dummy provider — Returns empty lists for library/radio/playlist requests so clients remain responsive when no provider is configured (Default provider).

- Extensible core
- 🧩 Clean separation between HTTP/WebSocket routing, media providers, and zone backends to make adding new integrations straightforward.

You can configure backends and providers via the admin UI; see the `Configuration Overview` below for pointer to `data/config.json` and per-backend notes.

## Requirements

- Node.js **20** or newer (the repo uses `@tsconfig/node20`).
- npm (ships with Node) for dependency management.
- Easiest deployment: use the published Docker image.
- Docker (recommended) — easiest way to run the server without building from source.
- docker-compose (optional) — the repository includes a `docker-compose.yml` for one-command startup.
- Make sure host ports `7091` and `7095` are available (or adjust host mappings when running the container).

## Quick Start

Clone the repository of use a zipped release from the releases.
The easiest options are `docker-compose` or `docker run`.

1. **Install dependencies**
### Recommended: docker-compose (one command)

```bash
npm install
```
If you have Docker and docker-compose installed you can use the included `docker-compose.yml`:

2. **Build**
```bash
docker compose up -d
```

```bash
npm run build
```
This starts a container named `lox-audioserver` and exposes the required ports (`7091`, `7095`).

3. **Run**
### Quick Docker run

```bash
npm start
```
If you prefer `docker run`:

The server exposes two endpoints by default:
```bash
docker run -d \
--name lox-audioserver \
-p 7091:7091 \
-p 7095:7095 \
-v $(pwd)/data:/app/data \
ghcr.io/rudyberends/rudyberends/lox-audioserver:latest
```

- `7091` – `AppHttp` (used by Loxone apps / WebSocket clients and the admin UI).
- `7095` – `msHttp` (used by the Miniserver itself).
This starts a container named `lox-audioserver` and exposes the required ports (`7091`, `7095`).

During development you can use the watcher to run TypeScript directly:
### Run standalone by cloning (no Docker)

```bash
npm run watch
```
If you prefer to run the server directly on the host without Docker, follow these steps. This is a minimal "standalone" run and requires Node.js and npm.

5. **Run via Docker (from GitHub Container Registry)**
Prerequisites

Every release publishes a multi-arch image to GHCR. Replace `VERSION` with a published tag
(or use `latest`).
- Node.js 20 or newer
- npm (comes with Node)
- Ports `7091` and `7095` available on the host

```bash
docker run \
-p 7091:7091 \
-p 7095:7095 \
-v $(pwd)/data:/data \
ghcr.io/rudyberends/rudyberends/lox-audioserver:VERSION
Step-by-step

The workflow `.github/workflows/create-release-and-build.yml` bumps the version, builds,
and pushes the image automatically whenever changes land on `main`.
1. Clone the repository and change directory:

## Configuring
```bash
git clone https://github.com/rudyberends/lox-audioserver.git
cd lox-audioserver
```

Open the admin UI at `http://<lox-audioserver-ip>:7091/admin` and follow the guided steps. It walks you through adding the Audio Server in Loxone Config, rebooting the Miniserver, pairing, and assigning zones/providers once the MiniServer reconnects.
2. Create a persistent data folder (used for config, logs, and cache):

When the lox-audioserver starts successfully and the Miniserver pairs successfully with the lox-audioserver, the Audio Server icon in
Loxone Config turns green.
```bash
mkdir -p data
```

## Configuration Overview
3. Install dependencies and build:

All settings are stored in `data/config.json`. The
admin UI reads and writes this file for you.
```bash
npm install
npm run build
```

4. Start the server:

```bash
npm start
```

- **Beolink/Sonos backends** expect a one-to-one mapping: each zone points to a dedicated
device IP.
- **MusicAssistant backend** can control many players on the same server. Set `maPlayerId` for
each zone using the “Player ID” from Music Assistant → Player settings.
### Configuring

| Zone backend | Compatible provider(s) | Notes |
| ------------ | ---------------------------------------- | ----- |
| `BackendMusicAssistant` | `MusicAssistantProvider`, `DummyProvider` | Requires `maPlayerId`; multiple zones can share one MA host. |
| `BackendBeolink` | `BeolinkProvider`, `DummyProvider` | One device per zone via its IP. |
| `BackendSonos` / `BackendExample` | `DummyProvider` | Stub/sample implementations; extend to add real provider support. |
Open the admin UI at http://<lox-audioserver-ip>:7091/admin and follow the guided steps. It walks you through adding the Audio Server in Loxone Config, rebooting the Miniserver, pairing, and assigning zones/providers once the MiniServer reconnects.

When the lox-audioserver starts successfully and the Miniserver pairs successfully with the lox-audioserver, the Audio Server icon in Loxone Config turns green.

## Code Structure

Expand Down Expand Up @@ -124,19 +131,17 @@ src/
(e.g., Music Assistant provider pairs with `BackendMusicAssistant`). Mixing incompatible
providers/backends is not supported.

## Development Notes
## Contributing

- TypeScript sources live in `src/`; compiled output goes to `dist/` via `npm run build`.
- Logging uses a Winston-based logger (`src/utils/troxorlogger.ts`). Log levels are configured
via the admin UI (stored in `data/config.json`).
- Graceful shutdown signals (`SIGINT`, `SIGTERM`) are handled in `src/server.ts`; staged
clean-up (zone backends, servers) ensures repeatable restarts.
Pull requests are welcome. Full contribution guidelines (commit message conventions, PR flow and release rules) are in `CONTRIBUTING.md`.

## Contributing
- Make a feature branch from `beta` (or `main` when appropriate):
`git checkout -b feature/your-feature-name`
- Follow Conventional Commits for message formatting (commitlint will reject non-conforming messages).
- Push your branch and open a PR targeting `beta` for testing: `gh pr create --base beta --head feature/your-feature-name`.

Pull requests for new providers/backends are welcome. Please run `npm run build` before
submitting to ensure the TypeScript output stays in sync.
Run `npm run build` locally before submitting a PR to keep compiled output in sync.

---

Need help or discovered a bug? Open an issue in the repository.
Need help or found a bug? Open an issue in the repository.
3 changes: 3 additions & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional']
};
Loading