Files
2026-08-12 15:31:50 +10:00

9.7 KiB
Raw Permalink Blame History

Riedel RRCS for Home Assistant

A custom integration that talks to Riedel Router Control Software (RRCS), the third-party gateway to an Artist intercom system. Built against the RRCS 8.9.1.Rev1 interface specification.

It gives you logic sources and GPIOs as real Home Assistant entities, alarms and panel events on the event bus, and services for crosspoints, keys, labels and gains.

Integration, not add-on. Home Assistant add-ons are Supervisor-only Docker containers and cannot create entities. This is a custom integration, so it works the same on HAOS, Home Assistant Container and Core.


What you get

Entities

Platform Entity Source
switch One per logic source GetAllLogicSources_v2 / SetLogicSourceState
switch One per GP output GetGpOutputState / SetGpOutput
binary_sensor One per GP input GetGpInputState
binary_sensor Artist connection IsConnectedToArtist
sensor Gateway state (Working / Standby) GetState
sensor Active crosspoints GetAllActiveXpsCount
sensor RRCS version, logic source count diagnostic, disabled by default
event Alarm ring, node, client and port alarms (ch. 9.7)
event Send string SendString / SendStringOff
event Panel spy panel spy events, disabled by default

Services

set_xp, kill_xp, set_xp_volume, set_gp_output, set_logic_source, press_key, set_key_label, clear_key_label, set_port_alias, set_input_gain, set_output_gain, and call_method as an escape hatch to any RRCS method that has no dedicated service.

Events

Every inbound notification is fired on the bus as riedel_rrcs_notification with entry_id, method and params.


Installation

HACS

Add this repository as a custom repository of type Integration, install it, restart Home Assistant, then add Riedel RRCS from Settings → Devices & Services → Add Integration.

Manual

Copy custom_components/riedel_rrcs/ into your Home Assistant config/custom_components/ directory and restart.


Configuration

Everything is done in the UI. The initial dialog asks for:

Field Notes
Host The machine running RRCS
Port 8193 by default
RPC path / unless the gateway is behind a reverse proxy
Request timeout Seconds
Transaction key prefix A single character. Avoid R, which RRCS reserves for its own requests

The connection is tested with GetVersion before the entry is created.

Options

Option Default Notes
Polling interval 30 s Raise it if push notifications are working
Receive push notifications on See below
Callback port HA's HTTP port Override if HA serves HTTPS
Discover GPIOs automatically on Requires RRCS 8.8 or later
Poll GPIO states on Turn off to reduce gateway load once push is confirmed
Manual GPIO list empty JSON, see below

Push notifications

With notifications enabled, the integration calls RegisterForAllEvents and serves an endpoint at /api/riedel_rrcs/<random-token>. RRCS then reverses the roles and posts XML-RPC method calls at Home Assistant, so logic source, GPIO, crosspoint and alarm changes arrive immediately rather than at the next poll.

Three things to know:

  1. The endpoint is unauthenticated. RRCS has no way to present a bearer token, so the path carries a random secret instead. It is only reachable from whatever can already reach your Home Assistant HTTP port.
  2. RRCS derives the destination host from the source address of the registration request. The gateway has to be able to reach Home Assistant back on that same IP, on the callback port, over plain HTTP. If Home Assistant serves HTTPS you must set a plain-HTTP callback port in the options; a warning is logged if this looks wrong.
  3. Answer or be dropped. If KeepAlive is enabled in the RRCS route options, RRCS sends GetAlive every three seconds of inactivity and tears the channel down if it goes unanswered. This integration answers it. It also re-checks the registration on every poll with IsRegisteredForAllEvents and re-registers if RRCS has forgotten us, which it does after a restart.

GPIOs

Automatic discovery

GetAllGpIns and GetAllGpOuts arrived in RRCS 8.8, but the specification documents them by name only — the return struct is literally ... on page 40. Discovery here is a tolerant walk over whatever comes back, mapping the three TGPIOAddress variants from chapter 6.7 onto the Net/Node/Port/Slot/Index addressing that Set/GetGpOutput actually wants.

That means the Bay-to-Slot mapping in particular is an educated guess. If the discovered entities look wrong, download the diagnostics for the config entry: the raw GetAllGpIns / GetAllGpOuts payloads are included verbatim.

Manual list

Anything you define manually wins over discovery. The options flow takes a JSON array:

