fieldpoc/README.md

464 lines
9.6 KiB
Markdown

# 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](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.