Signal Messenger using received message

I’m trying to use the Signal Integration and trigger automations with received messages.

Home Assistant should receive messages and switch either a red lightbulb or a green lightbulb on, depending on the message text. It should also reply with “OK, green” or “OK, red”.

I started with the standard REST sensor from the docs:

- resource: "http://127.0.0.1:8080/v1/receive/<number>"
  headers:
    Content-Type: application/json
  sensor:
    - name: "Signal message received"
      value_template: "" #this will fetch the message
      json_attributes_path: $[0].envelope
      json_attributes:
        - source #using attributes you can get additional information, in this case, the phone number.

But this gives me chatty messages every few seconds in my log (JSON result was not a dictionary or list with 0th element a dictionary), which I’d rather avoid because it pollutes the log when looking for real errors.

Is there a standard way to avoid those log messages?

I’ve tried this to get rid of the warnings by configuring the sensor like this:

resource: "http://127.0.0.1:8080/v1/receive/<number>"
headers:
  Content-Type: application/json
sensor:
  - name: "Signal message sender"
    value_template: "{{ value_json[0]['envelope']['sourceNumber'] if value_json else '' }}"
  - name: "Signal message received"
    value_template: "{{ value_json[0]['envelope']['dataMessage']['message'] if value_json else '' }}"

This seems to get rid of the warning and gives me two entities for the message text and the message sender.

However, when testing I can see that when receiving a message sometimes only one of the sensors is set and the other remains at unknown.
Can anyone explain what’s going on there?

It may have to do with the message being re-set to unknown before the sender?

The automation for turning on the green light looks like this. It tries to make sure that HA only reacts when my numbers sends the command.

triggers:
  - trigger: template
    value_template: >-
      {{ is_state('sensor.signal_message_sender', '+44444444444') and
      (states('sensor.signal_message_received') | lower == 'green')
      }}
actions:
  - action: notify.signal
    data:
      message: OK, green

Is that a viable way of implementing this automation?

the signal instructions seem to be wrong according to this thread: Unable to receive messages using Signal Messenger integration - #4 by Lewis

same here: Signal Messanger - can't recieve messages Websockets support needed. · Issue #117555 · home-assistant/core · GitHub

this might be a way around: GitHub - kalbasit/signal-api-receiver

this solution worked for me:

  1. install signal - make sure sending works, I changed the port to 9123
  2. change signal configuration to “json-rpc” and start addon
  3. test in terminal / ssh with :
curl http://192.168.1.77:9123/v1/about

should return:

{"versions":["v1","v2"],"build":2,"mode":"json-rpc","version":"0.92","capabilities":{"v2/send":["quotes","mentions"]}}#   

  1. in the following this fixed homeassistant IP is assumed: 192.168.1.66
  2. install : GitHub - hassio-addons/addon-appdaemon: AppDaemon4 - Home Assistant Community Add-ons
  3. in appdaemon configuration: grey box “python packages” write “websockets” and press “save”
  4. goto appdaemon folder: addon_configs/a0d7b954_appdaemon/apps
  5. change file : apps.yaml to (keep “—”)
 ---
hello_world:
  module: hello
  class: HelloWorld
signal_receiver:
  module: signal_listener
  class: SignalReceiver
  signal_ws_uri: "ws://192.168.1.66:YOUR_SIGNALPORT/v1/receive/+49YOURPHONENUMBERHERE"   

in my case it looks like this: :wink:

---
hello_world:
  module: hello
  class: HelloWorld
signal_receiver:
  module: signal_listener
  class: SignalReceiver
  signal_ws_uri: "ws://192.168.1.66:9123/v1/receive/+49190666666"   
  1. in appdaemon folder: addon_configs/a0d7b954_appdaemon/apps create new file " signal_listener.py" with content:
#!/usr/bin/python3 
import appdaemon.plugins.hass.hassapi as hass
import websockets
import asyncio
import json

class SignalReceiver(hass.Hass):
    async def initialize(self):
        self.uri = self.args.get("signal_ws_uri", "ws://localhost:8080/ws")
        self.log(f"Starting Signal WebSocket listener to {self.uri}")
        asyncio.create_task(self.listen_to_signal())

    async def listen_to_signal(self):
        while True:
            try:
                async with websockets.connect(self.uri) as websocket:
                    self.log("Connected to Signal WebSocket")
                    while True:
                        message = await websocket.recv()
                        self.log(f"Received Signal message: {message}")
                        
                        try:
                            data = json.loads(message)
                        except json.JSONDecodeError:
                            self.log("Warning: Received non-JSON message", level="WARNING")
                            data = {"raw_message": message}

                        # Fire event with parsed data
                        self.fire_event("signal_message_received", **data)
            except (websockets.exceptions.ConnectionClosedError, ConnectionRefusedError) as e:
                self.log(f"WebSocket connection error: {e}", level="ERROR")
                self.log("Retrying connection in 5 seconds...")
                await asyncio.sleep(5)
            except Exception as e:
                self.log(f"Unexpected error: {e}", level="ERROR")
                self.log("Retrying connection in 5 seconds...")
                await asyncio.sleep(5)
  1. create new automation:
alias: receive Signal Messages
description: "solution via app deamon and addon_configs/a0d7b954_appdaemon/apps"
triggers:
  - event_type: signal_message_received
    trigger: event
actions:

  - action: notify.signal
    data:
      message: "Home assistant received Signal message from {{ trigger.event.data.envelope.source }}:{{ trigger.event.data.envelope.dataMessage.message }}"
  1. restart HA!
    send signal message from your phone and enjoy!
2 Likes

Thanks for taking the time to describe your setup (and sorry for taking a while to try this out)! This seems to process messages a lot quicker than the standard setup :smile:

It seems that the automation is triggered 3 times with this setup - is this normal?

This is really neat. Two questions: one about user experience and one about implementation.

  1. Is anyone using this idea to broadcast TTS person-to-person social messages either throughout the house or to the room of the recipient when a message is received? Is it too intrusive? Wondering if this could be integrated with a local LLM so that the voice assistant could summarize, relay, and send messages hands free.
  2. Is there a container solution that does not require a HA add-on that can run alongside a docker HA setup?

I think this might be related to typing indicators. Typing indicators also send an envelope via JSON, but there’s no message attached. In my case, it looks like things work correctly every time as long as I wait for the typing indicator to be consumed by HA before I send my message. E.g., type the message, wait 15 seconds, then send. I think the issue arises when a signal-cli receive command gets two different envelopes in a single receive operation, one of which contains the typing indicator and one of which has the message. I think, in that case, only the first is envelope is processed, maybe?

If that’s the case, a potential solution would be to disable typing indicators for the Signal account used by HA. However, it’s not immediately clear to me how to do this via the Signal CLI REST API. I know it’s possible via the Signal CLI app using the updateConfiguration command, but I’m not sure if that’s exposed via the REST API?

Edit: While I suspect that turning off the typing notification would resolve the issue, for now I have had luck with adjusting the listener so that it uses index -1 (i.e., the last envelope) instead of index 0 (the first envelope). Since the typing indicator should always be prior to the message, I think this should work? So, it looks like this now:

resource: "http://127.0.0.1:8080/v1/receive/<number>"
headers:
  Content-Type: application/json
sensor:
  - name: "Signal message sender"
    value_template: "{{ value_json[-1]['envelope']['sourceNumber'] if value_json else '' }}"
  - name: "Signal message received"
    value_template: "{{ value_json[-1]['envelope']['dataMessage']['message'] if value_json else '' }}"