Home Assistant Holo-Series Integration

  • Home |
  • Home Assistant Holo-Series Integration
微信图片 20251127134942

Home Assistant Holo-Series Integration

Home Assistant Holo-Series Integration (Full Deployable Version)

 

1. Plugin Overview

 
Developed based on the latest Holo-Series Streamer Web API documentation, this integration supports all Holo-Series devices including HoloWhas, HoloOne AMP, and HoloOne Pro Max. It enables core functionalities such as device monitoring, input source management, and zone audio control, fully adapting to all implemented APIs.

2. File Directory Structure

 
plaintext
 
custom_components/
└── holoseries/
    ├── __init__.py          # Initialization & core API interactions
    ├── manifest.json        # Plugin metadata
    ├── sensor.py            # Device status sensor entities
    ├── media_player.py      # Zone audio control entities
    ├── services.yaml        # Service definitions
    ├── config_flow.py       # UI configuration flow
    ├── const.py             # Constant definitions
    └── translations/
        └── en.json          # English translations (optional)

3. Core File Implementations

3.1 const.py (Constant Definitions)

 
python
 
DOMAIN = "holoseries"
DEFAULT_PORT = 80
DEFAULT_SCAN_INTERVAL = 30

# Configuration constants
CONF_HOST = "host"
CONF_PORT = "port"
CONF_SCAN_INTERVAL = "scan_interval"
CONF_DEVICE_FILTERS = "device_filters"
CONF_INPUT_CLASS_FILTER = "input_class_filter"
CONF_ZONE_FILTERS = "zone_filters"

# Input class constants
INPUT_CLASSES = {
    0: "General Input",
    1: "Zone-based (Spotify)",
    2: "Zone-based (Airplay2)"
}

# Supported input types
SUPPORTED_INPUT_TYPES = [
    "Airplay", "DLNA", "Spotify", "USB", "Analog", "RCA", "ARC", "Optical", "Bluetooth"
]

# EQ bands (10 bands)
EQ_BANDS = [
    "31.25Hz", "62.5Hz", "125Hz", "250Hz", "500Hz",
    "1KHz", "2KHz", "4KHz", "8KHz", "16KHz"
]

3.2 manifest.json (Plugin Metadata)

 
json
 
{
  "domain": "holoseries",
  "name": "Holo-Series Streamer Integration",
  "version": "1.0.0",
  "documentation": "https://github.com/example/holoseries-homeassistant",
  "requirements": ["requests>=2.31.0"],
  "dependencies": [],
  "codeowners": ["@your_username"],
  "config_flow": true,
  "iot_class": "local_polling",
  "homeassistant": "2023.1.0"
}

3.3 init.py (Initialization & API Interactions)

 
python
 
import requests
import logging
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL
from .const import (
    DOMAIN, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL,
    CONF_DEVICE_FILTERS, CONF_INPUT_CLASS_FILTER, CONF_ZONE_FILTERS
)

_LOGGER = logging.getLogger(__name__)

async def async_setup(hass: HomeAssistant, config: dict) -> bool:
    hass.data.setdefault(DOMAIN, {})
    if DOMAIN not in config:
        return True
    
    # Read configuration
    conf = config[DOMAIN]
    host = conf[CONF_HOST]
    port = conf.get(CONF_PORT, DEFAULT_PORT)
    scan_interval = conf.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
    device_filters = conf.get(CONF_DEVICE_FILTERS, [])
    input_class_filter = conf.get(CONF_INPUT_CLASS_FILTER)
    zone_filters = conf.get(CONF_ZONE_FILTERS, [])
    
    # Initialize API client
    api = HoloSeriesAPI(host, port)
    hass.data[DOMAIN]["api"] = api
    hass.data[DOMAIN]["config"] = {
        "device_filters": device_filters,
        "input_class_filter": input_class_filter,
        "zone_filters": zone_filters,
        "scan_interval": scan_interval
    }
    
    # Load platforms
    hass.helpers.discovery.load_platforms(config, DOMAIN, ["sensor", "media_player"])
    return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    host = entry.data[CONF_HOST]
    port = entry.data.get(CONF_PORT, DEFAULT_PORT)
    api = HoloSeriesAPI(host, port)
    hass.data[DOMAIN]["api"] = api
    hass.data[DOMAIN]["config"] = {
        "device_filters": entry.options.get(CONF_DEVICE_FILTERS, []),
        "input_class_filter": entry.options.get(CONF_INPUT_CLASS_FILTER),
        "zone_filters": entry.options.get(CONF_ZONE_FILTERS, []),
        "scan_interval": entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
    }
    hass.config_entries.async_setup_platforms(entry, ["sensor", "media_player"])
    return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    unload_ok = await hass.config_entries.async_unload_platforms(entry, ["sensor", "media_player"])
    if unload_ok:
        hass.data.pop(DOMAIN)
    return unload_ok

