From 651a96a6d9fbe010e34844116529111827e7cfae Mon Sep 17 00:00:00 2001 From: Jakob Lechner Date: Tue, 29 Jul 2025 21:23:06 +0200 Subject: [PATCH] Add phonebook --- hosts/pbx/services/fieldpoc/default.nix | 1 + hosts/pbx/services/fieldpoc/extensions.nix | 47 ++++ hosts/pbx/services/fieldpoc/mkphonebook.py | 279 +++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 hosts/pbx/services/fieldpoc/extensions.nix create mode 100644 hosts/pbx/services/fieldpoc/mkphonebook.py diff --git a/hosts/pbx/services/fieldpoc/default.nix b/hosts/pbx/services/fieldpoc/default.nix index bd63f23..9aa1bec 100644 --- a/hosts/pbx/services/fieldpoc/default.nix +++ b/hosts/pbx/services/fieldpoc/default.nix @@ -7,6 +7,7 @@ in { imports = [ ./accounts.nix + ./extensions.nix ]; sops.secrets."fieldpoc/omm" = { diff --git a/hosts/pbx/services/fieldpoc/extensions.nix b/hosts/pbx/services/fieldpoc/extensions.nix new file mode 100644 index 0000000..bf28d01 --- /dev/null +++ b/hosts/pbx/services/fieldpoc/extensions.nix @@ -0,0 +1,47 @@ +{pkgs, ...}: let + mkphonebook = pkgs.python3.pkgs.buildPythonPackage { + pname = "fieldpoc-mkphonebook"; + version = "1.0"; + + src = ./mkphonebook.py; + + dontUnpack = true; + + propagatedBuildInputs = [pkgs.makeWrapper]; + + format = "other"; + + installPhase = '' + mkdir -p $out/lib/mkphonebook + mkdir -p $out/bin + cp $src $out/lib/mkphonebook/script.py + + makeWrapper ${pkgs.python3.interpreter} $out/bin/mkphonebook \ + --add-flags "$out/lib/mkphonebook/script.py" \ + --set PYTHONPATH "${pkgs.python3.pkgs.pyyaml}/${pkgs.python3.sitePackages}" + ''; + }; +in { + environment.systemPackages = [ + ( + pkgs.writeShellScriptBin "fieldpoc-load-extensions" '' + set -e + + tmpfile="$(mktemp -p /tmp tmp.extensions.XXXXXXXXXX.json)" + trap "rm -f $tmpfile" 0 2 3 15 + + ${pkgs.yq}/bin/yq \ + '.extensions[] |= with_entries(select(.key | IN("name", "type", "dialout_allowed", "trunk", "static_target", "callgroup_members", "sip_password", "dect_ipei")))' \ + "$1" > $tmpfile + + cat "$tmpfile" | /run/wrappers/bin/sudo -u fieldpoc tee /var/lib/fieldpoc/extensions.json >/dev/null + + curl -s --fail --json '{}' http://127.0.0.1:9437/reload + + ${mkphonebook}/bin/mkphonebook "$1" "/persist/html/index.html" + + rm -f $tmpfile + '' + ) + ]; +} diff --git a/hosts/pbx/services/fieldpoc/mkphonebook.py b/hosts/pbx/services/fieldpoc/mkphonebook.py new file mode 100644 index 0000000..94221bf --- /dev/null +++ b/hosts/pbx/services/fieldpoc/mkphonebook.py @@ -0,0 +1,279 @@ +import os +import sys +import yaml +from collections import defaultdict + + +def generate_html(extensions): + # Icons per Typ + type_icons = { + "sip": "πŸ“±", + "dect": "πŸ“ž", + "static": "πŸ“Œ", + "callgroup": "πŸ‘₯", + "temp": "⏳", + "default": "☎️", + } + + def make_color(idx): + hue = (idx * 30 + idx % 2 * 180) % 360 + return f"hsl({hue}, 50%, 95%)" + + emergency_color = "hsl(0, 75%, 75%)" + + # Generiere Farbzuordnung je Location + locations = sorted(set(info.get("location", "") for info in extensions.values())) + location_colors = {} + for idx, loc in enumerate(locations): + location_colors[loc] = make_color(idx + 1) + + # Generiere HTML + html_header = """ + + + + + + Telefonbuch + + + + +

πŸ“– Telefonbuch

+ +
+ + +
+ + + + + + + + + + + + +""" + + # Sortiere EintrΓ€ge: + # 1. Notrufnummern (3-Stellig) numerisch sortiert + # 2. alle weiteren Extensions: nach Location, dann Name + rows = sorted( + extensions.items(), + key=lambda x: ( + (0, x[0], "") + if len(x[0]) < 4 + else (1, x[1].get("location", ""), x[1].get("name", "")) + ), + ) + html_rows = "" + + for ext, info in rows: + name = info.get("name", "") + typ = info.get("type", "default") + location = info.get("location", "") + vanity = info.get("vanity", "") + dialin = info.get("dialin", "") + icon = type_icons.get(typ, type_icons["default"]) + row_color = ( + emergency_color if len(ext) < 4 else location_colors.get(location, "#fff") + ) + + name_cell = f"{name}" + if dialin: + name_cell = f'{name_cell}' + + html_rows += f""" + + + + + + + + +""" + + html_footer = """ + + +
πŸ§‘ NameπŸ”€ πŸ“ OrtπŸ”§ Typ
{icon}{ext}{name_cell}{vanity}{location}{typ}
+ + +""" + + return html_header + html_rows + html_footer + + +def main(): + INPUT_FILE, OUTPUT_FILE = sys.argv[1:] + + with open(INPUT_FILE, "r") as f: + data = yaml.safe_load(f) + + extensions = data.get("extensions", {}) + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: + f.write(generate_html(extensions)) + + +if __name__ == "__main__": + main()