{"id":696,"date":"2026-05-12T11:08:27","date_gmt":"2026-05-12T19:08:27","guid":{"rendered":"https:\/\/nramkumar.org\/tech\/?p=696"},"modified":"2026-05-12T11:10:38","modified_gmt":"2026-05-12T19:10:38","slug":"home-assistant-sms-alerts-via-asterisk-and-voip-ms","status":"publish","type":"post","link":"https:\/\/nramkumar.org\/tech\/blog\/2026\/05\/12\/home-assistant-sms-alerts-via-asterisk-and-voip-ms\/","title":{"rendered":"Home Assistant SMS Alerts via Asterisk and voip.ms"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In the previous post we setup inbound and outbound SMS routing through a LAN Asterisk server using a voip.ms SIP trunk. In this post we will make use of this functionality to allow a Home Assistant instance in the LAN to send notifications via SMS. The nice thing about this setup is that critical notifications can be sent to the device(s) even if it is outside the LAN and even outside reach of data connectivity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Network Architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The architecture involves Home Assistant running a connector which connects to the Asterisk server over the Asterisk Management Interface (AMI). The dialplan on Asterisk picks up the messages and sends them over the voip.ms trunk just like any other outbound message.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-1024x559.png\" alt=\"\" class=\"wp-image-697\" srcset=\"https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-1024x559.png 1024w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-300x164.png 300w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-768x419.png 768w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-1536x838.png 1536w, https:\/\/nramkumar.org\/tech\/wp-content\/uploads\/2026\/05\/HA-Asterisk-Architecture-2048x1117.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Asterisk Setup<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Enable AMI<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Edit <code>\/etc\/asterisk\/manager.conf<\/code> to add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;general]\nenabled = yes\nport = 5038\nbindaddr = 0.0.0.0\n\n&#91;homeassistant]\nsecret = your_ami_password\nread = originate\nwrite = originate\ndeny = 0.0.0.0\/0.0.0.0\npermit = &lt;HA_Static_IP|HA_Subnet&gt;\/255.255.255.255 ; replace with 255.255.255.0 for subnet (or as appropriate for your subnet)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Since AMI is a powerful surface, keeping it restricted to known hosts is a good idea. We configure a user (<code>homeassistant<\/code>) with a secure password and restrict it to <code>read = originate<\/code> and <code>write = originate<\/code> which gives permissions to this user for channel originates. Note however that this a weak restriction as AMI is powerful and the user can circumvent this to do other actions quite likely if they wanted to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reload by running <code>asterisk -rx \"manager reload\"<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dialplan for Home Assistant<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We add a dedicated context for HA messages &#8211; we take the alert text and destination number as channel variables from the AMI originate action and trigger a message send over the voip.ms trunk. I created <code>\/etc\/asterisk\/homeassistant-extensions.conf<\/code> with the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;ha-sms]\nexten => _X.,1,NoOp(HA alert SMS to ${EXTEN})\n ; Message body is passed as a channel variable from AMI\n same => n,Set(MESSAGE(body)=${HA_SMS_BODY})\n same => n,Set(MESSAGE(from)=sip:&lt;DID_Number>@asterisk.lan)\n same => n,MessageSend(pjsip:voipms\/sip:${EXTEN}@&lt;POP Server>,sip:&lt;DID_Number>@&lt;POP Server>)\n same => n,Log(NOTICE, HA SMS to ${EXTEN} status: ${MESSAGE_SEND_STATUS})\n same => n,Hangup()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then included it in <code>\/etc\/asterisk\/extensions.conf<\/code> with <code>#include homeassistant-extensions.conf<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reload the dialplan with <code>asterisk -rx \"dialplan reload\"<\/code>. With this dialplan setup, Home Assistant is expected to send an <code>Originate<\/code> action with the local channel (<code>Local\/NUMBER@ha-sms<\/code>) to the <code>ha-sms<\/code> context. The message body is expected in the <code>HA_SMS_BODY<\/code> variable.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Home Assistant Setup<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Custom Notify Component<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">HA has some Asterisk Integrations. But in the age of AI, it is easier to just build a small, custom component for what we want. Essentially we just need to open the TCP socket, authenticate and send some text across to the Asterisk server. Create the following under your HA config directory:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">custom_components\/asterisk_sms\/<br>    __init__.py<br>    manifest.json<br>    notify.py<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>__init.py__<\/strong> \u2014 empty file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>manifest.json<\/code><\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{<br>  \"domain\": \"asterisk_sms\",<br>  \"name\": \"Asterisk SMS\",<br>  \"version\": \"1.0.0\",<br>  \"documentation\": \"\",<br>  \"dependencies\": [],<br>  \"codeowners\": [],<br>  \"requirements\": []<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>notify.py<\/code><\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import socket\nimport logging\nfrom homeassistant.components.notify import BaseNotificationService, ATTR_TARGET\n\n_LOGGER = logging.getLogger(__name__)\n\nCONF_HOST     = \"host\"\nCONF_PORT     = \"port\"\nCONF_USERNAME = \"username\"\nCONF_PASSWORD = \"password\"\n\n\ndef get_service(hass, config, discovery_info=None):\n    return AsteriskSMSNotificationService(\n        host     = config.get(CONF_HOST, \"asterisk.lan\"),\n        port     = config.get(CONF_PORT, 5038),\n        username = config&#91;CONF_USERNAME],\n        password = config&#91;CONF_PASSWORD],\n    )\n\n\nclass AsteriskSMSNotificationService(BaseNotificationService):\n\n    def __init__(self, host, port, username, password):\n        self.host     = host\n        self.port     = port\n        self.username = username\n        self.password = password\n\n    def send_message(self, message=\"\", **kwargs):\n        targets = kwargs.get(ATTR_TARGET) or &#91;]\n        if not targets:\n            _LOGGER.error(\"asterisk_sms: no target number specified\")\n            return\n        if isinstance(targets, str):\n            targets = &#91;targets]\n        for number in targets:\n            self._send(number, message)\n\n    def _send(self, number, message):\n        try:\n            with socket.create_connection((self.host, self.port), timeout=10) as s:\n                s.recv(1024)  # AMI banner\n\n                self._ami(s, (\n                    f\"Action: Login\\r\\n\"\n                    f\"Username: {self.username}\\r\\n\"\n                    f\"Secret: {self.password}\\r\\n\"\n                ))\n                self._ami(s, (\n                    f\"Action: Originate\\r\\n\"\n                    f\"Channel: Local\/{number}@ha-sms\\r\\n\"\n                    f\"Application: Wait\\r\\n\"\n                    f\"Data: 2\\r\\n\"\n                    f\"Variable: HA_SMS_BODY={message}\\r\\n\"\n                    f\"Async: true\\r\\n\"\n                ))\n                self._ami(s, \"Action: Logoff\\r\\n\")\n\n            _LOGGER.info(\"asterisk_sms: sent to %s\", number)\n\n        except Exception as exc:\n            _LOGGER.error(\"asterisk_sms: AMI error sending to %s: %s\", number, exc)\n\n    @staticmethod\n    def _ami(sock, action):\n        sock.sendall((action + \"\\r\\n\").encode())\n        sock.recv(4096)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>configuration.yaml<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>notify:\n  - platform: asterisk_sms\n    name: asterisk_sms\n    host: asterisk.lan\n    port: 5038\n    username: homeassistant\n    password: your_ami_password<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Restart Home Assistant after adding the component and configuration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Testing<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Verify AMI is listening<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">From any LAN host:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>telnet asterisk.lan 5038<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the AMI banner: <code>Asterisk Call Manager\/X.X<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Test from HA Developer Tools<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Home Assistant, Go To Settings &gt; Developer Tools &gt; Actions<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Select <code>notify.asterisk_sms<\/code> and fill in:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Message<\/strong>: <code>test alert<\/code><\/li>\n\n\n\n<li><strong>Target<\/strong>: <code>1XXXXXXXXXX<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Hit <strong>Perform Action<\/strong>. Watch Asterisk logs for confirmation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>asterisk -rvvv<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NOTICE: HA SMS to 1XXXXXXXXXX status: SUCCESS<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using in Automations<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>action: notify.asterisk_sms\ndata:\n  message: \"Front door motion detected\"\n  target: \"1XXXXXXXXXX\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>target<\/code> field accepts a single number or a list for multi-recipient alerts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post we setup inbound and outbound SMS routing through a LAN Asterisk server using a voip.ms SIP trunk. In this post we will make use of this functionality to allow a Home Assistant instance in the LAN to send notifications via SMS. The nice thing about this setup is that critical notifications&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-696","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/696","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/comments?post=696"}],"version-history":[{"count":2,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/696\/revisions"}],"predecessor-version":[{"id":699,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/posts\/696\/revisions\/699"}],"wp:attachment":[{"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/media?parent=696"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/categories?post=696"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nramkumar.org\/tech\/wp-json\/wp\/v2\/tags?post=696"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}