class HoloSeriesAPI:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port
        self.base_url = f"http://{host}:{port}/api/v3"
        self.session = requests.Session()

    def _request(self, method: str, endpoint: str, json=None, params=None) -> requests.Response:
        """Generic request method"""
        url = f"{self.base_url}{endpoint}"
        try:
            response = self.session.request(
                method=method, url=url, json=json, params=params, timeout=10
            )
            response.raise_for_status()
            return response
        except requests.exceptions.RequestException as e:
            _LOGGER.error(f"API request failed {method} {url}: {str(e)}")
            raise

    # Device-related APIs
    def get_devices(self) -> list:
        """Get all device IDs"""
        response = self._request("GET", "/devices/")
        return response.json().get("device_ids", [])

    def get_device_info(self, device_id: str) -> dict:
        """Get single device details"""
        response = self._request("GET", f"/devices/{device_id}/attributes")
        return response.json()

    def get_device_metrics(self, device_id: str) -> dict:
        """Get device performance metrics"""
        response = self._request("GET", f"/devices/{device_id}/metrics")
        return response.json()

    def get_device_connection(self, device_id: str) -> dict:
        """Get device connection status"""
        response = self._request("GET", f"/devices/{device_id}/connection")
        return response.json()

    def reboot_device(self, device_id: str) -> None:
        """Reboot device"""
        self._request("POST", f"/devices/{device_id}/reboot")

    # Input-related APIs
    def get_inputs(self, class_filter: int = None) -> list:
        """Get all input IDs"""
        params = {"class_filter": class_filter} if class_filter is not None else None
        response = self._request("GET", "/inputs/", params=params)
        return response.json().get("input_ids", [])

    def get_input_details(self, input_id: str) -> dict:
        """Get input details"""
        response = self._request("GET", f"/inputs/{input_id}")
        return response.json()

    def set_input_type(self, input_id: str, input_type: str) -> None:
        """Set input type"""
        self._request("PUT", f"/inputs/{input_id}/type", json={"type": input_type})

    def set_input_volume(self, input_id: str, volume: int) -> None:
        """Set input volume"""
        self._request("PUT", f"/inputs/{input_id}/volume", json={"volume": volume})

    # Zone-related APIs
    def get_zones(self) -> list:
        """Get all zone IDs"""
        response = self._request("GET", "/zones/")
        return response.json().get("zone_ids", [])

    def get_zone_details(self, zone_id: str) -> dict:
        """Get zone details"""
        response = self._request("GET", f"/zones/{zone_id}")
        return response.json()

    def set_zone_volume(self, zone_id: str, volume: int) -> None:
        """Set zone volume"""
        self._request("PUT", f"/zones/{zone_id}/volume", json={"volume": volume})

    def set_zone_mute(self, zone_id: str, mute: bool) -> None:
        """Set zone mute"""
        self._request("PUT", f"/zones/{zone_id}/mute", json={"enable": mute})

    def set_zone_active_input(self, zone_id: str, input_id: str) -> None:
        """Set zone active input"""
        self._request("PUT", f"/zones/{zone_id}/input/active", json={"input_id": input_id})

    def set_zone_eq(self, zone_id: str, eq_values: list) -> None:
        """Set zone equalizer"""
        self._request("PUT", f"/zones/{zone_id}/volume_eq", json={"volume_eq": eq_values})

