Integrations · User guide

MQTT for CueTracker

Publish CueTracker events for club automations, Home Assistant and live venue experiences.

MQTT eventsConfigurable QoS

How it works

Each club connects CueTracker to its own MQTT broker and selects the match and competition events it wants to publish. Every club has an isolated, asynchronous client, so a slow or unreachable broker does not block another club. Open the club, choose Settings, expand Broker connection, then enter the broker details and test the connection. Automations subscribe to the club event stream or an individual match state topic.

Use a dedicated broker account for CueTracker. Broker passwords are encrypted at rest and credentials are redacted from connection errors. Keep controller, display, session and API credentials separate from MQTT broker credentials.

Real-world venue ideas

MQTT is best for actions that should happen immediately when somebody uses the Controller screen. CueTracker publishes information outward; MQTT subscribers cannot change scores or control a match.

Call players to a table

Use match.called_to_table to announce both sides and the allocated table over speakers, send a phone notification or update a waiting-area screen before the first break.

Master achievement party

Use achievement.master_break or achievement.master_first_visit to run a short lighting scene, sound effect or celebration animation for the named player.

Table-ready lighting

Turn a table light green when a match is called, change it during live play and return it to its normal scene after match.finished.

Shot-clock warnings

Flash an LED strip or sound a quiet warning at the configured warning and alert thresholds, then use a stronger signal for shot_clock.expired.

Streaming and recording

Start an OBS scene or NVR recording when play starts, add chapter markers after frames and stop or archive the recording when the match finishes.

Scoreboard and signage

Subscribe to retained match-state topics so a custom display, LED scoreboard or foyer panel can recover the latest score immediately after it reconnects.

Other useful ideas include a referee call button workflow after a foul, accessibility announcements, sponsor animations between frames, a “next match” display and a private club activity log. Keep celebrations brief so they do not interfere with nearby tables.

Topics

cuetracker/<club-id>/availability

Retained online or offline status for the club publisher.

cuetracker/<club-id>/events

Club-wide event stream containing only the selected subscription events.

cuetracker/<club-id>/match/<match-id>/state

Retained current state for a specific match. It updates after every scoring or clock action, including a shot-clock reset, but it is not an event subscription.

CueTracker permanently assigns cuetracker/<club-id> to the club. The prefix is read-only, remains stable when the club is renamed and prevents two clubs from publishing to the same topics.

Event names

  • frame.started
  • frame.finished
  • match.called_to_table
  • match.started
  • match.paused
  • match.resumed
  • match.finished
  • competition.started
  • competition.finished
  • turn.changed
  • ball.potted
  • foul.recorded
  • achievement.master_break
  • achievement.master_first_visit
  • shot_clock.reset
  • shot_clock.warning
  • shot_clock.alert
  • shot_clock.expired

Select the event types CueTracker publishes in the club’s MQTT settings. Existing clubs can enable the two achievement events by reopening Broker connection and saving their event selections.

EventWhen it is published
match.called_to_tableA match is allocated and ready for its players, before the initial frame has started. The payload includes the table and both teams’ player names.
match.pausedThe controller’s Pause action pauses the active visit and shot clock.
match.resumedThe controller’s Resume action restarts the paused visit and shot clock.
turn.changedThe active side or player changes through the turn control.
ball.pottedOne or more balls are recorded as potted.
foul.recordedRecord foul is used for the active player on the Controller screen.
achievement.master_breakA player legally pots all eight balls in the opening visit after breaking.
achievement.master_first_visitA player who did not break legally pots all eight balls on their first visit.
shot_clock.resetThe shot clock is started or manually reset.
shot_clock.warningThe running clock reaches that match’s enabled Warning at value.
shot_clock.alertThe running clock reaches that match’s enabled Alert at value.
shot_clock.expiredThe running clock reaches zero.

Master achievement events are published only when the recorded frame data proves the achievement; they are not inferred from a final score. Their payload identifies the achievement type, player, side, frame and eight-ball run. Warning and alert messages follow each match’s configured timer thresholds. A disabled warning or alert does not publish its corresponding event. Each threshold publishes once per shot, and resetting the clock begins a new shot.

Event payload

JSON event example
{
  "schemaVersion": 1,
  "messageType": "event",
  "eventId": "unique-event-id",
  "event": "frame.finished",
  "occurredAt": "2026-09-10T12:30:00.000Z",
  "club": {
    "id": "club-id",
    "name": "Example Club"
  },
  "match": {
    "id": "match-id",
    "name": "Friday Final",
    "format": "singles",
    "table": "Table 1",
    "teams": {
      "A": { "name": "Craig", "players": ["Craig"] },
      "B": { "name": "Taylor", "players": ["Taylor"] }
    }
  },
  "frame": {
    "number": 3,
    "winner": "Craig",
    "score": {
      "A": 2,
      "B": 1
    }
  }
}
Master achievement detail block
{
  "event": "achievement.master_break",
  "match": {
    "id": "match-id",
    "table": "Table 1",
    "teams": {
      "A": { "name": "Craig", "players": ["Craig"] },
      "B": { "name": "Taylor", "players": ["Taylor"] }
    }
  },
  "frame": {
    "number": 3,
    "winner": "Craig",
    "score": { "A": 2, "B": 1 }
  },
  "achievement": {
    "type": "master-break",
    "frame": 3,
    "side": "A",
    "player": "Craig",
    "run": 8
  }
}

