Prototpye of a Dockerized Phone Assistant Connecting Fritz!Box with n8n Automation
Freya connects a FRITZ!Box IP phone to Asterisk (PJSIP), handles calls with a Python ARI app, and speaks/recognizes speech locally using Wyoming Piper (TTS) and remote openai (STT).
Designed to trigger/receive data from n8n via a simple webhook.
What this project does
- Registers an IP phone on your FRITZ!Box and accepts inbound calls in Asterisk.
- Runs an ARI app (FastAPI) that answers, bridges audio, and can TTS/STT.
- Uses Wyoming Piper for fast, local TTS (and optional local STT).
- Provides a tiny Dialer API (
POST /call) to place an outbound call and speak a message. - Plays nicely with n8n via Webhook β Webhook Respond.
Requirements
- A FRITZ!Box in your LAN (e.g.,
192.168.178.1), with an IP phone (username/password) created. - Linux host with Docker & Docker Compose.
OPENAI_API_KEYon the host
π FRITZ!Box: New IP Phone Configuration
-
Open FRITZ!Box Web Interface
Go to http://fritz.box orhttp://192.168.178.1. -
Navigate to Telephony β Telephone Devices
Click βNew Deviceβ β choose βTelephone (with LAN/WLAN)β β click Next. -
Select βIP Telephoneβ
Give it a name, e.g.freya. -
Create credentials
- Username:
freya - Password: (choose a strong password, youβll use it in
pjsip.conf) - Allow registration from the home network: β
- Optionally allow from internet if Asterisk runs remotely.
- Username:
-
Finish setup
- The FRITZ!Box shows:
- Username:
freya - Registrar:
fritz.boxor192.168.178.1
- Username:
- Note these values for your Asterisk config.
- The FRITZ!Box shows:
-
Test registration
After starting your Docker container, open Asterisk CLI:docker exec -it fritz-voice-assistant asterisk -rvvv pjsip show registrationsYou should see
Registerednext to yourfritzendpoint.
Minimal project layout (only what you need)
.
βββ config/ # Asterisk & app configs (mounts into the container)
β βββ pjsip.conf.template # <- rename to pjsip.conf and set FRITZ user/password/IP
β βββ pjsip.conf # (your file after renaming; do NOT commit secrets)
β βββ extensions.conf # dialplan: routes inbound to ARI
β βββ http.conf # enables Asterisk HTTP/WS (for ARI)
β βββ ari.conf # ARI credentials
β βββ rtp.conf # RTP port range
β βββ asterisk.conf # base Asterisk config
β βββ freya.yaml # app settings (TTS/STT, webhook URL, etc.)
βββ docker/
β βββ supervisord.conf # starts Asterisk and ARI apps
βββ Dockerfile # builds Asterisk + Python venv image
βββ docker-compose.yml # runs Asterisk/ARI and Wyoming Piper
Edit only:
config/pjsip.conf(renamed from the provided template) to set<FRITZ_USER>,<FRITZ_PASS>,<FRITZ_IP>.
Quick install (Ubuntu)
https://docs.docker.com/engine/install/ubuntu/
Prepare config
# in your project root
cp config/pjsip.conf.template config/pjsip.conf
# open config/pjsip.conf and replace placeholders:
# <FRITZ_USER>, <FRITZ_PASS>, <FRITZ_IP> (e.g., 192.168.178.1)
Optional for examples:
export OPENAI_API_KEY=sk-... # on your host shell (not required for core call handling)
Docker Compose (copy/paste ready)
services:
fritz-voice-assistant:
build:
context: .
dockerfile: Dockerfile
image: fritz-voice-assistant:latest
network_mode: "host" # SIP/RTP friendly (no NAT hassle)
restart: unless-stopped
environment:
- OPENAI_API_KEY # passed through from host if set
volumes:
- ./config/asterisk.conf:/etc/asterisk/asterisk.conf:ro
- ./config/pjsip.conf:/etc/asterisk/pjsip.conf:ro
- ./config/extensions.conf:/etc/asterisk/extensions.conf:ro
- ./config/rtp.conf:/etc/asterisk/rtp.conf:ro
- ./config/http.conf:/etc/asterisk/http.conf:ro
- ./config/ari.conf:/etc/asterisk/ari.conf:ro
- ./config/freya.yaml:/opt/freya/phone/freya.yaml:ro # app config
wyoming-piper:
image: rhasspy/wyoming-piper:latest
container_name: wyoming-piper
restart: unless-stopped
ports:
- "10200:10200"
volumes:
- /home/andi/docker-volumes/wyoming-piper:/data # adjust path if needed
command: --voice de_DE-ramona-low --update
Start it:
docker compose up -d --build
Test the Dialer API (outbound)
curl -sS -X POST http://127.0.0.1:8099/call -H 'Content-Type: application/json' -d '{"callerId":"<TARGET_NUMBER>","message":"Hello from Freya! This is a test."}'
Inbound: call your FRITZ!Box number and watch Asterisk logs.
n8n (optional, very short)
Quick run:
docker run -d --name n8n -p 5678:5678 n8nio/n8n
In n8n:
- Add a Webhook (Trigger) node (POST), copy its URL.
- Add HTTP Request node calling
http://localhost:8099/callwith JSON body:{ "callerId": "0176...", "message": "Your dynamic text here" } - Add a Webhook Respond node if you want to return data immediately.
Troubleshooting (Asterisk essentials)
Enter the Asterisk CLI inside the container
docker exec -it fritz-voice-assistant asterisk -rvvvv
Reload and inspect PJSIP
pjsip reload
pjsip show registrations
pjsip show endpoints
pjsip show endpoint fritz-endpoint
SIP message trace / RTP trace
pjsip set logger on
rtp set debug on
Typical fixes
- 401 Unauthorized on register β check
<FRITZ_USER>/<FRITZ_PASS>/<FRITZ_IP>inconfig/pjsip.conf, confirm IP phone is enabled in FRITZ!Box. - No audio β ensure
Answer()beforeStasis()inextensions.conf; endpoint hasallow=alaw,ulaw; checkrtp set debug onshows βGot/Sent RTPβ. - Dialer 500 βAllocation failedβ β use a valid endpoint and/or AOR contact (e.g.,
contact=sip:<FRITZ_IP>:5060in AOR, or dialPJSIP/fritz-endpoint/sip:{number}@<FRITZ_IP>).
Exit CLI
exit
License
Open Source (MIT).
About
Built to connect a FRITZ!Box with n8n via Asterisk ARI β with fast, local TTS using Piper.
Created by Andreas (Hobby Developer).