3.4 sensor.py (Device Status Sensors)

python
 
 
import logging
from homeassistant.helpers.entity import Entity
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, TEMP_CELSIUS
from .const import DOMAIN, EQ_BANDS

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities):
    api = hass.data[DOMAIN]["api"]
    config = hass.data[DOMAIN]["config"]
    device_filters = config["device_filters"]
    
    # Get all devices
    devices = await hass.async_add_executor_job(api.get_devices)
    # Apply device filters
    if device_filters:
        devices = [dev for dev in devices if dev in device_filters]
    
    sensors = []
    for device_id in devices:
        # Performance metric sensors
        sensors.extend([
            HoloDeviceMetricSensor(api, device_id, "cpu_usage", "CPU Usage", PERCENTAGE),
            HoloDeviceMetricSensor(api, device_id, "ram_usage", "RAM Usage", PERCENTAGE),
            HoloDeviceMetricSensor(api, device_id, "disk_usage", "Disk Usage", PERCENTAGE),
            HoloDeviceMetricSensor(api, device_id, "internal_temp", "Internal Temperature", TEMP_CELSIUS)
        ])
        # Connection status sensor
        sensors.append(HoloDeviceConnectionSensor(api, device_id))
    
    async_add_entities(sensors, True)

class HoloDeviceMetricSensor(Entity):
    def __init__(self, api, device_id: str, metric_type: str, name: str, unit: str):
        self.api = api
        self.device_id = device_id
        self.metric_type = metric_type
        self._name = f"Holo-Series {device_id} {name}"
        self._unit_of_measurement = unit
        self._state = None

    @property
    def unique_id(self) -> str:
        return f"holoseries_{self.device_id}_{self.metric_type}"

    @property
    def name(self) -> str:
        return self._name

    @property
    def state(self):
        return self._state

    @property
    def unit_of_measurement(self) -> str:
        return self._unit_of_measurement

    def update(self):
        try:
            metrics = self.api.get_device_metrics(self.device_id)
            self._state = metrics.get(self.metric_type, 0)
        except Exception as e:
            self._state = None
            _LOGGER.error(f"Failed to update {self.name}: {str(e)}")

class HoloDeviceConnectionSensor(Entity):
    def __init__(self, api, device_id: str):
        self.api = api
        self.device_id = device_id
        self._name = f"Holo-Series {device_id} Connection Status"
        self._state = None
        self._extra_state_attributes = {}

    @property
    def unique_id(self) -> str:
        return f"holoseries_{self.device_id}_connection"

    @property
    def name(self) -> str:
        return self._name

    @property
    def state(self):
        return self._state

    @property
    def extra_state_attributes(self) -> dict:
        return self._extra_state_attributes

    def update(self):
        try:
            conn_data = self.api.get_device_connection(self.device_id)
            conn_type = conn_data.get("type", "none")
            self._state = "connected" if conn_type != "none" else "disconnected"
            self._extra_state_attributes = {
                "ip_address": conn_data.get("ip_address", "Unknown"),
                "signal_strength": conn_data.get("signal_strength", 0),
                "ssid": conn_data.get("ssid", "Unknown"),
                "uptime": conn_data.get("uptime", 0)
            }
        except Exception as e:
            self._state = "unavailable"
            self._extra_state_attributes = {}
            _LOGGER.error(f"Failed to update {self.name}: {str(e)}")
 

3.5 media_player.py (Zone Audio Control)

 python
 
 
import logging
from homeassistant.components.media_player import (
    MediaPlayerEntity, MediaPlayerEntityFeature, MediaPlayerState
)
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from .const import DOMAIN, SUPPORTED_INPUT_TYPES, EQ_BANDS

_LOGGER = logging.getLogger(__name__)

