Go to file
2023-10-14 18:19:49 +02:00
docs Explain FieldPOC setup config 2023-10-14 18:19:49 +02:00
fieldpoc Fix handling of temp extension data 2023-06-18 16:04:21 +02:00
nix/modules Add NixOS modules 2023-06-18 21:51:21 +02:00
.gitignore Do not commit nix build products to git 2023-06-17 20:55:44 +02:00
fieldpoc_config.json Move extensions.json file path to config file 2022-09-02 22:03:38 +02:00
fieldpoc_extensions.json allow to claim extensions 2022-07-20 23:39:58 +02:00
flake.lock Retry after failing DECT tasks 2023-06-17 20:18:54 +02:00
flake.nix Warp docs in a mkdocs website 2023-09-15 18:00:18 +02:00
LICENSE Add license 2022-07-06 01:50:58 +02:00
mkdocs.yml Warp docs in a mkdocs website 2023-09-15 18:00:18 +02:00
pyproject.toml Add missing dependencies 2022-07-06 14:45:37 +02:00
README.md Describe system architecture 2022-09-01 13:33:27 +02:00

FieldPOC

A simple to use, good enough phone system for medium sized DECT networks.

Setup

FieldPOC requires a Mitel SIP DECT phone system, i.e. RFP L43 WLAN. Make sure you have the equipment, else this software is quit useless.

This is more a sample setup. Everything described here can be done on multiple different ways. We just try to document a way that is proven to work.

Get a computer, i.e. an APU Board, and install Archlinux on it. This can be a VM too, but you have to adapt stuff.

Config

Create a file fieldpoc_config.json. This is your FieldPOC config in JSON format. You will add details to it with each step.

Networking

We assume you use two network interfaces enp2s0 and enp3s0. Configuration examples are for systemd-networkd.

systemctl enable --now systemd-networkd.service

Your FieldPOC box will act as a normal DHCP Client on enp2s0. This is the interface you connect to the internet and where you may connect to the box via SSH.

/etc/systemd/network/20-wired.network

[Match]
Name=enp2s0

[Network]
DHCP=yes

The other interface enp3s0 will be the DECT network. This is the network where you connect your RFPs. We will run a dedicated DHCP Server on this network.

/etc/systemd/network/30-dect.network

[Match]
Name=enp3s0

[Network]
Address=10.222.222.1/24

As the DHCP Server for this network we use kea.

pacman -S kea

/etc/kea/kea-dhcp4.conf

{
  "Dhcp4": {
    "interfaces-config": {
      "interfaces": [
        "enp3s0"
      ]
    },
    "lease-database": {
      "name": "/var/lib/kea/dhcp4.leases",
      "persist": true,
      "type": "memfile"
    },
    "option-def": [
      {
        "code": 43,
        "encapsulate": "sipdect",
        "name": "vendor-encapsulated-options",
        "space": "dhcp4",
        "type": "empty"
      },
      {
        "code": 10,
        "name": "ommip1",
        "space": "sipdect",
        "type": "ipv4-address"
      },
      {
        "code": 19,
        "name": "ommip2",
        "space": "sipdect",
        "type": "ipv4-address"
      },
      {
        "code": 14,
        "name": "syslogip",
        "space": "sipdect",
        "type": "ipv4-address"
      },
      {
        "code": 15,
        "name": "syslogport",
        "space": "sipdect",
        "type": "int16"
      },
      {
        "code": 224,
        "name": "magic_str",
        "space": "dhcp4",
        "type": "string"
      }
    ],
    "subnet4": [
      {
        "option-data": [
          {
            "data": "10.222.222.1",
            "name": "routers"
          }
        ],
        "pools": [
          {
            "pool": "10.222.222.100 - 10.222.222.200"
          }
        ],
        "reservations": [
          {
            "hostname": "<name of your oom>",
            "hw-address": "<mac address of your oom>",
            "ip-address": "10.222.222.11",
            "option-data": [
              {
                "data": "<name of your oom>",
                "name": "host-name"
              },
              {
                "name": "vendor-encapsulated-options"
              },
              {
                "data": "10.222.222.11",
                "name": "ommip1",
                "space": "sipdect"
              },
              {
                "data": "OpenMobilitySIP-DECT",
                "name": "magic_str"
              }
            ]
          }
        ],
        "subnet": "10.222.222.0/24"
      }
    ]
  }
}

Start the service.

systemctl enable --now kea-dhcp4

Database

pacman -S postgresql

Init database.

su - postgres -c "initdb --locale en_US.UTF-8 -D '/var/lib/postgres/data'"
systemctl enable --now postgresql

Open SQL shell

sudo -u postgres psql

Create database and user for routing engine. Change the password.

CREATE USER fieldpoc WITH PASSWORD 'fieldpoc';
CREATE DATABASE fieldpoc WITH OWNER fieldpoc;

Add the database credetials to your fieldpoc_config.json.

{
  "database": {
    "host": "127.0.0.1",
    "username": "fieldpoc",
    "password": "fieldpoc",
    "database": "fieldpoc"
  }
}

Phone Server

pacman -S yate

Remove all files in /etc/yate/ and add the following instead:

Dialout SIP connection

accfile.conf

[dialout]
enabled=yes
protocol=sip
registrar=10.222.222.254
username=dialout
password=xxx

Call deduplication

cdrbuild.conf

[parameters]
X-Eventphone-Id=false

