TapHome

Paradox Alarm Panels via PAI (MQTT)

Packet Parser → MQTT
Submitted by
Last updated: 11. 2026

Requires external gateway

PAI (Paradox Alarm Interface, open-source MQTT bridge) — product page

Paradox Alarm Panels via PAI (MQTT)

The Paradox PAI template connects Paradox alarm panels — Spectra SP, Magellan MG and Digiplex EVO — to TapHome over MQTT through PAI, the Paradox Alarm Interface. PAI is an open-source bridge, not a Paradox product. It runs on a small computer next to the panel (for example a Raspberry Pi, a NAS or a Home Assistant host), talks to the panel over its serial port or an IP150 module, and publishes the state of the panel to an MQTT broker. TapHome reads those messages and sends arming commands back. Paradox does not publish the protocol PAI uses.

The template shows the state of one partition and arms or disarms it, shows four zones with their warnings, and switches one PGM output. For Digiplex EVO panels with the PRT3 module there is a separate template that uses the official protocol of Paradox and needs no computer: Paradox Digiplex EVO via PRT3 (TCP).

Community template — not yet tested with TapHome on a real panel. The template was generated in the TapHome portal from the PAI documentation and tested only against a simulated PAI and panel on a local TapHome Core 2025.2, not on a real Paradox panel. Verify it on your installation before you rely on it for security; feedback from the field is welcome. Download the XML on this page, or get it from the TapHome portal: Paradox via PAI — community template, and import it as a new module.

Security: anyone who can publish to the broker can disarm the alarm. PAI arms and disarms the panel without the user code of the panel. Turn on a username and password on the MQTT broker (the broker built into TapHome supports it) and enter them in PAI and in this template.

Supported panels

FamilyModelsNotes
Spectra SPSP4000, SP5500, SP6000, SP7000, SP65On the “+” models (SP5500+, SP6000+, SP7000+) the serial port is locked for devices of other manufacturers. Paradox sells an unlock code, or connect PAI through an IP150
Magellan MGMG5000, MG5050, MG5050+, MG5075MG5050+ has the locked serial port as well. MG6250 is only experimental in PAI
Digiplex EVOEVO48, EVO96, EVO192, EVOHDEVO firmware 7.50 and newer encrypts the serial port; PAI 3.8 supports it with the PC password. EVO has no Sleep arming

Hardware connection

PAI connects to the panel in one of these ways:

  • Serial port of the panel — with a USB-to-TTL adapter to the computer running PAI, or with an ESP32 serial bridge on the local network.
  • IP150 module on the local network. An IP150 accepts one client at a time: while PAI is connected, the Paradox apps Insite Gold and BlueEye cannot connect through the same module. An IP150+ with firmware 6.x and the IP180 do not allow local connections.

The computer with PAI and the TapHome Core have to reach the same MQTT broker — usually the broker built into the TapHome Core.

Configuration

MQTT broker in TapHome

Turn on the MQTT Broker in TapHome and set a username and password — see MQTT Broker. PAI and this template log in to the broker with them.

PAI

In the PAI configuration, point MQTT to the broker TapHome uses. For the broker built into TapHome this is the IP address of the TapHome Core — the name taphome.mqtt.broker.local works only inside the Core, not for PAI:

1
2
3
4
5
6
7
MQTT_ENABLE: true
MQTT_HOST: 192.168.1.10        # IP address of the TapHome Core with the built-in broker
MQTT_PORT: 1883
MQTT_USERNAME: pai
MQTT_PASSWORD: 'replace-me'
MQTT_BASE_TOPIC: paradox
MQTT_RETAIN: true              # default: the broker keeps the last state for TapHome

MQTT_USE_NUMERIC_STATES may stay at its default (False); the template reads both True/False and 1/0.

Keys of the partition, zones and PGM

PAI names each partition, zone and PGM by its label in the panel, with spaces and special characters replaced by _ (for example Front door → Front_door). Read the keys from the broker after PAI starts, for example:

1
mosquitto_sub -h 192.168.1.10 -u pai -P 'replace-me' -t 'paradox/states/#' -v
1
2
3
paradox/states/partitions/Area_1/current_state disarmed
paradox/states/zones/Front_door/open False
paradox/states/outputs/Gate/on False

The part after partitions/, zones/ and outputs/ is the key. Keys are case-sensitive.

Import parameters

