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
159 changes: 159 additions & 0 deletions .github/workflows/build-feature-branch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# GitHub Action for Feature Branch Docker Builds
# Builds and pushes Docker images for feature branch development

name: Build Feature Branch Docker

on:
push:
branches:
- 'feature/zone-configuration-improvements'
pull_request:
branches:
- 'feature/zone-configuration-improvements'
workflow_dispatch:
inputs:
force_build:
description: 'Force rebuild even without changes'
required: false
default: false
type: boolean

# Environment variables for Docker registry and image naming
env:
REGISTRY: docker.io
IMAGE_NAME: docbobo/roon-extension-denon
FEATURE_TAG: feature-zone-config

jobs:
build-feature-branch:
name: Build Feature Branch Docker Image
runs-on: ubuntu-latest

# Only run on push to feature branch or manual dispatch
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'

environment: production
permissions:
packages: write
contents: read
attestations: write
id-token: write

steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ env.FEATURE_TAG }}
type=raw,value=${{ env.FEATURE_TAG }}-{{sha}}
type=ref,event=branch,prefix=${{ env.FEATURE_TAG }}-
labels: |
org.opencontainers.image.title=Roon Extension Denon (Feature Branch)
org.opencontainers.image.description=Roon Volume Control Extension for Denon/Marantz receivers (Zone Configuration Feature)
org.opencontainers.image.vendor=docbobo
org.opencontainers.image.url=https://github.com/docbobo/roon-extension-denon

- name: Build Docker image
id: build
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
context: .
file: ./Dockerfile
push: false
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.FEATURE_TAG }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Test container startup
run: |
echo "πŸ§ͺ Testing container startup..."
# Start container and capture output
CONTAINER_ID=$(docker run --rm -d ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.FEATURE_TAG }})
echo "Container ID: $CONTAINER_ID"

# Wait for container to start
sleep 3

# Check container logs for successful startup
if docker logs $CONTAINER_ID 2>&1 | grep -E "(Setting up sood|Starting sood)"; then
echo "βœ… Container started successfully"
docker stop $CONTAINER_ID 2>/dev/null || true
else
echo "❌ Container startup test failed"
echo "πŸ“‹ Container logs:"
docker logs $CONTAINER_ID 2>&1 || true
docker stop $CONTAINER_ID 2>/dev/null || true
exit 1
fi

- name: Push Docker image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Validate Docker image
run: |
echo "βœ… Docker image built and pushed successfully"
echo "πŸ“¦ Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
echo "🏷️ Tags: ${{ steps.meta.outputs.tags }}"
echo "πŸ”— Docker Hub: https://hub.docker.com/r/${{ env.IMAGE_NAME }}/tags"

# Optional job for pull requests - just validate build without push
validate-pr:
name: Validate PR Build
runs-on: ubuntu-latest

if: github.event_name == 'pull_request'

permissions:
contents: read

steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image (no push)
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
context: .
file: ./Dockerfile
push: false
tags: ${{ env.IMAGE_NAME }}:pr-${{ github.event.number }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Validate PR build
run: |
echo "βœ… PR build validation completed"
echo "πŸ” Build tested for platforms: linux/amd64,linux/arm64"
echo "πŸ“‹ Ready for feature branch merge"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
config.json
node_modules
package-lock.json
coverage/
99 changes: 99 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

This is a Roon Volume Control extension for controlling Denon/Marantz AV receivers via network connection. The extension allows volume control and muting from within Roon by connecting to the receiver's network interface.

## Common Commands

### Installation and Running
- Install dependencies: `npm install`
- Run the extension: `node .` or `node app.js`

### Development
- The main entry point is `app.js`
- Test suite: `npm test` (Jest)
- Coverage reports: `npm run test:coverage`
- Uses standard Node.js debugging with the `debug` package

## Architecture

### Core Components

The extension is a single-file Node.js application (`app.js`) that integrates several key components:

1. **Roon API Integration**: Uses multiple Roon API modules:
- `node-roon-api` - Core Roon API functionality
- `node-roon-api-settings` - Settings management UI
- `node-roon-api-status` - Status reporting
- `node-roon-api-volume-control` - Volume control interface
- `node-roon-api-source-control` - Source switching interface

2. **Denon Client Integration**: Uses `denon-client` package to communicate with Denon/Marantz receivers via TCP connection

3. **Extension Services**:
- **Settings Service** (`svc_settings`): Manages hostname/IP configuration and input selection
- **Status Service** (`svc_status`): Reports connection status to Roon
- **Volume Control Service** (`svc_volume_control`): Handles volume and mute operations
- **Source Control Service** (`svc_source_control`): Manages input switching and standby

### Key Architecture Patterns

- **Event-driven**: Uses event listeners for Denon client state changes (power, input, volume, mute)
- **Connection Management**: Implements automatic reconnection with keep-alive mechanism (60-second intervals)
- **State Synchronization**: Maintains local state objects (`denon.volume_state`, `denon.source_state`) that sync with both Denon receiver and Roon
- **Promise-based**: Async operations use Promises for Denon client communication

### Configuration Flow

1. Settings UI probes available inputs from Denon receiver
2. User configures hostname/IP and desired input source
3. Extension establishes TCP connection to receiver
4. Creates volume and source control devices in Roon
5. Maintains real-time synchronization via event handlers

### Connection Lifecycle

- `setup_denon_connection()`: Initializes connection with error handling and event setup
- `connect()`: Establishes connection and creates control interfaces
- Keep-alive mechanism prevents connection timeout
- Automatic reconnection on connection loss

## Version Numbering

**Semantic Versioning Scheme**: `YYYY.MINOR.PATCH`

- **Year (YYYY)**: Major version, updated annually
- **Minor**: New features, breaking changes, significant enhancements
- **Patch**: Bug fixes, small improvements, dependency updates

**Examples**:
- `2025.1.0` - First release of year with new features
- `2025.1.1` - Patch release with bug fixes
- `2025.2.0` - New minor version with feature additions
- `2025.8.0` - Multi-Zone Configuration feature

## Multi-Zone Configuration (v2025.8.0+)

**New Features**:
- Zone selection: Main Zone or Zone 2 control
- Coordinated power control: Option to power off both zones simultaneously
- Enhanced settings UI with dropdown controls
- Comprehensive zone management functions with full test coverage

**Zone Configuration Options**:
- `zone: "main"` - Controls Main Zone (default, full functionality)
- `zone: "zone2"` - Controls Zone 2 (power-only control)
- `powerOffBothZones: true` - Powers off both zones when turning off

## Important Notes

- Only supports one Denon client connection at a time (receiver limitation)
- Connection prevents other Telnet-based applications from connecting
- Extension ID: `org.pruessmann.roon.denon`
- Volume range: -79.5 dB to receiver max, 0.5 dB steps
- Requires receiver network interface to be enabled
- Zone 2 supports power control only (no volume/source control)
- Multi-zone coordination available for power operations
Loading