nix/modules/voip/options.nix

339 lines
13 KiB
Nix

{ lib, ... }:
let
phones = import ./phones.nix { inherit lib; };
in {
options.services.voip = {
enable = lib.mkEnableOption "VoIP provisioning (Asterisk + TFTP)";
serverAddress = lib.mkOption {
type = lib.types.str;
description = "IP address or hostname of this server (used in phone configs and SIP).";
};
ntpServer = lib.mkOption {
type = lib.types.str;
description = "NTP server for phones. Defaults to serverAddress.";
default = "";
};
sipPort = lib.mkOption {
type = lib.types.port;
default = 5060;
};
rtpStart = lib.mkOption {
type = lib.types.port;
default = 10000;
};
rtpEnd = lib.mkOption {
type = lib.types.port;
default = 20000;
};
directoryName = lib.mkOption {
type = lib.types.str;
default = "tel.baubs.net";
description = "Name shown in the phone directory title.";
};
directoryPort = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "HTTP port for the phone directory and services.";
};
backgroundImages = lib.mkOption {
default = {};
description = ''
Attrset of background images keyed by display name.
Value is a path to any image file it will be resized automatically
to the correct dimensions for each phone model during build.
For best results, use a 4:3 aspect ratio source image.
'';
type = lib.types.attrsOf lib.types.path;
};
sharedPhones = lib.mkOption {
default = {};
description = ''
Shared/location phones not assigned to a specific person (e.g. hallway, kitchen).
These have their own extension but no personal voicemail mailbox.
For cisco-8961, the key must be the lowercase MAC address (no colons).
For sip-client, the key is a free-form username.
'';
type = lib.types.attrsOf (lib.types.submodule {
options = phones.phoneDeviceOptions false;
});
};
persons = lib.mkOption {
default = {};
description = "People with personal extensions, optional voicemail mailboxes, and their own phones.";
type = lib.types.attrsOf (lib.types.submodule {
options = {
extension = lib.mkOption {
type = lib.types.str;
description = "Personal extension number.";
};
displayName = lib.mkOption {
type = lib.types.str;
default = "";
description = "Name shown in the directory and on caller ID.";
};
sharedLine = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether this person's phones participate in the shared/family line (L2).
Set to false for persons who should only be reachable on their personal
extension and must not appear in "all" DID routing.
'';
};
mailbox = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether this person gets a personal voicemail mailbox.";
};
ringTimeout = lib.mkOption {
type = lib.types.ints.positive;
default = 30;
description = "Seconds to ring before going to voicemail (or hanging up if no mailbox).";
};
mailboxGreeting = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Audio file played to callers before the voicemail beep. Transcoded to multiple formats at build time. null = Asterisk's default announcement.";
};
phones = lib.mkOption {
default = {};
description = ''
Phones belonging to this person, keyed by SIP identity.
For cisco-8961, the key must be the lowercase MAC address (no colons).
For sip-client, the key is a free-form username.
'';
type = lib.types.attrsOf (lib.types.submodule {
options = phones.phoneDeviceOptions true;
});
};
};
});
};
intercomPrefix = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Dial prefix for auto-generated intercom extensions. e.g. \"*80\" generates *80100 for ext 100. Only intercom-capable (provisioned) phones get entries.";
};
mohClasses = lib.mkOption {
default = {};
description = "Music on hold classes. Files are transcoded to ulaw at build time.";
type = lib.types.attrsOf (lib.types.submodule {
options = {
files = lib.mkOption {
type = lib.types.listOf lib.types.path;
description = "Audio files to play (MP3, WAV, etc). Transcoded to 8kHz ulaw automatically.";
};
sort = lib.mkOption {
type = lib.types.enum [ "random" "alphabetical" ];
default = "random";
};
};
});
};
sipTrunks = lib.mkOption {
default = {};
description = "External SIP provider trunks, keyed by a short name (e.g. \"provider-a\").";
type = lib.types.attrsOf (lib.types.submodule {
options = {
host = lib.mkOption {
type = lib.types.str;
default = "";
description = "SIP provider hostname or IP address. Use hostFile to read from a file.";
};
hostFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "File containing the SIP provider hostname. Takes precedence over host.";
};
username = lib.mkOption {
type = lib.types.str;
default = "";
description = "SIP account username. Use usernameFile to read from a file.";
};
usernameFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "File containing the SIP account username. Takes precedence over username.";
};
password = lib.mkOption {
type = lib.types.str;
default = "";
description = "SIP account password. Use passwordFile to read from a file.";
};
passwordFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "File containing the SIP account password. Takes precedence over password.";
};
transport = lib.mkOption {
type = lib.types.enum [ "udp" "tcp" ];
default = "udp";
};
callerId = lib.mkOption {
type = lib.types.str;
default = "";
description = "Outbound caller ID number presented to the provider. Leave empty to use provider default. Use callerIdFile to read from a file.";
};
callerIdFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "File containing the outbound caller ID. Takes precedence over callerId.";
};
codecs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "alaw" "ulaw" ];
description = "Codec preference list for this trunk, ordered highest priority first. Most providers only support G.711.";
};
};
});
};
sharedMailbox = lib.mkOption {
default = null;
description = "Shared voicemail mailbox accessible by all phones (family answering machine).";
type = lib.types.nullOr (lib.types.submodule {
options = {
mailboxId = lib.mkOption {
type = lib.types.str;
default = "200";
description = "Numeric mailbox ID used internally in voicemail.conf and VoiceMail(). Must be numeric.";
};
checkExtension = lib.mkOption {
type = lib.types.str;
default = "*98";
description = "Extension dialled to check this shared mailbox directly (via VoiceMailMain).";
};
displayName = lib.mkOption {
type = lib.types.str;
default = "Shared";
description = "Name shown in voicemail configuration.";
};
greeting = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Audio file played to callers before the voicemail beep. Transcoded to multiple formats at build time. null = Asterisk's default announcement.";
};
};
});
};
dids = lib.mkOption {
default = {};
description = "Inbound DID routing. Each DID must reference a key from sipTrunks.";
type = lib.types.attrsOf (lib.types.submodule {
options = {
number = lib.mkOption {
type = lib.types.str;
default = "";
description = "DID number in E.164 format (e.g. \"+4912345678\"). Use numberFile to read from a file.";
};
numberFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "File containing the DID number. Takes precedence over number.";
};
trunk = lib.mkOption {
type = lib.types.str;
description = "Key of the sipTrunks entry this DID arrives on.";
};
displayName = lib.mkOption {
type = lib.types.str;
default = "";
description = "Human-readable label for this DID (informational only).";
};
routing = lib.mkOption {
description = "How inbound calls on this DID are distributed to phones.";
type = lib.types.submodule {
options = {
type = lib.mkOption {
type = lib.types.enum [ "all" "person" "persons" ];
description = ''
all ring all phones (sharedPhones + all persons) on their L2 line
person ring a single person on their L1 line
persons ring a list of persons on their L2 line
'';
};
person = lib.mkOption {
type = lib.types.str;
default = "";
description = "Person key for routing.type = \"person\".";
};
persons = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Person keys for routing.type = \"persons\".";
};
timeout = lib.mkOption {
type = lib.types.ints.positive;
default = 30;
description = "Seconds to ring before going to voicemail (or hanging up).";
};
};
};
};
mailbox = lib.mkOption {
type = lib.types.enum [ "shared" "person" "none" ];
default = "shared";
description = ''
shared go to sharedMailbox on no answer (requires sharedMailbox to be set)
person go to the routed person's mailbox on no answer (only valid with routing.type = "person")
none hang up on no answer
'';
};
musicOnHold = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
MOH class name to play to the caller while phones ring, instead of ringback.
Must match a key in mohClasses. null = standard ringback.
'';
};
};
});
};
extensions = lib.mkOption {
default = {};
description = ''
Extra extensions: page groups and custom app entries.
Line extensions are auto-generated from sharedPhones and persons do not declare them here.
'';
type = lib.types.attrsOf (lib.types.submodule {
options = {
mode = lib.mkOption {
type = lib.types.enum [ "page" "app" ];
default = "page";
description = ''
Extension mode:
- "page": one-way announcement to all phones
- "app": custom Asterisk dialplan application
'';
};
displayName = lib.mkOption {
type = lib.types.str;
default = "";
};
app = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Verbatim Asterisk dialplan app. Required for mode = \"app\".";
};
};
});
};
};
}