Import parameterDescriptionDefault
MQTT broker addresstaphome.mqtt.broker.local for the broker built into TapHome, or the address of another brokertaphome.mqtt.broker.local
MQTT broker portPort of the broker1883
MQTT broker username, passwordThe broker account(empty)
PAI base topicMQTT_BASE_TOPIC of PAIparadox
Partition keyKey of the partitionArea_1
Zone 1–4 keysKeys of four zonesZone_1 … Zone_4
PGM output keyKey of one PGM outputPGM_1

For more partitions or zones, import the template again with other keys and delete the devices you do not need.

Device capabilities

Partition

Shows the state of the partition and arms or disarms it.

StatePAI current_stateWhen selected in TapHome
Disarmeddisarmedsends disarm
Armedarmed_awaysends arm
Armed Stayarmed_homesends arm_stay
Armed Sleeparmed_nightsends arm_sleep (SP and MG only)
Armingarming (exit delay)sends nothing
Alarmtriggeredsends nothing

Commands are published to paradox/control/partitions/<key> without the retain flag, as PAI requires. Arming and Alarm are states reported by the panel and never send a command. When the TapHome Core starts, the partition takes its state from the retained PAI message and sends nothing.

Zones 1–4

Each zone shows whether it is open or closed. Warnings show when the zone is in alarm, tampered or bypassed, or when a wireless zone has a low battery. Each warning stays until PAI reports the state as cleared.

PGM output

Switches one PGM output of the panel on or off and shows its state. Whether a PGM can be switched depends on the panel and on how the installer programmed it. When the TapHome Core starts, the PGM takes its state from PAI and is not switched.

Module

The module shows an error when PAI reports itself offline (paradox/interface/availability), for example when PAI lost the connection to the panel or stopped.

Without PAI

A PGM output of the panel wired to a TapHome input (armed, alarm) and a TapHome relay wired to a keyswitch zone of the panel work with every Paradox panel, with fewer states and no zone details.

Troubleshooting

No states in TapHome

Check that PAI runs and publishes (mosquitto_sub … -t 'paradox/#'), that the base topic and the keys match exactly, and that MQTT_RETAIN is on in PAI.

Arming does nothing

Check that PAI and TapHome use the same broker, and read paradox/interface/command_status for the answer of PAI. On EVO, Armed Sleep is not available.

Module error “PAI reports offline”

PAI lost the panel or stopped; see the PAI log.

PAI does not connect to the panel

On SP5500+, SP6000+, SP7000+ and MG5050+ the serial port is locked — ask Paradox for the unlock code, or connect through an IP150. While PAI is connected through an IP150, close Insite Gold and BlueEye. An IP150+ with firmware 6.x or an IP180 does not allow PAI to connect locally.

Available devices

Paradox PAI MQTT Bridge Module
Custom Variables
baseTopic (string)Base topic of PAI (MQTT_BASE_TOPIC in the PAI configuration), default paradox
Enter the same value as MQTT_BASE_TOPIC in the PAI configuration as the import parameter PAI base topic.

Paradox PAI MQTT Bridge

Listener
IF RECEIVEDMSG.TOPIC = baseTopic + "/interface/availability"
    paiStatus := TOSTRING(RECEIVEDMSG.PAYLOAD);
END
# PAI publishes availability as a retained message: offline = PAI lost the panel or stopped
IF paiStatus = "offline"
    ADDERROR(1, "PAI reports offline - no updates from the alarm panel. Check PAI and its connection to the panel.");
END
Partition Multi-value Switch

State of one partition of the panel and arming or disarming it; Arming and Alarm are only shown and send nothing

enum string
Values / States: Disarmed · Armed · Armed Stay · Armed Sleep · Arming · Alarm
Variable: partitionKey — Key of the partition as PAI publishes it in paradox/states/partitions/<key>, for example Area_1

Partition

Read switch state
RETURN(Mu);
Write switch state
# Only the four commands are sent. Arming and Alarm are states reported by the panel, never commands.
VAR cmd := SWITCH(Mu, 0, "disarm", 1, "arm", 2, "arm_stay", 3, "arm_sleep", "");
IF cmd != ""
    MQTTPUBLISH(baseTopic + "/control/partitions/" + partitionKey, cmd);