class HoloSeriesZoneMediaPlayer(MediaPlayerEntity):
    _attr_supported_features = (
        MediaPlayerEntityFeature.VOLUME_SET
        | MediaPlayerEntityFeature.VOLUME_MUTE
        | MediaPlayerEntityFeature.SELECT_SOURCE
    )

    def __init__(self, api, zone_id: str):
        self.api = api
        self.zone_id = zone_id
        self._name = None
        self._state = MediaPlayerState.OFF
        self._volume = 0
        self._muted = False
        self._active_input = ""
        self._available_inputs = []
        self._eq_values = [0] * 10

    @property
    def unique_id(self) -> str:
        return f"holoseries_zone_{self.zone_id}"

    @property
    def name(self) -> str:
        return self._name

    @property
    def state(self) -> MediaPlayerState:
        return self._state

    @property
    def volume_level(self) -> float:
        return self._volume / 100

    @property
    def is_volume_muted(self) -> bool:
        return self._muted

    @property
    def source(self) -> str:
        return self._active_input

    @property
    def source_list(self) -> list:
        return self._available_inputs

    @property
    def extra_state_attributes(self) -> dict:
        return {f"eq_{EQ_BANDS[i]}": self._eq_values[i] for i in range(10)}

    def set_volume_level(self, volume: float) -> None:
        """Set volume (convert 0.0-1.0 to 0-100)"""
        volume_int = int(volume * 100)
        self.api.set_zone_volume(self.zone_id, volume_int)
        self._volume = volume_int

    def mute_volume(self, mute: bool) -> None:
        """Mute/unmute"""
        self.api.set_zone_mute(self.zone_id, mute)
        self._muted = mute

    def select_source(self, source: str) -> None:
        """Select active input source"""
        if source in self._available_inputs:
            self.api.set_zone_active_input(self.zone_id, source)
            self._active_input = source
            self._state = MediaPlayerState.ON

    def update(self) -> None:
        """Update zone status"""
        try:
            zone_data = self.api.get_zone_details(self.zone_id)
            self._name = zone_data.get("name", self.zone_id)
            self._volume = zone_data.get("volume", 0)
            self._muted = zone_data.get("muted", False)
            self._active_input = zone_data.get("active_input", "")
            self._available_inputs = zone_data.get("input", [])
            self._eq_values = zone_data.get("volume_eq", [0] * 10)
            
            # Determine device state
            self._state = MediaPlayerState.ON if zone_data.get("enabled", True) else MediaPlayerState.OFF
            if self._active_input:
                self._state = MediaPlayerState.ON
        except Exception as e:
            self._state = MediaPlayerState.UNAVAILABLE
            _LOGGER.error(f"Failed to update zone {self.zone_id} status: {str(e)}")

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities):
    api = hass.data[DOMAIN]["api"]
    config = hass.data[DOMAIN]["config"]
    zone_filters = config["zone_filters"]
    
    # Get all zones
    zones = await hass.async_add_executor_job(api.get_zones)
    # Apply zone filters
    if zone_filters:
        zones = [zone for zone in zones if zone in zone_filters]
    
    async_add_entities([HoloSeriesZoneMediaPlayer(api, zone_id) for zone_id in zones], True)
 

3.6 services.yaml (Service Definitions)

yaml
 
 
# Device-related services
reboot_device:
  name: Reboot Holo-Series Device
  description: Reboot a specific Holo-Series device
  fields:
    device_id:
      name: Device ID
      required: true
      example: "DynamoAmp-DEV99"
      selector:
        text:

# Input-related services
set_input_type:
  name: Set Input Type
  description: Set input type for a general class input
  fields:
    input_id:
      name: Input ID
      required: true
      example: "1"
      selector:
        text:
    type:
      name: Input Type
      required: true
      example: "USB"
      selector:
        select:
          options:
            - "Airplay"
            - "DLNA"
            - "Spotify"
            - "USB"
            - "Analog"
            - "RCA"
            - "ARC"
            - "Optical"
            - "Bluetooth"

set_input_volume:
  name: Set Input Volume
  description: Set volume for USB/RCA/Optical inputs (0-100)
  fields:
    input_id:
      name: Input ID
      required: true
      example: "1"
      selector:
        text:
    volume:
      name: Volume
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1

