# 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/`. 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: ```json [ {"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. ```yaml 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: ```yaml 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 ```yaml - 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 ```yaml 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: ```yaml triggers: - trigger: event event_type: riedel_rrcs_notification event_data: method: PortInactive ``` ### Call anything else ```yaml - 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: ```yaml logger: logs: custom_components.riedel_rrcs: debug ``` Config entry diagnostics include the coordinator state, the resolved GPIO addresses and the raw GPIO list payloads.