How to Add a Live Yealink Remote Phonebook in FreePBX v17 (Using EPM)

Logicom USA  > Uncategorized >  How to Add a Live Yealink Remote Phonebook in FreePBX v17 (Using EPM)
0 Comments

Keeping a consistent, up-to-date company directory on every desk phone can be a pain—especially when you’re managing multiple Yealink devices across a business. In this guide, we’ll generate a Yealink-compatible XML directory directly from the FreePBX users table, place it in /tftpboot, and enable it across all Yealink phones using EPM > Base File Edit—so your remote phonebook stays current automatically with no per-phone configuration.

This guide shows how to:

  1. Generate a Yealink-compatible directory XML from FreePBX users.
  2. Place it in /tftpboot.
  3. Make it available at your provisioning URL.
  4. Enable it on all Yealink phones using a few lines added to the Yealink Base File.

What We’re Building

We want Yealink phones to successfully download a remote phonebook from:

__provisionAddress__/company_directory.xml

Using a Yealink-compatible XML structure like:

<?xml version="1.0" encoding="UTF-8"?>
<CompanyIPPhoneDirectory>
  <DirectoryEntry>
    <Name>Aaron Atwood</Name>
    <Telephone>5584</Telephone>
  </DirectoryEntry>
</CompanyIPPhoneDirectory>

1) Create the XML Generator Script

Create this script on your FreePBX v17 system:

sudo nano /usr/local/sbin/xml_directory.sh

Paste the full script below.

xml_directory.sh (Yealink-compatible output)

#!/usr/bin/env bash
set -euo pipefail

# -----------------------------------------------------------------------------
# Build an XML Company Directory for Yealink IP phones
# Pulls extension + name from FreePBX "users" table.
#
# Output structure:
#
# <?xml version="1.0" encoding="UTF-8"?>
# <CompanyIPPhoneDirectory>
#   <DirectoryEntry>
#     <Name>...</Name>
#     <Telephone>...</Telephone>
#   </DirectoryEntry>
# </CompanyIPPhoneDirectory>
#
# FreePBX 17 notes:
# - /etc/freepbx.conf is PHP and often the authoritative place for AMPDB*.
# - This script reads AMPDB* from amportal.conf if present, otherwise
#   pulls them from freepbx.conf via php.
# -----------------------------------------------------------------------------

# --- TFTP directory ---
TFTP_DIR="/tftpboot"

FILE="${TFTP_DIR}/company_directory.xml"
TMP="$(mktemp)"

# --- Defaults ---
DBUSER=""
DBPASS=""
DBNAME="asterisk"
DBHOST="localhost"

# --- Helper: read value from /etc/freepbx.conf (PHP) ---
get_php_amp_conf() {
  local key="$1"
  if command -v php >/dev/null 2>&1 && [[ -f /etc/freepbx.conf ]]; then
    php -r "
      error_reporting(0);
      \$amp_conf = [];
      include '/etc/freepbx.conf';
      if (isset(\$amp_conf['$key'])) { echo \$amp_conf['$key']; }
    " 2>/dev/null || true
  fi
}

# --- First choice: amportal.conf (older style) ---
if [[ -f /etc/asterisk/amportal.conf ]]; then
  # shellcheck disable=SC1091
  source /etc/asterisk/amportal.conf || true

  DBUSER="${AMPDBUSER:-}"
  DBPASS="${AMPDBPASS:-}"
  DBNAME="${AMPDBNAME:-asterisk}"
  DBHOST="${AMPDBHOST:-localhost}"
fi

# --- Second choice: /etc/freepbx.conf (PHP amp_conf array) ---
if [[ -z "$DBUSER" ]]; then
  DBUSER="$(get_php_amp_conf "AMPDBUSER")"
  DBPASS="$(get_php_amp_conf "AMPDBPASS")"
  DBNAME="$(get_php_amp_conf "AMPDBNAME")"
  DBHOST="$(get_php_amp_conf "AMPDBHOST")"

  DBNAME="${DBNAME:-asterisk}"
  DBHOST="${DBHOST:-localhost}"
fi

# --- Last-ditch: grep patterns just in case ---
if [[ -z "$DBUSER" && -f /etc/freepbx.conf ]]; then
  DBUSER="$(grep -E "AMPDBUSER" /etc/freepbx.conf | sed -n "s/.*\['AMPDBUSER'\][[:space:]]*=[[:space:]]*'\([^']*\)'.*/\1/p" | head -n1 || true)"
  DBPASS="$(grep -E "AMPDBPASS" /etc/freepbx.conf | sed -n "s/.*\['AMPDBPASS'\][[:space:]]*=[[:space:]]*'\([^']*\)'.*/\1/p" | head -n1 || true)"
  DBNAME="$(grep -E "AMPDBNAME" /etc/freepbx.conf | sed -n "s/.*\['AMPDBNAME'\][[:space:]]*=[[:space:]]*'\([^']*\)'.*/\1/p" | head -n1 || true)"
  DBHOST="$(grep -E "AMPDBHOST" /etc/freepbx.conf | sed -n "s/.*\['AMPDBHOST'\][[:space:]]*=[[:space:]]*'\([^']*\)'.*/\1/p" | head -n1 || true)"

  DBNAME="${DBNAME:-asterisk}"
  DBHOST="${DBHOST:-localhost}"
