8 minutes
Building Téléfonefix - Baby’s first international landline
Téléfonefix is a kid-friendly telephone system allowing kids to safely call relatives, locally and abroad.
The main features I sought when building téléfonefix were:
- Do not expose the user to a screen (no apps or fancy phones)
- Use a physical phone
- Perform calls locally and internationally
- Permit/decline calls based on a ruleset. Specifically:
- only permit calls to be made to an adult-controlled set of numbers
- timezones and “awake/asleep” schedules, to avoid calling Europe at their 1am
- prevent inbound calls entirely
- As user-friendly as possible
Shopping list
- A corded phone: any phone with an RJ11 connector will work. A rotary dial phone would add cool factor for kids old enough to dial them. I used this “Telephone for Seniors” . The pictured speed dial numbers are perfect for very young kids to choose who they want to call
- An analog telephone adapter: I use the Grandstrean HT801 . It is well-documented and reliable
- A raspberry pi (or any compute really). For this setup, I used a Pi 4 B. It embeds WiFi and an ethernet port making it easy to connect both to your network and directly wire the HT801 to the Pi
- A twilio account with a number purchased (~ $1.15/month)
Architecture
Hardware setup
Phone to HT801
We simply plug an RJ11
cable in between the Phone and the HT801.
HT801 to Raspberry Pi
I opted to connect these two directly with an RJ45
as we will see below.
Phone
On the phone itself, to make updating phone numbers easier, I assigned each of the 9 speed dial picture buttons with a number from 100
to 107
and assigned 200
to the last one (which I keep for debugging). Those extensions will be translated later on to actual numbers by a small golang program I built for the occasion, allo-wed
.
Twilio setup
First, you will need to create an Elastic SIP Trunk in twilio.
Once created, in the Termination tab, set up the Authentication. Create a new set of credentials (your telefonefix_twilio_user
and telefonefix_twilio_password
). IP Access control lists are also available.
In the Numbers tab, buy a new number and assign it to the trunk. That number will be your telefonefix_twilio_phone_number
value (formatted as '+15555555555'
). Note that numbers have a monthly cost independent of usage (~ $1.15/month).
Incoming calls are not something I am looking for, so I did not change Origination.
Téléfonefix stack configuration
I built ansible-role-telefonefix
to handle the configuration.
Let’s look at a telefonefix.yml
playbook example and detail each task:
- hosts: telefonefix
become: true
gather_facts: false
vars:
telefonefix_asterisk_playback_patterns:
connecting:
fr: "Un instant, je vous connecte à \"${contact_name}\""
pt: "Só um momento. Ligando para \"${contact_name}.\""
not-allowed:
fr: "\"${contact_name}\" fait dodo, rappelle plus tard!"
pt: "\"${contact_name}.\" está dormindo, ligue mais tarde!"
currently-busy:
fr: "\"${contact_name}\" est occupé, rappelle plus tard!"
pt: "\"${contact_name}.\" está occupado, ligue mais tarde!"
unavailable:
fr: "\"${contact_name}\" est occupé, rappelle plus tard!"
pt: "\"${contact_name}.\" está occupado, ligue mais tarde!"
telefonefix_voice_map:
en: "en_US-amy-low"
fr: "fr_FR-siwis-low"
pt: "pt_BR-cadu-medium"
telefonefix_network_subnet: "192.168.100.0/24"
telefonefix_gateway_ip: "192.168.100.1"
ht801_mac: # TODO
ht801_static_ip: "192.168.100.20"
telefonefix_dns_servers:
- "8.8.8.8"
- "8.8.4.4"
telefonefix_ethernet_interface: "eth0"
telefonefix_asterisk_extra_sounds_folder: /tmp/telefonefix/extra_sounds
telefonefix_super_user_override_prefix: '23'
telefonefix_public_ip: # TODO
telefonefix_asterisk_phone_user: '6001'
telefonefix_asterisk_phone_password: # TODO
telefonefix_twilio_domaine: # TODO
telefonefix_twilio_phone_number: # TODO
telefonefix_twilio_user: # TODO
telefonefix_twilio_password: # TODO
ht801_password: # TODO
tasks:
- name: Generating asterisk voices
include_role:
name: ansible-role-telefonefix
tasks_from: asterisk_playbacks_generate.yml
- name: DHCP config
include_role:
name: ansible-role-telefonefix
tasks_from: dhcp.yml
- name: Asterisk setup
include_role:
name: ansible-role-telefonefix
tasks_from: asterisk.yml
- name: HT801 setup
include_role:
name: ansible-role-telefonefix
tasks_from: ht801.yml
The variables are mostly self-explanatory and can be updated to fit different needs and setups.
We will review the Generating asterisk voices
task at the end, and start with the main tasks first.
Task DHCP config
Configures a dhcp server on the raspberry pi, and has it assign a fixed ip to the HT801 in their own shared very local LAN to keep things isolated and simple.
We can also easily run asterisk on a VPS, and have the HT801 point directly to it.
Task Asterisk setup
This is the main task. It:
- installs docker
- pushes the main configuration files
- pushes optional sound files for asterisk
- launches the
asterisk
docker container (asterisk + allo-wed )
Let’s review the main configuration files.
File extensions.conf
extensions.conf defines what happens when a number is dialed.
On the physical phone, I have defined each speed dial pictured number to dial a number from 100
to 107
. The last picture is defined as 200
, and currently used for debugging
.
Calls coming in using one of those numbers are handled here:
We call the allo-wed
script with the dialed extension as a parameter. If calls are allowed to this number, it returns the 1XX
number to real world number translation, based on its configuration file
.
If the call is denied, we let the user know:
Otherwise, we announce success and perform the call:
A parental override
can be enabled by setting a telefonefix_super_user_override_prefix
value.
For example, with telefonefix_super_user_override_prefix = 23
, you will be able to bypass call restrictions on the 101
extensions by dialing 23101
.
File pjsip.conf
This is where we configure the actual communication stack.
First
we setup our local extension [6001]
, our physical phone, and set up authentication for it so the HT801 can connect to asterisk.
Then we set up the Twilio trunk for outbound calls, linking and authenticating to our domain.
File rtp.conf
The RTP configuration is lightly edited to reduce the port range. We have a single user, and not having to add port forwarding for the full range can make home router/firewall configuration easier.
File allo-wed.yml
You need an asterisk/allo-wed.yml
file alongside your calling playbook (eg in files/asterisk/allo-wed.yml
). A configuration example is available in the allo-wed repo
.
This file will define who each speed dial extension will call, their language and name (for fancy spoken messages), their timezone and awake hours, and the number to dial.
Number format tends to be the tricky part here. I have had success putting the full number with country code and leading +
. For example, to dial a French cell: '+336XXXXXXXX'
.
Task HT801 setup
We send a POST
request to the HT801’s administration API, essentially setting up communication and authentication with the asterisk server.
Bonus: Generating asterisk voices
Using my previous Text To Speech Setup
and asterisk’s Playback
feature, I generate custom messages that asterisk will play for different occasion. For example, when dialing a specific contact, asterisk will announce in the callee’s language “Dialing XXX, please hold”.
The messages are generated based on these variables:
telefonefix_asterisk_playback_patterns:
connecting:
fr: "Un instant, je vous connecte à \"${contact_name}\""
pt: "Só um momento. Ligando para \"${contact_name}.\""
not-allowed:
fr: "\"${contact_name}\" fait dodo, rappelle plus tard!"
pt: "\"${contact_name}.\" está dormindo, ligue mais tarde!"
currently-busy:
fr: "\"${contact_name}\" est occupé, rappelle plus tard!"
pt: "\"${contact_name}.\" está occupado, ligue mais tarde!"
unavailable:
fr: "\"${contact_name}\" est occupé, rappelle plus tard!"
pt: "\"${contact_name}.\" está occupado, ligue mais tarde!"
Using these voices, which I found to be the best for each language I was interested in:
telefonefix_voice_map:
en: "en_US-amy-low"
fr: "fr_FR-siwis-low"
pt: "pt_BR-cadu-medium"
The Generating asterisk voices
task will start gopipertts
on the local machine, generate all the messages we need, and convert them to be playable by asterisk.
Firewall configuration
Twilio needs to be able to reach asterisk on the following ports, you will need to configure your firewall and port forwarding accordingly:
5060/udp
: SIP po rt10000-10100/udp
: the RTP port range
Conclusion and future improvements
The phone has been a huge success, and the concept easily graspable by young kids.
A few potential improvements:
- preventing hangups… Kids get excited, pick up the phone, hang it up, pick it up again. Being able to prevent premature call end would be useful (twilio starts charging immediately too, so you’ll be billed the full minute for that 3s call-hang-up)
- limiting calls per day/hours? It could be useful to have
allo-wed
prevent the user from calling the same contact 15 times within a minute - better error handling. There are cases where calls fail with little to no feedback. I’m not sure where the issues come from, but gathering more information from twilio/asterisk and speaking it to the user would be helpful
- forcing speaker mode… But that would require tweaking with the phone itself
If you implement this and have feedback, please reach out !
Links
Core Components
- Asterisk - The open source PBX system
- Twilio - SIP trunking and phone number provider
- Grandstream HT801 - Analog telephone adapter
Software & Configuration
- allo-wed - Extension translation and call scheduling
- ansible-role-telefonefix - Ansible automation for the entire setup
- docker-asterisk - Ubuntu-based Asterisk container
- gopipertts - Text-to-speech voice generation
References & Inspiration
- Connecting a Grandstream HT80x to Twilio SIP - Helpful setup guide
- Twilio + Grandstream Setup - Another useful walkthrough
- Astérix et Obélix - Naming convention inspiration