107 lines
3.9 KiB
Nix
107 lines
3.9 KiB
Nix
{ lib }:
|
|
|
|
let
|
|
# Per-model config. Adding a new hardware model:
|
|
# 1. Add an entry here (PBX-relevant fields only)
|
|
# 2. Add a provisioning template in ./provisioning/templates/<model>.nix
|
|
# exporting: desktopSize, thumbnailSize, mkConfig, mkDialplan
|
|
models = {
|
|
"cisco-8961" = {
|
|
endpointTemplate = "endpoint-cisco-8961";
|
|
maxContacts = 1;
|
|
hasProvisioning = true;
|
|
hasIntercom = true; # auto-answer speakerphone line
|
|
hasMultiLine = true; # separate L2 line for family/shared DID
|
|
codecs = [ "g722" "alaw" "ulaw" "ilbc" ];
|
|
};
|
|
"cisco-9971" = {
|
|
endpointTemplate = "endpoint-cisco-8961";
|
|
maxContacts = 1;
|
|
hasProvisioning = true;
|
|
hasIntercom = true;
|
|
hasMultiLine = true;
|
|
codecs = [ "g722" "alaw" "ulaw" "ilbc" ];
|
|
};
|
|
"sip-client" = {
|
|
endpointTemplate = "endpoint-generic";
|
|
maxContacts = 1;
|
|
hasProvisioning = false;
|
|
hasIntercom = false;
|
|
hasMultiLine = false;
|
|
codecs = [ "opus" "g722" "alaw" "ulaw" ];
|
|
};
|
|
};
|
|
|
|
# Shared option set for a physical phone device.
|
|
# isPersonPhone = true → no extension/displayName fields (inherited from person)
|
|
# isPersonPhone = false → includes extension and displayName
|
|
phoneDeviceOptions = isPersonPhone: {
|
|
model = lib.mkOption {
|
|
type = lib.types.enum (lib.attrNames models);
|
|
description = "Phone model. Use \"sip-client\" for software SIP clients (no provisioning file).";
|
|
};
|
|
label = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "";
|
|
description = "Label shown on the phone screen (max ~12 chars for Cisco). Required for provisioned hardware phones.";
|
|
};
|
|
password = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "SIP registration password.";
|
|
};
|
|
} // lib.optionalAttrs (!isPersonPhone) {
|
|
extension = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Internal extension number for this shared phone.";
|
|
};
|
|
displayName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "";
|
|
description = "Name shown in the phone directory.";
|
|
};
|
|
};
|
|
|
|
# Unified view of all physical devices, keyed by SIP identity (MAC or username).
|
|
# Each entry carries the fields needed by sub-modules without them having to
|
|
# know about sharedPhones vs persons.
|
|
mkAllPhones = cfg:
|
|
lib.mapAttrs (key: p: {
|
|
inherit (p) model label password;
|
|
extension = p.extension;
|
|
displayName = p.displayName;
|
|
personKey = null;
|
|
sharedLine = true; # shared phones always participate in shared line
|
|
mailboxExt = null; # shared phones have no personal mailbox
|
|
}) cfg.sharedPhones
|
|
//
|
|
lib.foldlAttrs (acc: personKey: person:
|
|
acc // lib.mapAttrs (_key: ph: {
|
|
inherit (ph) model label password;
|
|
extension = person.extension;
|
|
displayName = person.displayName;
|
|
personKey = personKey;
|
|
sharedLine = person.sharedLine;
|
|
mailboxExt = if person.mailbox then person.extension else null;
|
|
}) person.phones
|
|
) {} cfg.persons;
|
|
|
|
# Auto-generate intercom extensions for provisioned phones.
|
|
mkIntercomEntries = cfg: allPhones:
|
|
if cfg.intercomPrefix == null then []
|
|
else lib.concatLists (lib.mapAttrsToList (key: phone:
|
|
let m = models.${phone.model}; in
|
|
lib.optional m.hasIntercom {
|
|
extension = "${cfg.intercomPrefix}${phone.extension}";
|
|
endpoint = "${key}-intercom";
|
|
phoneKey = key;
|
|
target = phone.extension;
|
|
displayName = "Intercom ${phone.displayName}";
|
|
password = phone.password;
|
|
endpointTemplate = m.endpointTemplate;
|
|
maxContacts = m.maxContacts;
|
|
}
|
|
) allPhones);
|
|
|
|
in {
|
|
inherit models phoneDeviceOptions mkAllPhones mkIntercomEntries;
|
|
}
|