Yate remote control socket

extmodule.conf

[listener fieldpoc]
type=tcp
addr=127.0.0.1
port=5039

Add the information for the remote control socket to fieldpoc_config.json:

{
  "yate": {
    "host": "127.0.0.1",
    "port": 5039
  }
}

Database connection

Replace credentials with the ones you have choosen.

pgsqldb.conf

[default]
host=127.0.0.1
database=fieldpoc
user=fieldpoc    
password=fieldpoc

Call routing

regexroute.conf

[default]
${username}^$=-;error=noauth
^iocaste$=goto dialin
^99991001$=tone/dial
^99991002$=tone/busy
^99991003$=tone/ring
^99991004$=tone/specdial
^99991005$=tone/congestion
^99991006$=tone/outoforder
^99991007$=tone/milliwatt
^99991008$=tone/info
^.*$=line/\0;line=dialout

[dialin]
${sip_x-called}^.*$=lateroute/\1

User registration

register.conf

[general]
expires=30

user.auth=yes
user.register=yes
user.unregister=yes
engine.timer=yes
;call.preroute=no
call.cdr=yes
linetracker=yes

[default]
priority=10
account=default

[user.auth]
query=SELECT password FROM users WHERE username='${username}' AND password IS NOT NULL AND password<>'' AND type='user' LIMIT 1;
result=password

[user.register]
query=INSERT INTO registrations (username, location, oconnection_id, expires) VALUES ('${username}', '${data}', '${oconnection_id}', NOW() + INTERVAL '${expires} s') ON CONFLICT ON CONSTRAINT uniq_registrations DO UPDATE SET expires = NOW() + INTERVAL '${expires} s'

[user.unregister]
query=DELETE FROM registrations WHERE (username = '${username}' AND location = '${data}' AND oconnection_id = '${connection_id}') OR ('${username}' = '' AND '${data}' = '' AND oconnection_id = '${connection_id}')

[engine.timer]
query=DELETE FROM registrations WHERE expires<=CURRENT_TIMESTAMP;

[call.cdr]
critical=no

[linetracker]
critical=yes
initquery=UPDATE users SET inuse=0 WHERE inuse is not NULL;DELETE from active_calls;
cdr_initialize=UPDATE users SET inuse=inuse+1 WHERE username='${external}';INSERT INTO active_calls SELECT username, x_eventphone_id FROM (SELECT '${external}' as username, '${X-Eventphone-Id}' as x_eventphone_id, '${direction}' as direction) as active_call WHERE x_eventphone_id != '' AND x_eventphone_id IS NOT NULL and direction = 'outgoing';
cdr_finalize=UPDATE users SET inuse=(CASE WHEN inuse>0 THEN inuse-1 ELSE 0 END) WHERE username='${external}';DELETE FROM active_calls WHERE username = '${external}' AND  x_eventphone_id = '${X-Eventphone-Id}' AND '${direction}' = 'outgoing';

DECT

Just choose one of your RFPs as your OMM. Find out the MAC address and fill it in the according fields in the kea config before.

Please generate a 16-characters long hexadecimal string as sipsecret. This allowes the SIP accounts used for DECT phones to get a deterministic, but somewhat secure internal password.

Create a privileged user account and add the credentials to fieldpoc_config.json:

{
  "omm": {
    "host": "10.222.222.11",
    "username": "omm",
    "password": "<password>"
    "sipsecret": "<secret>"
  }
}

FieldPOC

Make sure Python is installed.

pacman -S python

Install FieldPOC:

pip install git+https://git.clerie.de/clerie/fieldpoc@main

FieldPOC provides a telnet socket for debugging and state mangement, add config for this to fieldpoc_config.json too:

{
  "controller": {
    "host": "127.0.0.1",
    "port": 9437
  }
}

Usage

Init

Some datastructures have get prepared once:

fieldpoc -c /path/to/fieldpoc_config.json -e /path/to/fieldpoc_extensions.json --init

Run

fieldpoc -c /path/to/fieldpoc_config.json -e /path/to/fieldpoc_extensions.json

Debug

Show log messages:

fieldpoc -c /path/to/fieldpoc_config.json -e /path/to/fieldpoc_extensions.json --debug

Development setup

Make sure you have mitel_ommclient2 in your Python modules path.

Add user credentials to your OMM to fieldpoc_config.json.

Edit fieldpoc_extensions.json to meet your preferences.

Start FieldPOC:

python -m fieldpoc.run

List some options:

python -m fieldpoc.run --help

Architecture

FieldPOC is daemon that takes a defined state and makes all connected services fit that state. Each connected service is observed by a dedicated class executed in a seperate thread. There exist some more classes and threads to just glue everything together.

extensions.json

The extensions.json file is the core of the whole setup and defines the state. It contains which extensions exist and for what they should be used.

fieldpoc.py

This file containes the main class FieldPOC. It spawns the mentioned threads, initializes other classes and provices communication queues between the threads/classes.

config.py

This manages FieldPOCs configuration.

controller.py

The controller provices an interactive interface to manage and debug FieldPOC.

run.py

This is a thin wrapper to start FieldPOC.

dect.py

This deals with the Mitel OMM and configures all phones based on the current state.

routing.py

This part sets up Yate using Ywsd, registers all extensions and configures routing.

ywsd.py

This starts Ywsd with FieldPOC so you don't have to start it seperately.