END
Listener
IF RECEIVEDMSG.TOPIC = baseTopic + "/states/partitions/" + partitionKey + "/current_state"
    VAR state := TOSTRING(RECEIVEDMSG.PAYLOAD);
    # PAI current_state: disarmed, arming (exit delay), armed_away, armed_home (stay), armed_night (sleep), triggered
    IF state = "disarmed"
        Mu := 0;
    ELSEIF state = "armed_away"
        Mu := 1;
    ELSEIF state = "armed_home"
        Mu := 2;
    ELSEIF state = "armed_night"
        Mu := 3;
    ELSEIF state = "arming"
        Mu := 4;
    ELSEIF state = "triggered"
        Mu := 5;
    END
END
Zone 1 Reed Contact Read-only

Open or closed state of one zone of the panel, with warnings for alarm, tamper, low battery of a wireless zone and bypass

boolean
Variable: zoneKey — Key of the zone as PAI publishes it in paradox/states/zones/<key>, for example Front_door

Zone 1

Read
RETURN(Rc);
Listener
VAR prefix := baseTopic + "/states/zones/" + zoneKey + "/";
VAR topic := RECEIVEDMSG.TOPIC;
IF INDEXOF(topic, prefix) = 0
    VAR payload := TOSTRING(RECEIVEDMSG.PAYLOAD);
    VAR isOn := IF(payload = "True" OR payload = "1", 1, 0);
    IF topic = prefix + "open"
        Rc := isOn;
    ELSEIF topic = prefix + "tamper"
        zoneTamper := isOn;
    ELSEIF topic = prefix + "rf_low_battery_trouble"
        zoneLowBattery := isOn;
    ELSEIF topic = prefix + "bypassed"
        zoneBypassed := isOn;
    ELSEIF topic = prefix + "alarm"
        zoneAlarm := isOn;
    END
END
# Warnings are rebuilt from the stored zone states on every message, so they stay until the panel clears them
IF zoneAlarm = 1
    ADDWARNING(1, "Zone in alarm");
END
IF zoneTamper = 1
    ADDWARNING(2, "Zone tampered");
END
IF zoneLowBattery = 1
    ADDWARNING(3, "Wireless zone: low battery");
END
IF zoneBypassed = 1
    ADDWARNING(4, "Zone bypassed");
END
Zone 2 Reed Contact Read-only

Open or closed state of one zone of the panel, with warnings for alarm, tamper, low battery of a wireless zone and bypass

boolean
Variable: zoneKey — Key of the zone as PAI publishes it in paradox/states/zones/<key>, for example Front_door

Zone 2

Read
RETURN(Rc);
Listener
VAR prefix := baseTopic + "/states/zones/" + zoneKey + "/";
VAR topic := RECEIVEDMSG.TOPIC;
IF INDEXOF(topic, prefix) = 0
    VAR payload := TOSTRING(RECEIVEDMSG.PAYLOAD);
    VAR isOn := IF(payload = "True" OR payload = "1", 1, 0);
    IF topic = prefix + "open"
        Rc := isOn;
    ELSEIF topic = prefix + "tamper"
        zoneTamper := isOn;
    ELSEIF topic = prefix + "rf_low_battery_trouble"
        zoneLowBattery := isOn;
    ELSEIF topic = prefix + "bypassed"
        zoneBypassed := isOn;
    ELSEIF topic = prefix + "alarm"
        zoneAlarm := isOn;
    END
END
# Warnings are rebuilt from the stored zone states on every message, so they stay until the panel clears them
IF zoneAlarm = 1
    ADDWARNING(1, "Zone in alarm");
END
IF zoneTamper = 1
    ADDWARNING(2, "Zone tampered");
END
IF zoneLowBattery = 1
    ADDWARNING(3, "Wireless zone: low battery");
END
IF zoneBypassed = 1
    ADDWARNING(4, "Zone bypassed");
END
Zone 3 Reed Contact Read-only

Open or closed state of one zone of the panel, with warnings for alarm, tamper, low battery of a wireless zone and bypass

boolean
Variable: zoneKey — Key of the zone as PAI publishes it in paradox/states/zones/<key>, for example Front_door

Zone 3

Read
RETURN(Rc);
Listener
VAR prefix := baseTopic + "/states/zones/" + zoneKey + "/";
VAR topic := RECEIVEDMSG.TOPIC;
IF INDEXOF(topic, prefix) = 0
    VAR payload := TOSTRING(RECEIVEDMSG.PAYLOAD);
    VAR isOn := IF(payload = "True" OR payload = "1", 1, 0);
    IF topic = prefix + "open"
        Rc := isOn;
    ELSEIF topic = prefix + "tamper"
        zoneTamper := isOn;
    ELSEIF topic = prefix + "rf_low_battery_trouble"
        zoneLowBattery := isOn;
    ELSEIF topic = prefix + "bypassed"
        zoneBypassed := isOn;
    ELSEIF topic = prefix + "alarm"
        zoneAlarm := isOn;
    END
