{ 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: "