Every event has the same required envelope: schemaVersion, messageType, eventId, event, occurredAt and club. Match, competition, frame, turn, ball, foul, pause, achievement and shot-clock blocks are added only when relevant. Each logical event receives one UUID event ID before it enters the durable outbox; a retry keeps that same ID.

Match blocks include the table plus both team names and player lists, making call-up announcements possible without another lookup. Turn events include previous and current side/player details. Pot events include the side, player, number potted and balls remaining. Foul events include the frame, side, player and that player’s match total. Pause and resume events include the active frame, side and player. Achievement events include the verified type, player, side, frame and run. Shot-clock events include the frame, side, player, total limit and threshold remaining.

Schema versioning

Schema version 1 is the current contract. CueTracker validates the version when building every event envelope. Adding an optional field or a new event name remains version 1; removing or renaming a field, changing its type, or changing required envelope semantics requires a new schema version. Consumers should reject unsupported major schema versions and ignore unknown optional fields.

VersionCompatibility record
1Original common event envelope, per-event detail blocks, UUID event IDs and ISO UTC occurrence timestamps.

Delivery behaviour

AreaBehaviour
Quality of serviceChoose QoS 0, 1 or 2 in Club Settings. New connections default to QoS 1.
Event retentionEvent messages are not retained by the broker.
Match stateState messages are retained and identify themselves with messageType: match.state plus the action in cause. Pending state for the same match is coalesced to its latest value.
AvailabilityOnline/offline status is retained.
DuplicatesConsumers deduplicate event messages using the eventId when QoS can redeliver.
OrderingOne serial outbox is flushed per club. A failed event remains first in line, so later events from that club cannot overtake it.
Occurrence timeoccurredAt is written when the action happens and is retained unchanged while an event waits or retries.
Offline backlogUnpublished messages are stored durably and replayed after reconnect. Each club keeps at most 10,000 pending messages by default; pending match state is coalesced and, only if the cap is reached, the oldest non-retained events are discarded first.
ReconnectCueTracker reconnects independently per club with exponential backoff from one second to a 60-second cap.
Plan downgradeThe saved broker, credentials, topic and event choices remain in Club Settings, but publishing and reconnects are disabled while the club’s plan does not include MQTT.
SubscriptionsEach club selects the event types it publishes.
TLSAlways use TLS for a public broker. CueTracker shows a prominent security warning when a public broker is configured without it; plaintext remains available for existing integrations and private LAN brokers. TLS connections validate the broker certificate; when connecting to a LAN IP, enter the DNS name on the certificate in TLS certificate hostname.

Subscribe to the exact /events topic for automations based on event names. A wildcard subscription also receives retained match-state updates and availability messages. CueTracker operators monitor active connections, last successful publish time, publish successes and failures, reconnects, pending messages and cap drops; broker credentials are never included in those metrics.

Home Assistant use

These examples listen to the same club event topic but respond only to one selected event name.

Call both sides to their table

Home Assistant YAML
alias: CueTracker - call players to table
mode: queued
trigger:
  - platform: mqtt
    topic: cuetracker/club-id/events
condition:
  - condition: template
    value_template: "{{ trigger.payload_json.event == 'match.called_to_table' }}"
action:
  - service: notify.mobile_app_club_manager
    data:
      title: "Match ready · {{ trigger.payload_json.match.table }}"
      message: >-
        {{ trigger.payload_json.match.teams.A.players | join(', ') }} versus
        {{ trigger.payload_json.match.teams.B.players | join(', ') }}.
        Please report to {{ trigger.payload_json.match.table }}.

Celebrate a Master Break or Master First Visit

Home Assistant YAML
alias: CueTracker - master achievement party
mode: queued
trigger:
  - platform: mqtt
    topic: cuetracker/club-id/events
condition:
  - condition: template
    value_template: >-
      {{ trigger.payload_json.event in
         ['achievement.master_break', 'achievement.master_first_visit'] }}
action:
  - service: light.turn_on
    target:
      entity_id: light.pool_room
    data:
      flash: short
      color_name: purple
  - service: media_player.play_media
    target:
      entity_id: media_player.pool_room
    data:
      media_content_id: https://example.com/master-achievement.mp3
      media_content_type: music

The event’s achievement.player and achievement.type values can also be used in a spoken announcement or display animation. For reliable real-world use, store the event ID in the automation so repeated QoS 1 delivery does not call the players or play the same celebration twice.