98 lines
4.2 KiB
Nix
98 lines
4.2 KiB
Nix
{ lib, pkgs, cfg, models, allPhones, backgroundEntries }:
|
|
|
|
let
|
|
hasTrunk = cfg.sipTrunks != {};
|
|
|
|
# Page extensions for the intercom button auto-dial
|
|
pageExtension =
|
|
let pages = lib.attrNames (lib.filterAttrs (_: e: e.mode == "page") cfg.extensions);
|
|
in if pages != [] then lib.head pages else null;
|
|
|
|
hasIntercomButton = cfg.intercomPrefix != null && pageExtension != null;
|
|
|
|
# Collect all internal extension numbers to generate exact-match patterns.
|
|
# This tells the phone "this number is complete" so it dials immediately
|
|
# rather than waiting for the timeout or firing on the first digit.
|
|
allExtensions = lib.unique (
|
|
lib.mapAttrsToList (_: p: p.extension) cfg.sharedPhones
|
|
++ lib.mapAttrsToList (_: p: p.extension) cfg.persons
|
|
);
|
|
|
|
# Star extensions: intercom prefix + each extension, plus custom extensions.
|
|
allStarExtensions =
|
|
(lib.optionals (cfg.intercomPrefix != null)
|
|
(map (ext: "${cfg.intercomPrefix}${ext}") allExtensions))
|
|
++ lib.attrNames cfg.extensions
|
|
++ lib.optional (cfg.sharedMailbox != null) cfg.sharedMailbox.checkExtension;
|
|
|
|
# Generate the dial template for a phone, given its intercom lineIndex.
|
|
# The lineIndex is derived from familyLineEnabled — the same condition used
|
|
# in the Cisco template for button layout, so both are always in sync.
|
|
#
|
|
# Pattern evaluation: the phone tests templates top-to-bottom and dials as
|
|
# soon as digits match a pattern with timeout="0", or after the timeout
|
|
# elapses for patterns with timeout > 0. Explicit patterns must come before
|
|
# the catch-all "." so the phone doesn't fire early on a partial match.
|
|
mkDialplanXml = key: intercomLineIndex:
|
|
let
|
|
# Stable UUID derived from a hash of all inputs that affect this dialplan.
|
|
# Changes whenever extensions, trunk config, or intercom config changes,
|
|
# causing phones to reload the file. Format: 8-4-4-4-12 hex chars.
|
|
h = builtins.hashString "sha256" (builtins.toJSON {
|
|
inherit key allExtensions allStarExtensions hasTrunk hasIntercomButton intercomLineIndex;
|
|
});
|
|
versionStamp = "${builtins.substring 0 8 h}-${builtins.substring 8 4 h}-${builtins.substring 12 4 h}-${builtins.substring 16 4 h}-${builtins.substring 20 12 h}";
|
|
|
|
# Exact-match a complete extension number — dial immediately.
|
|
extTemplate = ext: " <TEMPLATE match=\"${ext}\" timeout=\"0\" />";
|
|
in ''
|
|
<dialTemplate>
|
|
<versionStamp>${versionStamp}</versionStamp>
|
|
${lib.optionalString hasIntercomButton
|
|
" <TEMPLATE match=\"\" timeout=\"0\" rewrite=\"${pageExtension}\" line=\"${toString intercomLineIndex}\" />"}
|
|
${lib.concatMapStrings (ext: extTemplate ext + "\n") allExtensions}
|
|
${lib.concatMapStrings (ext: extTemplate ext + "\n") allStarExtensions}
|
|
<TEMPLATE match=".." timeout="5" />
|
|
</dialTemplate>
|
|
'';
|
|
|
|
in
|
|
pkgs.linkFarm "voip-tftp-root" (
|
|
lib.concatLists (lib.mapAttrsToList (key: phone:
|
|
let m = models.${phone.model}; in
|
|
lib.optionals m.hasProvisioning (
|
|
let
|
|
upperKey = lib.toUpper key;
|
|
familyLineEnabled = hasTrunk && phone.personKey != null;
|
|
intercomLineIndex = if familyLineEnabled then 3 else 2;
|
|
dialplanFilename = "dialplan-${upperKey}.xml";
|
|
|
|
xml = m.template ({
|
|
mac = key;
|
|
inherit (phone) label password displayName;
|
|
serverAddress = cfg.serverAddress;
|
|
ntpServer = cfg.ntpServer;
|
|
sipPort = cfg.sipPort;
|
|
directoryPort = cfg.directoryPort;
|
|
dialplanFile = dialplanFilename;
|
|
} // lib.optionalAttrs (cfg.intercomPrefix != null) {
|
|
intercomEnabled = true;
|
|
intercomPassword = phone.password;
|
|
} // lib.optionalAttrs familyLineEnabled {
|
|
inherit familyLineEnabled;
|
|
familyLineLabel =
|
|
if cfg.sharedMailbox != null
|
|
then cfg.sharedMailbox.displayName
|
|
else "Familie";
|
|
familyLinePassword = phone.password;
|
|
});
|
|
in [
|
|
{ name = "SEP${upperKey}.cnf.xml";
|
|
path = pkgs.writeText "SEP${upperKey}.cnf.xml" xml; }
|
|
{ name = dialplanFilename;
|
|
path = pkgs.writeText dialplanFilename (mkDialplanXml key intercomLineIndex); }
|
|
]
|
|
)
|
|
) allPhones)
|
|
++ backgroundEntries
|
|
)
|