I use the excellent signal-cli-rest-api by @bbernhard on
my server, and my home-assistant is configured to send me and the Home group
notifications of all kinds. Sending messages quickly is crucial for many of my
automations, so I'm running the API in json-rpc
mode.
Recently, I wanted to add a way for Home Assistant to receive messages from us
to trigger automations (or stop others). However, when the signal-api is
running in json-rpc
mode, the /v1/receive
endpoint becomes websocket-only.
This is not supported by Home Assistant's signal_messenger
integration, which relies on REST API calls.
This project, signal-api-receiver
, provides a solution by creating a
lightweight wrapper that:
- Consumes the websocket stream from the
/v1/receive
endpoint. - Stores received messages in memory.
- Exposes a REST API for retrieving those messages.
This approach allows Home Assistant to easily receive Signal messages and
trigger automations without requiring modifications to the existing
signal-cli-rest-api
or the Home Assistant integration.
While developing signal-api-receiver
solved my immediate need, there were
other potential approaches to this problem:
- Improve the Home Assistant integration with Signal to function properly with a Websocket.
- Propose a new endpoint to the
signal-cli-rest-api
that responds to REST.
These alternatives might be more comprehensive solutions in the long term, but creating the wrapper provided a more immediate and focused solution for my specific use case.
signal-api-receiver
exposes the following API endpoints:
GET /receive/pop
:- Returns one message at a time from the queue.
- If no messages are available, it returns a
204 No Content
status.
GET /receive/flush
:- Returns all available messages as a list.
- If no messages are available, it returns an empty list (
[]
).
signal-api-receiver
is available as a Docker image on Docker Hub. This is the recommended way to run the application.
docker pull kalbasit/signal-api-receiver:latest
Here's an example docker run command:
docker run -p 8105:8105 \
-e SIGNAL_ACCOUNT="your_signal_account" \
-e SIGNAL_API_URL="wss://your-signal-api-url" \
kalbasit/signal-api-receiver:latest
Explanation:
-p 8105:8105
: Maps port 8105 on the host to port 8105 in the container.-e SIGNAL_ACCOUNT="your_signal_account"
: Sets theSIGNAL_ACCOUNT
environment variable. Replace with your actual Signal account.-e SIGNAL_API_URL="wss://your-signal-api-url"
: Sets theSIGNAL_API_URL
environment variable. Replace with the URL of your Signal API.
Refer to the Docker Hub page for more information.
To run signal-api-receiver
from source, you need to provide the following command-line flags:
Global Options:
--log-level <value>
: Sets the logging level (default: "info"). Can be set using the$LOG_LEVEL
environment variable.
Options for the serve
command:
--record-message-type <value>
: Specifies which message types to record. Valid types are: "receipt", "typing", "data", "data-message", and "sync". This flag can be repeated to record multiple types (default: "data-message").--repeat-last-message
: If enabled, repeats the last message if no new messages are available (applies to/receive/pop
). This can be set using the$REPEAT_LAST_MESSAGE
environment variable (default: false).--signal-account <value>
: Required. Specifies your Signal account number. Can be set using the$SIGNAL_ACCOUNT
environment variable.--signal-api-url <value>
: Required. Specifies the URL of your Signal API, including the scheme (e.g.,wss://signal-api.example.com
). Can be set using the$SIGNAL_API_URL
environment variable.--server-addr <value>
: Sets the address where the server will listen (default: ":8105"). Can be set using the$SERVER_ADDR
environment variable.
By default, the server starts on :8105
. You can change this using the --server-addr
flag (e.g., --server-addr :8080
).
You can see all available options by running:
signal-api-receiver serve --help
Here's an example of how to deploy signal-api-receiver
on Kubernetes alongside existing signal-cli-rest-api
deployment that is not shown here:
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: signal-api-receiver
labels:
app: signal-receiver
tier: api
spec:
replicas: 1
selector:
matchLabels:
app: signal-receiver
tier: api
template:
metadata:
labels:
app: signal-receiver
tier: api
spec:
containers:
- image: kalbasit/signal-receiver:latest
name: signal-receiver
args:
- /bin/signal-api-receiver
- serve
- --signal-api-url=ws://signal-api.ns.svc:8080
- --signal-account=+19876543210
ports:
- containerPort: 8105
name: receiver-web
livenessProbe:
httpGet:
path: /healthz
port: receiver-web
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /healthz
port: receiver-web
initialDelaySeconds: 5
periodSeconds: 10
Service
apiVersion: v1
kind: Service
metadata:
name: signal-api-receiver
labels:
app: signal-receiver
tier: api
spec:
type: ClusterIP
ports:
- name: receiver-web
port: 8105
selector:
app: signal-receiver
tier: api
Traefik IngressRoute
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: signal-api
spec:
entryPoints:
- web
- websecure
routes:
# This rule is for existing signal-cli-rest-api service that is not shown here.
- kind: Rule
match: Host(`signal-api.example.com`)
priority: 10
services:
- name: signal-api
port: http-web
# The new rule for signal-api-receiver.
- kind: Rule
match: Host(`signal-api.example.com`) && Path(`/receive`)
priority: 20
services:
- name: signal-api-receiver
port: receiver-web
tls:
secretName: signal-api-tls
This project is licensed under the MIT License - see the LICENSE file for details.