# Zone-related services
set_zone_eq:
  name: Set Zone Equalizer
  description: Set 10-band EQ for a zone (0-100 per band)
  fields:
    zone_id:
      name: Zone ID
      required: true
      example: "DynamoAmp-DEV99-Z1"
      selector:
        text:
    eq_31.25Hz:
      name: 31.25Hz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_62.5Hz:
      name: 62.5Hz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_125Hz:
      name: 125Hz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_250Hz:
      name: 250Hz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_500Hz:
      name: 500Hz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_1KHz:
      name: 1KHz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_2KHz:
      name: 2KHz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_4KHz:
      name: 4KHz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_8KHz:
      name: 8KHz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
    eq_16KHz:
      name: 16KHz
      required: true
      example: 50
      selector:
        number:
          min: 0
          max: 100
          step: 1
 
 

3.7 config_flow.py (UI Configuration Flow)

 python
 
 
import voluptuous as vol
import requests
from homeassistant import config_entries
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from .const import (
    DOMAIN, CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL,
    CONF_DEVICE_FILTERS, CONF_INPUT_CLASS_FILTER, CONF_ZONE_FILTERS,
    DEFAULT_PORT, DEFAULT_SCAN_INTERVAL
)

class HoloSeriesConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
    VERSION = 1

    async def async_step_user(self, user_input=None) -> FlowResult:
        errors = {}
        
        if user_input is not None:
            # Verify connection
            try:
                host = user_input[CONF_HOST]
                port = user_input[CONF_PORT]
                # Simple connection test
                response = await self.hass.async_add_executor_job(
                    requests.get, f"http://{host}:{port}/api/v3/devices/", timeout=5
                )
                response.raise_for_status()
            except Exception:
                errors["base"] = "cannot_connect"
            else:
                await self.async_set_unique_id(f"holoseries_{host}_{port}")
                self._abort_if_unique_id_configured()
                return self.async_create_entry(title=f"Holo-Series ({host})", data=user_input)
        
        # Configuration form
        data_schema = vol.Schema({
            vol.Required(CONF_HOST): str,
            vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
            vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): int,
        })
        
        return self.async_show_form(
            step_id="user", data_schema=data_schema, errors=errors
        )

    async def async_step_options(self, user_input=None) -> FlowResult:
        """Configuration options"""
        if user_input is not None:
            return self.async_create_entry(title="", data=user_input)
        
        # Get current configuration
        config = self.config_entry.options or {}
        data_schema = vol.Schema({
            vol.Optional(CONF_DEVICE_FILTERS, default=config.get(CONF_DEVICE_FILTERS, [])): vol.All(
                vol.Split(","), [str]
            ),
            vol.Optional(CONF_INPUT_CLASS_FILTER, default=config.get(CONF_INPUT_CLASS_FILTER)): vol.In([None, 0, 1, 2]),
            vol.Optional(CONF_ZONE_FILTERS, default=config.get(CONF_ZONE_FILTERS, [])): vol.All(
                vol.Split(","), [str]
            ),
        })
        
        return self.async_show_form(step_id="options", data_schema=data_schema)
 

4. Configuration & Deployment Steps

4.1 Basic Configuration (YAML Method)

 
Add to configuration.yaml:
 
yaml
 
 
holoseries:
  host: "192.168.0.115"  # Holo-Series server IP
  port: 80
  scan_interval: 30
  device_filters: ["DynamoAmp-DEV99"]  # Optional, specify devices
  input_class_filter: 0  # Optional, filter input class
  zone_filters: ["DynamoAmp-DEV99-Z1", "DynamoAmp-DEV99-Z2"]  # Optional, specify zones
 

4.2 UI Configuration Method

 
  1. Restart Home Assistant, go to “Settings > Devices & Services > Add Integration”;
  2. Search for “Holo-Series Streamer”, enter server IP and port to complete initial configuration;
  3. To adjust filters, go to the integration’s “Options” page.