END
# Warnings are rebuilt from the stored zone states on every message, so they stay until the panel clears them
IF zoneAlarm = 1
    ADDWARNING(1, "Zone in alarm");
END
IF zoneTamper = 1
    ADDWARNING(2, "Zone tampered");
END
IF zoneLowBattery = 1
    ADDWARNING(3, "Wireless zone: low battery");
END
IF zoneBypassed = 1
    ADDWARNING(4, "Zone bypassed");
END
Zone 4 Reed Contact Read-only

Open or closed state of one zone of the panel, with warnings for alarm, tamper, low battery of a wireless zone and bypass

boolean
Variable: zoneKey — Key of the zone as PAI publishes it in paradox/states/zones/<key>, for example Front_door

Zone 4

Read
RETURN(Rc);
Listener
VAR prefix := baseTopic + "/states/zones/" + zoneKey + "/";
VAR topic := RECEIVEDMSG.TOPIC;
IF INDEXOF(topic, prefix) = 0
    VAR payload := TOSTRING(RECEIVEDMSG.PAYLOAD);
    VAR isOn := IF(payload = "True" OR payload = "1", 1, 0);
    IF topic = prefix + "open"
        Rc := isOn;
    ELSEIF topic = prefix + "tamper"
        zoneTamper := isOn;
    ELSEIF topic = prefix + "rf_low_battery_trouble"
        zoneLowBattery := isOn;
    ELSEIF topic = prefix + "bypassed"
        zoneBypassed := isOn;
    ELSEIF topic = prefix + "alarm"
        zoneAlarm := isOn;
    END
END
# Warnings are rebuilt from the stored zone states on every message, so they stay until the panel clears them
IF zoneAlarm = 1
    ADDWARNING(1, "Zone in alarm");
END
IF zoneTamper = 1
    ADDWARNING(2, "Zone tampered");
END
IF zoneLowBattery = 1
    ADDWARNING(3, "Wireless zone: low battery");
END
IF zoneBypassed = 1
    ADDWARNING(4, "Zone bypassed");
END
PGM Output Switch

Switches one programmable output (PGM) of the panel on or off and shows its state

boolean
Values / States: ON · OFF
Variable: outputKey — Key of the PGM output as PAI publishes it in paradox/states/outputs/<key>

PGM Output

Read switch state
RETURN(St);
Write switch state
MQTTPUBLISH(baseTopic + "/control/outputs/" + outputKey, IF(St = 1, "on", "off"));
Listener
IF RECEIVEDMSG.TOPIC = baseTopic + "/states/outputs/" + outputKey + "/on"
    VAR payload := TOSTRING(RECEIVEDMSG.PAYLOAD);
    St := IF(payload = "True" OR payload = "1", 1, 0);
END
Connection: Packet Parser → MQTT
Possible improvements (6)
  • Zone bypass control — The template shows the bypass state of a zone as a warning but does not bypass a zone.
  • Force arming and EVO instant arming — arm_instant exists on EVO panels only; EVO has no Sleep arming, so Armed Sleep does nothing there.
  • Panic alarms — Left out on purpose: a panic command triggers a real alarm report.
  • System trouble flags — Trouble flags of the panel that PAI publishes under states/system/troubles.
  • Event stream — Live panel events PAI publishes as JSON when MQTT_PUBLISH_RAW_EVENTS is on.
  • More partitions, zones and PGM outputs — The template has one partition, four zones and one PGM. Import it again with other keys for more.

Sources

  • PAI — Paradox Alarm Interface, project README (version 3.8.1)
    github.com 2026-11-04
  • PAI wiki — MQTT Interface (topics, states, control commands)
    github.com 2026-11-04
  • PAI — example configuration pai.conf.example
  • PAI wiki — Compatibility (panels, firmware, connections)
    github.com 2026-11-04
  • PAI wiki — Connection methods (serial, IP150, ESP32 bridge)
    github.com 2026-11-04

Found a problem with this device template?

Tell us what's wrong, what's missing, or how the template should behave. We rely on your feedback to keep the catalog accurate.

Community — unreviewed

Want to use this in your TapHome Core?

Open this template in the Customer Portal to apply it to one of your homes, or to draft a refinement and submit it back to the catalog.

Open in portal