Docs menu
Guides

Listeners

Let other systems trigger Tangle with their own messages - WebSocket, HTTP, TCP, UDP, OSC or serial - matched by rules that set variables, run steps and automations, and answer the sender.

A listener opens a port (or a serial line) for other systems: a touch panel, a show controller, a Stream Deck through Companion, QLab, TouchDesigner, a building management system, a webhook. They send their own strings, JSON or OSC, in their own shape. Each message is checked against the listener's rules: a matching rule sets variables, runs steps and automations, starts timelines and answers the sender.

The REST API and the WebSocket API do the same with Tangle's own messages and a token. Use a listener when the other system cannot be changed to speak them.

Listeners are stored in the project (project.listeners, contract 0.2.25 and later) and opened by the primary node, or by the node named in node.

Make a listener

  1. Click Listeners in the project tree, then New listener.
  2. Give it a Name. Module stays on Built-in listeners unless you installed another listener module.
  3. Under Where it listens, choose the Transport and fill in its settings:
TransportSettingsA message is
websocketPort, WebSocket path (default /), Listen on addressOne text frame. The reply goes back on the same connection.
httpPort, Listen on addressOne request. The reply is the HTTP response.
tcpPort, Listen on address, Message format, End of messageOne line (or up to the end-of-message characters you set, such as \r\n or \x03).
udpPort, Listen on address, Message formatOne datagram. With format osc, one OSC message (bundles are split).
serialSerial port, Baud rate, Data bits, Parity, Stop bits, Message format, End of messageOne line. The serial port can be a network serial port (tcp://gateway:4001), see Devices.

Message format is text, json, hex (raw bytes shown as hex) or osc. Listen on address 0.0.0.0 listens on every network card, 127.0.0.1 on this computer only. Ports under 1024 need Tangle to run with more rights.

  1. Under Who may send:
    • Allowed hosts: addresses or ranges (one per line, 192.168.1.0/24). Empty: this computer and private networks only. * allows everyone.
    • Token: a secret senders must give, as Authorization: Bearer <token> or ?token=<token> over HTTP and WebSocket, or as the first line over TCP and serial. It is kept in the credential store and never shown again.
    • Messages per second (default 50), Burst (100) and Largest message (65536 bytes). Extra messages are dropped and counted, never queued.
  2. Add rules, then click Create listener.

Open closes the listener without deleting it when unticked.

Rules

Rules are checked in order and the first that matches wins, unless it has Keep checking the next rules. Each rule has a Match:

MatchMatchesCaptures
Any messageEvery message
Exact textThe whole message (a trailing line end is ignored), optionally ignoring case
Starts withMessages that start with the text{{rest}}
Regular expressionA regular expression (flags i, m, s, x)Named groups as {{name}}, every group as {{_1}}, {{_2}}…
JSON valueThe message parsed as JSON, optionally at a path ($.cue.number){{value}}, and named paths
OSC addressAn OSC address pattern (/cue/*/go, ?, [a-z], {a,b})Wildcards as {{_1}}…, arguments as {{args}}, {{arg1}}…
HTTP routeA method and a path (/cue/:number/*):name as {{name}}, * as {{rest}}, plus {{query}}, {{headers}} and {{body}}

Every run also gets {{listener}}, {{rule}}, {{message}}, {{json}} (the message as JSON, when it parses) and {{from}} (the sender's address). Captures that look like numbers become numbers, unless the rule has Keep captures as text.

What a matching rule does:

  • Set variables: each variable gets a template ({{number}}) or an expression. A project variable is set for everyone (and its variable triggers fire); any other name is a run variable for the rule's steps.
  • Steps: any automation step: an intent, a device action, a timeline, Run an automation, an if, and so on, with the captures as {{name}}.
  • Answer: a reply template. "{{name}}" alone keeps the capture's type; "OK {{cue}}" fills it in as text; JSON objects are filled inside. Over HTTP you can also set the status and headers. Answer when the steps are done waits for them (at most 10 s) before replying.

Automations can also start on a rule with the trigger A listener message matches (Automations). That keeps the work in one automation that a schedule or a button can run too.

When no rule matches sets the reply for other messages (HTTP answers 404 by default) and whether they are logged.

This HTTP listener lets any system on the network start and stop the room built in the tutorial with a plain HTTP request:

{
  "id": "gallery-http",
  "name": "Gallery HTTP",
  "module": "tangle.builtin.listener",
  "config": { "transport": "http", "port": 9100 },
  "rules": [
    {
      "id": "start", "name": "Start",
      "match": { "kind": "http", "method": "POST", "path": "/gallery/start" },
      "steps": [{ "kind": "automation", "automationId": "start-installation", "wait": false }],
      "reply": "OK"
    },
    {
      "id": "stop", "name": "Stop",
      "match": { "kind": "http", "method": "POST", "path": "/gallery/stop" },
      "steps": [{ "kind": "automation", "automationId": "stop-installation", "wait": false }],
      "reply": "OK"
    }
  ]
}
curl -X POST http://<core address>:9100/gallery/start     # OK

Other examples:

  • QLab or a lighting console sends OSC /tangle/cue/12 over UDP: an OSC address rule /tangle/cue/* runs a timeline step go with cue {{_1}}.
  • A control system sends SCENE 3\r over TCP: a Regular expression rule ^SCENE (?<n>\d+)$ sets the project variable scene to {{n}}, and an automation that starts when scene changes does the rest.
  • A webhook posts JSON: a JSON value rule at $.event with the value door.open runs the steps.

Messages and tests

The listener page shows its Status (opening, listening, error, closed) and the Messages log, live: each message with its sender, the rule that matched, the captures, the runs it started and the reply. Filter it by matched, unmatched, dropped or test messages.

Try it sends a message to the saved rules without a real sender. By default it is a dry run that only shows what would match; tick Also set variables and run the steps to run them as a real message would.

API

  • REST: GET /api/v1/listeners (with status), POST /api/v1/listeners, GET, PUT and DELETE /api/v1/listeners/{id}, GET /api/v1/listeners/{id}/log, GET /api/v1/listeners/{id}/runs, POST /api/v1/listeners/{id}/test ({"text": "SCENE 3", "run": false}; bytes, osc or http for other kinds of message).
  • Events: listener.message:<id> for every message received.
  • MCP: get_listeners, get_listener_runs, test_listener, and create_listener / update_listener.

Listener modules

The built-in listener (tangle.builtin.listener) covers WebSocket, HTTP, TCP, UDP (text, JSON, hex, OSC) and serial. Other modules can open listeners too: a module of category listener declares its transports and settings in a listener block of its manifest, and only owns the socket and the framing. Core does the matching, the security checks, the rate limits, the log and the tester. See Module SDK.