4.3 Deployment Verification

 
  1. Copy the holoseries folder to Home Assistant’s custom_components directory;
  2. Restart Home Assistant service;
  3. Test plugin services (e.g., holoseries.reboot_device) in “Developer Tools > Services”;
  4. Add “Media Player” and “Sensor” cards to the dashboard to view device status and control interface.
Would you like me to generate a detailed README file for the plugin, including feature descriptions, troubleshooting guides, and automation examples?
 

Holo-Series Streamer Home Assistant Integration

 
A full-featured Home Assistant integration for Holo-Series devices (HoloWhas, HoloOne AMP, HoloOne Pro Max, etc.), enabling seamless control of device status, input sources, zone audio, and more via the Holo-Series Streamer Web API.
 

Table of Contents

Features

  • Device Monitoring: Track CPU/memory/disk usage, internal temperature, and connection status (IP address, signal strength, SSID) for all Holo-Series devices.
  • Zone Audio Control: Adjust volume, mute/unmute, select active input sources, and configure 10-band equalizer for individual zones.
  • Input Source Management: Set input types (Airplay, USB, Spotify, etc.), adjust input volume, and map inputs to specific zones.
  • System Control: Reboot devices and monitor system health via sensor entities.
  • Flexible Filtering: Restrict monitoring/control to specific devices, input classes, or zones.
  • UI & YAML Support: Configure via Home Assistant’s UI or traditional YAML.

Prerequisites

  • Home Assistant Core 2023.1.0 or higher.
  • A Holo-Series device (HoloWhas, HoloOne AMP, HoloOne Pro Max) with the latest firmware.
  • Network access between Home Assistant and the Holo-Series server (default port: 80).
  • Python requests>=2.31.0 (automatically installed by Home Assistant).

Installation

Manual Installation

  1. Download the latest release from the GitHub repository (or copy the holoseries folder from this integration package).
  2. Navigate to your Home Assistant configuration directory (where configuration.yaml is located).
  3. Create a custom_components folder if it doesn’t exist.
  4. Copy the holoseries folder into custom_components.
  5. Restart Home Assistant to load the integration.

HACS Installation (Recommended)

  1. Open HACS in Home Assistant.
  2. Go to “Integrations > Explore & Download Repositories”.
  3. Search for “Holo-Series Streamer”.
  4. Click “Download” and select the latest version.
  5. Restart Home Assistant.
Configuration

YAML Configuration

Add the following to your configuration.yaml file (adjust values as needed):
 
yaml
 
holoseries:
  host: "192.168.0.115"  # Required: IP address of your Holo-Series server
  port: 80  # Optional: Defaults to 80
  scan_interval: 30  # Optional: Status refresh interval (seconds), defaults to 30
  device_filters: ["DynamoAmp-DEV99"]  # Optional: Limit to specific device IDs
  input_class_filter: 0  # Optional: Filter inputs (0=General, 1=Spotify Zone, 2=Airplay2 Zone)
  zone_filters: ["DynamoAmp-DEV99-Z1", "DynamoAmp-DEV99-Z2"]  # Optional: Limit to specific zones
 
After saving, restart Home Assistant.

UI Configuration

  1. Go to “Settings > Devices & Services > Add Integration”.
  2. Search for “Holo-Series Streamer” and select it.
  3. Enter the Host (IP address of your Holo-Series server) and Port (default: 80).
  4. Click “Submit” – the integration will test the connection and load entities.
  5. (Optional) To adjust filters:
    • Go to the “Holo-Series Streamer” integration page.
    • Click the three dots > “Options”.
    • Enter device/zone filters (comma-separated) or select an input class filter.
    • Click “Submit”.

Usage

Entities

The integration creates the following entities for each device/zone:

Sensor Entities

  • sensor.holoseries_<device_id>_cpu_usage: CPU usage percentage.
  • sensor.holoseries_<device_id>_ram_usage: RAM usage percentage.
  • sensor.holoseries_<device_id>_disk_usage: Disk usage percentage.
  • sensor.holoseries_<device_id>_internal_temperature: Internal temperature (°C).
  • sensor.holoseries_<device_id>_connection_status: Connection state (connected/disconnected/unavailable) with attributes (IP, SSID, signal strength).

