diff --git a/hosts/telefonmann/default.nix b/hosts/telefonmann/default.nix
index 338d6cc..ea7132a 100644
--- a/hosts/telefonmann/default.nix
+++ b/hosts/telefonmann/default.nix
@@ -61,6 +61,7 @@ in {
mailboxGreeting = ./greetings/anrufbeantworter.wav;
phones = {
"jannel-mobile" = { model = "sip-client"; password = "changeme101"; };
+ "f47f35a3fb72" = { model = "cisco-9971"; label = "Jannelfon"; password = "changeme101"; };
};
};
};
diff --git a/modules/voip/default.nix b/modules/voip/default.nix
index d63e13d..5502160 100644
--- a/modules/voip/default.nix
+++ b/modules/voip/default.nix
@@ -89,6 +89,7 @@ in {
"= /directory-list.xml" = { alias = "${directory.listFile}"; extraConfig = "default_type text/xml;"; };
"= /intercom.xml" = { alias = "${directory.intercomFile}"; extraConfig = "default_type text/xml;"; };
"= /contacts.json" = { alias = "${directoryJson}"; extraConfig = ''default_type application/json; add_header Access-Control-Allow-Origin "*";''; };
+ "= /auth" = { extraConfig = ''default_type text/plain; return 200 "AUTHORIZED";''; };
"/" = {
root = "${diagram.webRoot}";
extraConfig = lib.optionalString (!hasRuntimeSecrets) "index index.html;";
diff --git a/modules/voip/phones.nix b/modules/voip/phones.nix
index 33be46e..5ae0080 100644
--- a/modules/voip/phones.nix
+++ b/modules/voip/phones.nix
@@ -14,6 +14,14 @@ let
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;
diff --git a/modules/voip/provisioning/templates/cisco-8961.nix b/modules/voip/provisioning/templates/cisco-8961.nix
index ebb2a5a..29d2cf0 100644
--- a/modules/voip/provisioning/templates/cisco-8961.nix
+++ b/modules/voip/provisioning/templates/cisco-8961.nix
@@ -195,7 +195,7 @@ in {
true
2
- sip8961.9-4-2ES-14
+
0
1
@@ -213,7 +213,7 @@ in {
utf-8
1
-
+ http://${serverAddress}:${toString directoryPort}/auth
http://${serverAddress}:${toString directoryPort}/directory.xml
diff --git a/modules/voip/provisioning/templates/cisco-9971.nix b/modules/voip/provisioning/templates/cisco-9971.nix
new file mode 100644
index 0000000..99f0915
--- /dev/null
+++ b/modules/voip/provisioning/templates/cisco-9971.nix
@@ -0,0 +1,285 @@
+{ lib }:
+
+let
+ cisco = import ./cisco-base.nix { inherit lib; };
+in {
+ desktopSize = "640x480x24";
+ thumbnailSize = "123x111";
+
+ # Return a list of { name, content } provisioning files for this phone.
+ # provisioning/default.nix wraps each with pkgs.writeText for the linkFarm.
+ mkFiles =
+ { mac, label, displayName, password, serverAddress, ntpServer
+ , sipPort ? 5060
+ , directoryPort ? 8080
+ , familyLineEnabled ? false
+ , familyLineLabel ? "Familie"
+ , familyLinePassword ? ""
+ , intercomEnabled ? false
+ , intercomPassword ? ""
+ , intercomLineIndex ? 2
+ , allExtensions ? []
+ , allStarExtensions ? []
+ , hasTrunk ? false
+ , hasIntercomButton ? false
+ , pageExtension ? null
+ , blfPersons ? []
+ }:
+ let
+ # Line button assignments:
+ # button 1 / lineIndex 1 — personal/location L1 line (always present)
+ # button 2 / lineIndex 2 — family L2 line (when familyLineEnabled)
+ # button N / lineIndex N — intercom (when intercomEnabled; N = 2 or 3)
+ intercomButton = if familyLineEnabled then 3 else 2;
+ firstBlfButton = 1 + (if familyLineEnabled then 1 else 0) + (if intercomEnabled then 1 else 0) + 1;
+
+ dialplanFile = cisco.dialplanFilename mac;
+
+ configXml = ''
+
+
+ SIP
+ admin
+ password
+
+
+ D.M.YA
+ Central Europe Standard/Daylight Time
+
+
+ ${ntpServer}
+
+
+
+
+
+
+
+
+ ${toString sipPort}
+
+ ${serverAddress}
+
+
+
+
+
+
+
+
+ 5060
+
+
+
+
+ true
+
+
+ true
+ x-serviceuri-cfwdall
+ x-cisco-serviceuri-pickup
+ x-cisco-serviceuri-opickup
+ x-cisco-serviceuri-gpickup
+ x-cisco-serviceuri-meetme
+ x-cisco-serviceuri-abbrdial
+ 2
+ 2
+ 2
+ 0
+ true
+
+
+ 6
+ 10
+ 180
+ 3600
+ 5
+ 120
+ 120
+ 5
+ 500
+ 4000
+ 70
+ false
+ None
+
+ false
+ 3
+ ${(builtins.substring 0 12 label)}
+ 1
+ false
+
+
+ 9
+ ${displayName}
+ USECALLMANAGER
+ ${toString sipPort}
+ ${mac}
+ ${displayName}
+
+ 2
+
+ 3
+ ${mac}
+ ${password}
+ 1
+ *97
+ ${mac}
+
+ true
+ true
+ true
+ true
+
+
+ ${if familyLineEnabled then ''
+
+ 9
+ ${familyLineLabel}
+ USECALLMANAGER
+ ${toString sipPort}
+ ${mac}-l2
+ ${familyLineLabel}
+
+ 2
+
+ 3
+ ${mac}-l2
+ ${familyLinePassword}
+ 3
+ *97
+ ${mac}-l2
+
+ true
+ true
+ true
+ true
+
+
+ '' else ""}${if intercomEnabled then ''
+
+ 23
+ Intercom
+ USECALLMANAGER
+ ${toString sipPort}
+ ${mac}-intercom
+ Intercom
+
+ 3
+ Auto Answer with Speakerphone
+
+ 3
+ ${mac}-intercom
+ ${intercomPassword}
+ 1
+ 1
+ ${mac}-intercom
+
+ '' else ""}${lib.concatImapStrings (i: p: ''
+
+ 21
+ ${p.displayName}
+ 1
+ ${p.extension}
+
+ '') blfPersons}
+ ${toString sipPort}
+ 16348
+ 20134
+ 184
+ ${dialplanFile}
+
+ 1
+
+
+ true
+ 2
+
+
+
+ 0
+ 1
+ 0
+ 1
+ 1,2,3,4,5,6,7
+ 0
+ 00:00
+ 00:00
+ 00:05
+ 1
+
+
+ en_US
+ utf-8
+
+ 1
+ http://${serverAddress}:${toString directoryPort}/auth
+ http://${serverAddress}:${toString directoryPort}/directory.xml
+
+
+
+
+
+ 2
+
+ Missed Calls
+ Application:Cisco/MissedCalls
+
+
+
+
+ Received Calls
+ Application:Cisco/ReceivedCalls
+
+
+
+
+ Placed Calls
+ Application:Cisco/PlacedCalls
+
+
+
+
+ Voicemail
+ Application:Cisco/Voicemail
+
+
+
+
+
+ 1
+ 0
+
+
+ 3804
+
+
+ false
+
+ '';
+
+ # Dial template: the phone tests patterns top-to-bottom and dials as soon
+ # as digits match a pattern with timeout="0", or after the timeout for
+ # timeout > 0. Explicit patterns must come before the catch-all.
+ h = builtins.hashString "sha256" (builtins.toJSON {
+ inherit mac allExtensions allStarExtensions hasTrunk hasIntercomButton intercomLineIndex blfPersons;
+ });
+ 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}";
+ extMatch = ext: " ";
+
+ dialplanXml = ''
+
+ ${versionStamp}
+ ${lib.optionalString hasIntercomButton
+ " "}
+ ${lib.concatMapStrings (ext: extMatch ext + "\n") allExtensions}
+ ${lib.concatMapStrings (ext: extMatch ext + "\n") allStarExtensions}
+
+
+ '';
+
+ in [
+ { name = cisco.configFilename mac; content = configXml; }
+ { name = cisco.dialplanFilename mac; content = dialplanXml; }
+ ];
+}