# 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": "", "hw-address": "", "ip-address": "10.222.222.11", "option-data": [ { "data": "", "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": "" "sipsecret": "" } } ``` ### 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](https://git.clerie.de/clerie/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.