Media Player Entities

  • media_player.holoseries_zone_<zone_id>: Controls for zone audio:
    • Volume adjustment (0-100).
    • Mute/unmute.
    • Input source selection (from mapped inputs).
    • Equalizer settings (exposed as attributes: eq_31.25Hzeq_62.5Hz, etc.).

Services

The integration provides the following services (accessible via “Developer Tools > Services”):

Device Services

  • holoseries.reboot_device: Reboot a specific Holo-Series device.
    • Required parameter: device_id (e.g., “DynamoAmp-DEV99”).

Input Services

  • holoseries.set_input_type: Set the type of a general-class input.
    • Required parameters: input_id (e.g., “1”), type (e.g., “USB”, “Spotify”).
  • holoseries.set_input_volume: Adjust volume for USB/RCA/Optical inputs.
    • Required parameters: input_id (e.g., “1”), volume (0-100).

Zone Services

  • holoseries.set_zone_eq: Configure the 10-band equalizer for a zone.
    • Required parameters: zone_id (e.g., “DynamoAmp-DEV99-Z1”), plus 10 EQ band values (0-100).

Automation Examples

Example 1: Lower Zone Volume at Night

 
yaml
 
automation:
  - alias: "Lower Living Room Volume at 10 PM"
    trigger:
      - platform: time
        at: "22:00:00"
    action:
      - service: media_player.set_volume_level
        target:
          entity_id: media_player.holoseries_zone_dynamoamp_dev99_z1
        data:
          volume_level: 0.2  # 20% volume
 

Example 2: Reboot Device on High CPU Usage

 
yaml
 
automation:
  - alias: "Reboot Device if CPU > 90%"
    trigger:
      - platform: numeric_state
        entity_id: sensor.holoseries_dynamoamp_dev99_cpu_usage
        above: 90
        for:
          minutes: 5
    action:
      - service: holoseries.reboot_device
        data:
          device_id: "DynamoAmp-DEV99"
 

Example 3: Switch Input Source When Motion Detected

 
yaml
 
automation:
  - alias: "Switch to Spotify When Motion Detected"
    trigger:
      - platform: state
        entity_id: binary_sensor.living_room_motion
        to: "on"
    action:
      - service: media_player.select_source
        target:
          entity_id: media_player.holoseries_zone_dynamoamp_dev99_z1
        data:
          source: "2"  # Input ID for Spotify
 

Troubleshooting

Common Issues

 
  1. Integration Not Found:
    • Ensure the holoseries folder is correctly placed in custom_components.
    • Restart Home Assistant and clear the browser cache.
  2. Connection Failures:
    • Verify the Holo-Series server IP/port is correct.
    • Ensure no firewall is blocking traffic between Home Assistant and the Holo-Series device.
    • Check that the Holo-Series device is powered on and connected to the network.
  3. Entities Not Loading:
    • Confirm the device/zone IDs in filters are correct (use GET /api/v3/devices/ or GET /api/v3/zones/ to list IDs).
    • Check Home Assistant logs (Settings > System > Logs) for errors.
  4. Services Not Working:
    • Ensure the input/zone/device ID is valid.
    • For input-related services, confirm the input is a “General Class” (input_class=0).

Logs

To debug issues, enable debug logging by adding the following to configuration.yaml:
 
yaml
 
logger:
  default: warning
  logs:
    custom_components.holoseries: debug
View logs in “Settings > System > Logs”.

Changelog

v1.0.0

  • Initial release.
  • Support for device monitoring, input management, and zone audio control.
  • UI and YAML configuration.
  • Filtering for devices, inputs, and zones.

License

This integration is licensed under the MIT License. See the LICENSE file for details.

 
Need help with advanced configuration (e.g., custom automations, EQ presets) or have feature requests? Feel free to open an issue on the GitHub repository!
 
 
 
0