Skip to content

Backend Detection

This document describes the backend detection module for aiohomematic.

Overview

The backend detection module (aiohomematic.backend_detection) provides functionality to detect the type of Homematic backend (CCU or Homegear/PyDevCCU) and discover available interfaces without requiring a fully initialized environment.

This is useful for:

  • Auto-discovery during initial setup
  • Configuration validation
  • Diagnostic tools

Supported Backends

Backend Description Detection Method
CCU Homematic CCU3/CCU2 Version string like "3.61.345"
Homegear Homegear software Version contains "Homegear"
PyDevCCU Development CCU emulator Version contains "pydevccu"

Ports Probed

The detection probes the following ports:

Interface Non-TLS Port TLS Port
HmIP-RF 2010 42010
BidCos-RF 2001 42001
BidCos-Wired 2000 42000

For CCU backends, JSON-RPC is also queried on:

  • Port 80 (HTTP)
  • Port 443 (HTTPS)

Detection Algorithm

1. Probe XML-RPC ports in order (non-TLS first, then TLS)
2. For each port:
   a. Call system.listMethods() to verify connection
   b. Call getVersion() if available
   c. Determine backend type from version string

3. If Homegear/PyDevCCU detected:
   - Return with only BidCos-RF interface

4. If CCU detected:
   - Query JSON-RPC for available interfaces
   - Return with discovered interfaces

Usage

Basic Usage

from aiohomematic.backend_detection import detect_backend, DetectionConfig

config = DetectionConfig(
    host="192.168.1.100",
    username="admin",
    password="secret",
)

result = await detect_backend(config=config)

if result:
    print(f"Backend: {result.backend}")
    print(f"Interfaces: {result.available_interfaces}")
    print(f"Port: {result.detected_port}")
    print(f"TLS: {result.tls}")
else:
    print("No backend found")

Configuration Options

@dataclass
class DetectionConfig:
    host: str                      # Required: Host address
    username: str = ""             # Optional: Username for authentication
    password: str = ""             # Optional: Password for authentication
    request_timeout: float = 5.0   # Optional: Timeout per request (default from TimeoutConfig)
    total_timeout: float = 15.0    # Optional: Total detection timeout (default from TimeoutConfig)
    verify_tls: bool = False       # Optional: Verify TLS certificates

Timeout Configuration:

The default timeout values are taken from TimeoutConfig:

  • request_timeout: Individual request timeout (default: 5s, 1s in test mode)
  • total_timeout: Total detection timeout (default: 15s, 3s in test mode)

You can override timeouts in two ways:

  1. Set them explicitly in DetectionConfig
  2. Pass a custom TimeoutConfig to detect_backend()

Result Structure

@dataclass
class BackendDetectionResult:
    backend: Backend              # CCU, HOMEGEAR, or PYDEVCCU
    available_interfaces: tuple[Interface, ...]  # Detected interfaces
    detected_port: int            # Port where backend was found
    tls: bool                     # Whether TLS is used
    host: str                     # Host address
    version: str | None           # Backend version string
    auth_enabled: bool | None     # Whether auth is enabled (CCU only)

Using with aiohttp Session

You can provide an existing aiohttp ClientSession to reuse connections:

import aiohttp
from aiohomematic.backend_detection import detect_backend, DetectionConfig

async with aiohttp.ClientSession() as session:
    config = DetectionConfig(host="192.168.1.100")
    result = await detect_backend(config=config, client_session=session)

Using with Custom TimeoutConfig

You can override timeout defaults using a custom TimeoutConfig:

from aiohomematic.backend_detection import detect_backend, DetectionConfig
from aiohomematic.const import TimeoutConfig

# Create custom timeout configuration
timeout_config = TimeoutConfig(
    backend_detection_request=10.0,  # 10 seconds per request
    backend_detection_total=30.0,    # 30 seconds total
)

# Create detection config (can still override individual timeouts)
config = DetectionConfig(host="192.168.1.100")

# Pass timeout_config to override defaults
result = await detect_backend(
    config=config,
    timeout_config=timeout_config
)

Note: If you pass timeout_config, it will override the timeout values in DetectionConfig. This is useful when you want to use a consistent timeout configuration across your application.

Logging

All detection operations are logged at INFO level:

detect_backend: Starting detection for host 192.168.1.100
detect_backend: Probing 192.168.1.100:2010 (TLS=False, interface=HmIP-RF)
detect_backend: Found version '3.61.345' on port 2010
detect_backend: Detected backend type: CCU
detect_backend: Querying JSON-RPC at http://192.168.1.100:80/api/homematic.cgi
detect_backend: Found interfaces via JSON-RPC: (Interface.HMIP_RF, Interface.BIDCOS_RF)

Error Handling

The detection module catches all exceptions internally and returns None if no backend can be detected. Errors are logged at INFO level:

result = await detect_backend(config=config)

if result is None:
    # No backend found - check logs for details
    print("Could not detect backend")

Examples

Detect CCU with Multiple Interfaces

config = DetectionConfig(
    host="192.168.1.100",
    username="Admin",
    password="secret123",
)

result = await detect_backend(config=config)

# Result:
# BackendDetectionResult(
#     backend=<Backend.CCU>,
#     available_interfaces=(Interface.HMIP_RF, Interface.BIDCOS_RF, Interface.BIDCOS_WIRED),
#     detected_port=2010,
#     tls=False,
#     host="192.168.1.100",
#     version="3.61.345",
#     auth_enabled=True,
# )

Detect Homegear

config = DetectionConfig(host="192.168.1.100")

result = await detect_backend(config=config)

# Result:
# BackendDetectionResult(
#     backend=<Backend.HOMEGEAR>,
#     available_interfaces=(Interface.BIDCOS_RF,),
#     detected_port=2010,
#     tls=False,
#     host="192.168.1.100",
#     version="Homegear 0.8.0",
#     auth_enabled=None,
# )

Detect with TLS

config = DetectionConfig(
    host="192.168.1.100",
    verify_tls=True,  # Verify TLS certificates
)

result = await detect_backend(config=config)

if result and result.tls:
    print(f"Backend found on TLS port {result.detected_port}")

Integration with CentralConfig

After detection, you can use the result to create a CentralConfig:

from aiohomematic.backend_detection import detect_backend, DetectionConfig
from aiohomematic.central import CentralConfig
from aiohomematic.client import InterfaceConfig

# Detect backend
detection_config = DetectionConfig(
    host="192.168.1.100",
    username="admin",
    password="secret",
)
result = await detect_backend(config=detection_config)

if result:
    # Create interface configs from detected interfaces
    interface_configs = frozenset(
        InterfaceConfig(
            central_name="my-ccu",
            interface=iface,
            port=_get_port_for_interface(iface, result.tls),
        )
        for iface in result.available_interfaces
    )

    # Create central config
    central_config = CentralConfig(
        name="my-ccu",
        host=result.host,
        username=detection_config.username,
        password=detection_config.password,
        central_id="unique-id",
        interface_configs=interface_configs,
        tls=result.tls,
    )

Limitations

  • Detection requires network access to the backend
  • JSON-RPC interface query requires valid credentials for CCU with authentication enabled
  • Unknown interfaces returned by the backend are skipped
  • The module does not cache detection results