fi

if [[ -z "$DBUSER" ]]; then
  echo "❌ ERROR: Could not determine FreePBX DB credentials."
  echo "   Checked:"
  echo "   - /etc/asterisk/amportal.conf"
  echo "   - /etc/freepbx.conf (PHP \$amp_conf array)"
  exit 1
fi

# --- XML escape helper ---
xml_escape() {
  local s="${1:-}"
  s="${s//&/&amp;}"
  s="${s//</&lt;}"
  s="${s//>/&gt;}"
  s="${s//\"/&quot;}"
  s="${s//\'/&apos;}"
  printf '%s' "$s"
}

# --- Build XML header ---
{
  echo '<?xml version="1.0" encoding="UTF-8"?>'
  echo '<CompanyIPPhoneDirectory>'
} > "$TMP"

# --- Query DB safely ---
QUERY="SELECT extension, name FROM users WHERE extension IS NOT NULL AND extension <> '' ORDER BY name, extension;"

MYSQL_CMD=(mysql -N -B -h "$DBHOST" -u "$DBUSER")
if [[ -n "${DBPASS:-}" ]]; then
  MYSQL_CMD+=(-p"$DBPASS")
fi
MYSQL_CMD+=("$DBNAME" -e "$QUERY")

# Read rows (extension<TAB>name)
while IFS=$'\t' read -r exten name; do
  [[ -z "${exten:-}" ]] && continue
  name="${name:-}"

  # If name is missing, fall back to extension
  display_name="$name"
  if [[ -z "$display_name" ]]; then
    display_name="$exten"
  fi

  # Escape for XML
  esc_name="$(xml_escape "$display_name")"
  esc_ext="$(xml_escape "$exten")"

  {
    echo '  <DirectoryEntry>'
    echo "    <Name>${esc_name}</Name>"
    echo "    <Telephone>${esc_ext}</Telephone>"
    echo '  </DirectoryEntry>'
  } >> "$TMP"

done < <("${MYSQL_CMD[@]}")

# --- Close XML ---
echo '</CompanyIPPhoneDirectory>' >> "$TMP"

# --- Install final file atomically ---
mkdir -p "$TFTP_DIR"
mv "$TMP" "$FILE"

# Ownership/permissions
chown asterisk:asterisk "$FILE" 2>/dev/null || true
chmod 0644 "$FILE"

echo "✅ OK - wrote $FILE"

Make it executable:

sudo chmod +x /usr/local/sbin/xml_directory.sh

2) Run It Once Manually



sudo -u asterisk /usr/local/sbin/xml_directory.sh

Confirm the output:



ls -l /tftpboot/company_directory.xml
head -n 20 /tftpboot/company_directory.xml

3) Ensure the File is Served by the Provisioning Web Server

Most EPM setups already serve the provisioning directory via the PBX’s web server.

To confirm the phone can reach it, test from another machine:

curl -i http://YOUR_PBX_FQDN/company_directory.xml

or if your provisioning is IP-based:

curl -i http://YOUR_PBX_IP/company_directory.xml

You should see:

  • HTTP/1.1 200 OK
  • Content-Type: application/xml
  • Your XML content

If your provisioning URL is different, match whatever your phones use for provisioning. The goal is that the same base provisioning address can also serve company_directory.xml.


4) Automate It via Cron

Edit the system crontab:

sudo nano /etc/crontab

Add:

0 * * * * asterisk /usr/local/sbin/xml_directory.sh

This rebuilds the directory every hour so it stays in sync with user changes.


5) Enable the Phonebook via EPM > Base File Edit (Yealink)

This is the key step that makes this scalable.

In FreePBX:

  1. Go to:
    Admin → Endpoint Manager
  2. Select:
    Brands → Yealink
  3. Open:
    Base File Edit
  4. Add these lines:
remote_phonebook.data.1.name = Phonebook
remote_phonebook.data.1.url = __provisionAddress__/company_directory.xml
features.remote_phonebook.enable = 1
  1. Save
  2. Rebuild configs
  3. Reboot or reprovision the phones

Why This Method Is Better

Instead of manually configuring each phone’s remote phonebook URL, using the Yealink Base File:

  • Pushes the config to every Yealink model in EPM
  • Uses the dynamic __provisionAddress__ token
  • Keeps your directory centralized and consistent

This is especially helpful when you manage multiple sites or VLANs, or when phones are re-provisioned often.


Troubleshooting Tips

Phone still says “Cannot download remote phonebook”

Check these quickly:

  1. XML formatting
    • Yealink is picky.
    • Your root must be:
<CompanyIPPhoneDirectory>

And entries must be:

<DirectoryEntry>

Provisioning address accessibility

  • The phone must be able to reach the PBX’s provisioning URL.

HTTP vs HTTPS

  • If you’re using HTTPS with a self-signed cert, test HTTP first.

Permissions

  • Confirm:
    • ls -l /tftpboot/company_directory.xml
    • 0644 is typically perfect.

Final Result

Once applied, your Yealink phones will:

  • Auto-enable Remote Phonebook
  • Pull:
  • __provisionAddress__/company_directory.xml
  • Display a live-updated company directory based on FreePBX users


Leave a Reply

Your email address will not be published. Required fields are marked *