[
  {"name": "Studio A tally", "direction": "in",  "node": 2, "port": 128, "slot": 0, "index": 3},
  {"name": "Red light",      "direction": "out", "node": 2, "port": 128, "slot": 0, "index": 5},
  {"name": "Panel GPI",      "direction": "in",  "node": 4, "port": 9,   "index": 0}
]

node and index are required. Defaults are net 1, port 128, slot 0 and direction in.

Addressing follows chapter 6.9:

  • GPIO client card — port is 128 and slot selects the bay: 0–15 for bays 1–16, 16 for bay X, 17 for bay Y, 20 for bay B.
  • Panel GPIO — port is the panel's port address and slot is ignored.
  • Port address for Artist 32/64/128 is ((slot - 1) * 8) + port - 1, so port 5 in slot 2 is address 12.

Examples

Toggle a logic source from a physical button

This replaces toggle_v5.vbs — the logic source is just a switch now.

automation:
  - alias: Toggle busy from desk button
    triggers:
      - trigger: state
        entity_id: binary_sensor.desk_button
        to: "on"
    actions:
      - action: switch.toggle
        target:
          entity_id: switch.rrcs_ben_busy

Press a panel key, primary trigger

The XML-RPC equivalent of ras_pi_RRCS_BusyLight.py, press and release:

script:
  ben_busy_pulse:
    sequence:
      - action: riedel_rrcs.press_key
        data:
          node: 64
          port: 46
          page: 2
          expansion_panel: 0
          key_number: 1
          is_virt_key: true
          trigger: 1
          press: true
      - delay: "00:00:00.25"
      - action: riedel_rrcs.press_key
        data:
          node: 64
          port: 46
          page: 2
          expansion_panel: 0
          key_number: 1
          is_virt_key: true
          trigger: 1
          press: false

Route audio on an event

- action: riedel_rrcs.set_xp
  data:
    source_node: 2
    source_port: 12
    dest_node: 3
    dest_port: 4
    priority: "2"

Set destructive: true to drop any existing route to the destination first. Passing net, node and port all as 0 clears every route for a source or destination.

React to a ring alarm

automation:
  - alias: Notify on upstream failure
    triggers:
      - trigger: state
        entity_id: event.rrcs_alarm
    conditions:
      - condition: template
        value_template: >-
          {{ trigger.to_state.attributes.event_type in
             ['UpstreamFailed', 'DownstreamFailed', 'NodeControllerFailed'] }}
    actions:
      - action: notify.ntfy
        data:
          message: >-
            RRCS {{ trigger.to_state.attributes.event_type }}:
            {{ trigger.to_state.attributes.params }}

Or catch everything on the bus:

triggers:
  - trigger: event
    event_type: riedel_rrcs_notification
    event_data:
      method: PortInactive

Call anything else

- action: riedel_rrcs.call_method
  data:
    method: GetAllPorts
  response_variable: ports
- action: notify.persistent_notification
  data:
    message: "{{ ports.result[1] | count }} ports"

call_method prepends a transaction key for you unless you turn that off. The decoded response comes back under result.


Notes and limitations

  • RRCS only undoes its own work. SetLogicSourceState, SetGpOutput and KillXp only affect state that RRCS itself set. A logic source driven from a panel key will not respond to "off", and GetXpStatus can still report true after a KillXp.
  • One request at a time. Requests are serialised behind a lock. RRCS is a single Windows service in front of a live ring, and chapter 12's timing figures (10 ms for queries, 50 ms for state changes, seconds for configuration changes) all assume an otherwise idle gateway. Don't set the polling interval aggressively low on a large system.
  • No configuration changes. ConfigurationChange and friends are not wrapped as services deliberately — they rewrite the Artist configuration and are better done in Director. call_method will still let you if you mean it.
  • Standby gateways. In a redundant pair, a standby gateway answers only the redundancy command set, so most entities will be unavailable against one. GetState and the connection sensor still work.
  • Crosspoint volume does not work for destinations on an Artist 1024.
  • Encoding: some gateways declare UTF-8 but emit single-byte port labels. The parser strips the declaration and decodes leniently rather than dropping the whole response.

Troubleshooting

Turn on debug logging to see every RPC in both directions:

logger:
  logs:
    custom_components.riedel_rrcs: debug

Config entry diagnostics include the coordinator state, the resolved GPIO addresses and the raw GPIO list payloads.