An adapter between MQTT and signal-cli JSON RPC.
This project allows to send and receive messages between MQTT clients and Signal messengers. This includes, but is not limited to, sending and receiving of text messages, quotations and emojis to/from Signal accounts and groups. The signal-cli documentation contains a list of all supported commands.
-
Register or link your Signal account.
-
Create a run configuration, e.g.:
compose.ymlservices: signal-mqtt: image: ckware/signal-mqtt container_name: signal-mqtt restart: unless-stopped init: true user: "nobody:nogroup" environment: MQTT_PUBLISH_OPTIONS: "-h broker -i signal-receiver" MQTT_SUBSCRIBE_OPTIONS: "-h broker -i signal-sender" volumes: - "./data:/home/.local/share/signal-cli"
-
Start a container:
$ docker compose up -d
-
Send and receive messages via MQTT:
$ mosquitto_sub -v -h broker -t 'signal/#' & signal/in/method/receive/source_number/%2B491713920000/timestamp/1577882096000 Incoming message $ mosquitto_pub -v -h broker -t signal/out/method/send/recipient/%2B491713920000 -m 'Outgoing message' signal/out/method/send/recipient/%2B491713920000 Outgoing message
-
A phone number that is registered as a Signal account (see Registration with captcha)
-
Docker Compose
The Docker Compose documentation contains a comprehensive guide explaining several install options. On recent debian-based systems, Docker Compose may be installed by calling
$ sudo apt install docker-ce docker-compose-plugin
There are two different ways to use this service:
-
Send and receive complete JSON RPC objects including content and metadata.
-
Send and receive simple text messages and handle metadata as part of the MQTT topic (aka "parameter topics").
Action | Command |
---|---|
Start the container |
|
Stop the container |
|
View the logs |
|
Incoming and outgoing JSON RPC objects are published to a topic per direction.
Direction | Default topic | Environment variable |
---|---|---|
Send |
|
|
Receive |
|
|
To send a command, publish a JSON RPC object to the send topic (default: signal/out
).
Example:
$ mosquitto_pub -h broker -t signal/out -m '{"jsonrpc":"2.0","method":"send","params":{"recipient":["+491713920000"],"message":"Outgoing message"}}'
The text Outgoing message is sent to the phone.
To receive JSON RPC objects, subscribe to the receive topic (default: signal/in
).
Reception of JSON RPC objects is disabled by default.
To enable it, set MQTT_PUBLISH_JSONRPC
to true
.
You may additionally want to disable reception of messages on parameter topics
by setting MQTT_PUBLISH_TO_PARAMETER_TOPIC
to false
.
Example:
$ mosquitto_sub -v -h broker -t 'signal/#' &
# The user starts to type 'Incoming message' on the phone
signal/in {"jsonrpc":"2.0","method":"receive","params":{"envelope":{"source":"+491713920000","sourceNumber":"+491713920000","sourceUuid":"3689ed97-01b2-4fa5-8ed8-18174ad5cf15","sourceName":"Sally Sender","sourceDevice":1,"timestamp":1577882080000,"typingMessage":{"action":"STARTED","timestamp":1577882080000}},"account":"+493023125000","subscription":0}}
# The text 'Incoming message' is completed
signal/in {"jsonrpc":"2.0","method":"receive","params":{"envelope":{"source":"+491713920000","sourceNumber":"+491713920000","sourceUuid":"3689ed97-01b2-4fa5-8ed8-18174ad5cf15","sourceName":"Sally Sender","sourceDevice":1,"timestamp":1577882090000,"typingMessage":{"action":"STOPPED","timestamp":1577882090000}},"account":"+493023125000","subscription":0}}
# The message is sent to +493023125000
signal/in {"jsonrpc":"2.0","method":"receive","params":{"envelope":{"source":"+491713920000","sourceNumber":"+491713920000","sourceUuid":"3689ed97-01b2-4fa5-8ed8-18174ad5cf15","sourceName":"Sally Sender","sourceDevice":1,"timestamp":1577882096000,"dataMessage":{"timestamp":1577882096000,"message":"Incoming message","expiresInSeconds":0,"viewOnce":false}},"account":"+493023125000","subscription":0}}
$ mosquitto_pub -h broker -t signal/out/method/send/recipient/%2B491713920000 -m 'Outgoing message'
signal/out/method/send/recipient/%2B491713920000 Outgoing message
# The message was delivered to mobile phone +491713920000
signal/in {"jsonrpc":"2.0","method":"receive","params":{"envelope":{"source":"+491713920000","sourceNumber":"+491713920000","sourceUuid":"3689ed97-01b2-4fa5-8ed8-18174ad5cf15","sourceName":"Sally Sender","sourceDevice":1,"timestamp":1577882097000,"receiptMessage":{"when":1577882097000,"isDelivery":true,"isRead":false,"isViewed":false,"timestamps":[1577882098000]}},"account":"+493023125000","subscription":0}}
# The message was read on mobile phone +491713920000
signal/in {"jsonrpc":"2.0","method":"receive","params":{"envelope":{"source":"+491713920000","sourceNumber":"+491713920000","sourceUuid":"3689ed97-01b2-4fa5-8ed8-18174ad5cf15","sourceName":"Sally Sender","sourceDevice":1,"timestamp":1577882099000,"receiptMessage":{"when":1577882099000,"isDelivery":false,"isRead":true,"isViewed":false,"timestamps":[1577882098000]}},"account":"+493023125000","subscription":0}}
Parameter topics have been designed for use cases where handling of complete JSON RPC objects is not suitable. They allow to send and receive commands in form of simple text messages, whereas all required metadata is managed in the MQTT topic.
A parameter topic has the following structure:
<PREFIX> /method/
<METHOD> [/
<PARAMETER_NAME> /
<PARAMETER_VALUE> ]…
It is composed of:
-
A prefix per direction, defaulting to
signal/out
for outgoing andsignal/in
for incoming MQTT messages -
The JSON RPC method, e.g.
send
orreceive
-
An optional list of parameters. Each parameter is composed of a name and a value. Parameters may be in any order.
Some parameter values, e.g. an international account number or a group id, include special characters which are forbidden as part of an MQTT topic. Thus, all values in parameter topics are percent-encoded (aka URL-encoded).
Example: topic signal/out/method/send/recipient/%2B491713920000
contains parameter recipient with value +491713920000
.
Characters with a special meaning in the context of MQTT, base64 and percent-encoding include:
Character | Percent Encoding |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
To send a JSON RPC command, publish the message text to a topic that is composed of
-
the send topic (default:
signal/out
) and -
the method (e.g.
method/send
) and -
all parameters of the command.
The signal-cli documentation contains a list of commands and parameters. Syntax rules:
-
Parameter names are provided in camelCase format instead of the hyphen-format.
-
Multi-valued parameters can be provided as comma-separated list.
See the signal-cli wiki for detailed explanations of commands and parameters.
The following values are used in the examples:
-
Account number of signal-mqtt:
+493023125000
-
Phone number:
+491713920000
-
Hostname of the MQTT broker:
broker
-
Group Name: Admins
-
Group ID:
LS0+YWRtaW5zPz8/Cg==
$ mosquitto_pub -h broker -t signal/out/method/send/recipient/%2B491713920000 -m 'Outgoing message'
The text Outgoing message is sent to the phone.
$ mosquitto_pub -h broker -t signal/out/method/send/recipient/%2B491713920000/quoteAuthor/%2B491713920001/quoteTimestamp/1577882096000 -m 'Outgoing message'
The text Outgoing message is sent to the phone, quoting a message sent from +491713920001
at timestamp 1577882096000
.
By default, the type of a parameter value is derived from its content.
When the default type does not match the expected type,
the value must be suffixed by a colon (:
) and a type-id:
<PARAMETER_NAME> /
<PERCENT_ENCODED_VALUE> :
<TYPE_ID>
Some parameters allow multiple values; these may be represented by a comma-separated list of multiple values:
<PARAMETER_NAME> /
<PERCENT_ENCODED_VALUE1> ,
<PERCENT_ENCODED_VALUE2> :
<TYPE_ID>
Type Rules:
-
The supported type-ids are
string
,number
,boolean
. Each type-id may be suffixed with[]
which means that the value is a list. -
When a value contains digits only, its default type is
number
, otherwisestring
. -
The type-id may be abbreviated. For example,
string
,str
ands
are all valid type-ids for typestring
, -
In a comma-separated value list, the type-id suffix
[]
may be omitted.
Type | type-id | JSON syntax | Topic syntax |
---|---|---|---|
String |
|
|
|
Number |
|
|
|
Boolean |
|
|
|
String[] |
|
|
|
|
|
||
Number[] |
|
|
|
|
|
||
Boolean[] |
|
|
|
|
|
Example: topic
signal/out/method/send/recipient/%2B491713920000,%2B491713920001/quoteTimestamp/1577882096000:n
contains a parameter recipient holding a string
array
of the two phone numbers +491713920000
& +491713920001
,
and parameter quoteTimestamp is explicitely typed as number
(although the default would work here, too).
To receive messages on parameter topics,
subscribe to all children of the receive topic (default: signal/in
).
The following values are used in the examples:
-
Account number of signal-mqtt:
+493023125000
-
Phone number:
+491713920000
-
Hostname of the MQTT broker:
broker
-
Group Name: Admins
-
Group ID:
LS0+YWRtaW5zPz8/Cg==
The text Incoming message is sent from the phone to +493023125000
.
$ mosquitto_sub -v -h broker -t signal/#
signal/in/method/receive/source_number/%2B491713920000/timestamp/1577882096000 Incoming message
The text Incoming message is sent from the phone to the group Admins.
$ mosquitto_sub -v -h broker -t signal/#
signal/in/method/receive/source_number/%2B491713920000/timestamp/1577882096000/group_id/LS0%2BYWRtaW5zPz8%2FCg%3D%3D Incoming message
The text Incoming quote is sent from the phone as quotation to the message Outgoing message from above.
$ mosquitto_sub -v -h broker -t signal/#
signal/in/method/receive/source_number/%2B491713920000/timestamp/1577882100000/quote_id/1577882096000 Incoming quote
The emoji đź‘Ť is sent from the phone as reaction to the message Outgoing message from above.
$ mosquitto_sub -v -h broker -t signal/#
signal/in/method/receive/source_number/%2B491713920000/timestamp/1577882100000/reaction_emoji/%F0%9F%91%8D%F0%9F%8F%BB/reaction_timestamp/1577882096000 (null)
A JSON RPC object usually contains a lot of metadata parameters along with the main content. Most metadata parameters are not required for an average user, thus the parameters are filtered before building an MQTT topic.
The parameters that are used for topic building
are read from /etc/signal-mqtt/topic-parameters
(within the container) once at application start.
The file contains a key/value pair per parameter.
The key is used as parameter name in the topic. The value is a path expression in
dot notation,
representing the position of the value in JSON RPC objects. Example:
# Restrict the topic to method, source number and group id.
method = method
source_number = params.envelope.sourceNumber
group_id = params.envelope.dataMessage.groupInfo.groupId
The default file can be found under image/topic-parameters
.
There are several options to customize the filter, overriding the default file being the simplest one.
Write a custom file and mount it to /etc/signal-mqtt/topic-parameters
in the container.
Example:
services:
signal-mqtt:
[...]
volumes:
- "./data:/home/.local/share/signal-cli"
- "./custom-topic-parameters:/etc/signal-mqtt/topic-parameters"
Write a custom file, mount it into the container and
set the environment variable MQTT_TOPIC_PARAMETERS_FILE
.
In a containerized environment,
this option offers no advantages over replacing the default file.
services:
signal-mqtt:
[...]
environment:
MQTT_TOPIC_PARAMETERS_FILE: "/home/custom-topic-parameters"
volumes:
- "./data:/home/.local/share/signal-cli"
- "./custom-topic-parameters:/home/custom-topic-parameters"
The filter may be configured without file
by setting the environment variable MQTT_TOPIC_PARAMETERS_PATTERN
.
The variable must contain a regular expression of named capturing groups.
The group name corresponds to the filter key, the content of the group to the filter value.
Example:
services:
signal-mqtt:
[...]
environment:
MQTT_TOPIC_PARAMETERS_PATTERN: "(?<method>method)|(?<source_number>params.envelope.sourceNumber)|(?<group_id>params.envelope.dataMessage.groupInfo.groupId)"
Syntax:
$ docker compose run signal-mqtt signal-cli <command>
Most signal-cli commands require that no container is running.
Example:
$ docker compose run --rm -ti signal-mqtt signal-cli listContacts
Number: +491713920000 Name: Profile name: Sally Sender Blocked: false Message expiration: disabled
Number: +491713920001 Name: Profile name: Rudy Receiver Blocked: false Message expiration: disabled
The configuration is based on environment variables.
Variable | Description | Allowed values | Default | Example |
---|---|---|---|---|
|
MQTT publish options |
All options supported by |
none |
|
|
MQTT topic for publishing messages received from Signal |
|
|
|
|
Publish JSON RPC objects received from signal-cli? |
|
|
|
|
Publish messages received from Signal to a parameter topic? |
|
|
|
|
MQTT subscribe options |
All options supported by |
none |
|
|
MQTT topic to listen for messages that are sent to a Signal receiver |
|
|
|
|
Prefix for MQTT topics |
|
|
|
|
Custom filter file for topic parameters |
File paths |
none |
|
|
Custom pattern for topic parameters |
A regular expression containing named capturing groups |
none |
|
|
Enable logging via MQTT? |
|
|
|
|
MQTT topic to publish the log to |
|
|
|
|
Enable logging of JSON RPC objects? |
|
|
|
|
Phone number of the signal account |
International phone number format with leading |
Account from signal-cli configuration |
|
|
signal-cli options |
All options supported by |
none |
|
The section contains example configurations for integration into FHEM.
It is possible to create either 1 FHEM device per contact or a single FHEM device for all contacts, or both.
-
Create a device:
fhem.cfgdefine mqtt_signal_user_sally MQTT2_DEVICE attr mqtt_signal_user_sally devicetopic %2B491713920000 attr mqtt_signal_user_sally readingList signal/in/method/receive/source_number/$DEVICETOPIC/timestamp/[^/]+ message attr mqtt_signal_user_sally setList message signal/out/method/send/recipient/$DEVICETOPIC
-
Send a message:
set mqtt_signal_user_sally message FHEM greets Sally
-
Create a device:
fhem.cfgdefine mqtt_signal_group_admins MQTT2_DEVICE attr mqtt_signal_group_admins devicetopic LS0%2BYWRtaW5zPz8%2FCg%3D%3D attr mqtt_signal_group_admins readingList signal/in/method/receive/source_number/[^/]+/timestamp/[^/]+/group_id/$DEVICETOPIC:.* message attr mqtt_signal_group_admins setList message signal/out/method/send/groupId/$DEVICETOPIC
-
Send a message:
set mqtt_signal_group_admins message FHEM greets Admins
This configuration creates a single device that receives all messages.
Readings sourceName
, message
, and groupId
are updated when a message arrives.
All remaining message parameters are available as readings prefixed with json_params_
.
-
Requirement: Enable publishing of JSON messages:
compose.ymlservices: signal-mqtt: environment: MQTT_PUBLISH_JSONRPC: "true"
-
Create the device:
fhem.cfgdefine mqtt_signal MQTT2_DEVICE attr mqtt_signal devicetopic signal/in attr mqtt_signal readingList $DEVICETOPIC:.* { json2nameValue($EVENT, 'json_', $JSONMAP) } attr mqtt_signal jsonMap \ json_params_envelope_sourceName:sourceName \ json_params_envelope_dataMessage_message:message \ json_params_envelope_dataMessage_groupInfo_groupId:groupId attr mqtt_signal stateFormat sourceName: message
-
It is optionally possible to introduce a reading
groupName
. This implementation requires a user attributegroups
with a space-separated list of key/value pairs containing a mapping from groupId to groupName.fhem.cfgattr mqtt_signal userattr groups attr mqtt_signal groups LS0+YWRtaW5zPz8/Cg== Admins attr mqtt_signal userReadings groupName:groupId.+ \ { my %groups = split(' ', AttrVal($NAME, 'groups', undef)); return $groups{ReadingsVal($NAME, 'groupId', undef)}; }
-
This project is an integration of
-
It was inspired by