Ekahau: Measured APs per Python-Script automatisch umbenennen
Wer mit Ekahau eine Bestandsmessung macht, kennt das Bild: Nach dem Survey ist der Grundriss voller Access Points mit Namen wie Measured AP-61:31, Measured AP-62:f1 oder Measured AP-37:12. Für die Auswertung im eigenen Kopf reicht das vielleicht noch. Spätestens im Report für den Kunden will aber niemand MAC-Fragmente lesen, sondern die Namen, die auch im Management-System und an der Decke stehen: AP-EG-101, AP-OG2-214 und so weiter.
Bei einer größeren Messung mit weit über 200 Access Points war mir klar: Das benenne ich nicht von Hand um. In diesem Artikel beschreibe ich, wie ich die Ekahau-Projektdatei per Python-Script umbenannt habe – und über welche Stolpersteine ich dabei gefallen bin.
Ein Blick in die .esx-Datei
Eine Ekahau-Projektdatei (.esx) ist im Kern nichts anderes als ein ZIP-Archiv. Benennt man sie um oder entpackt sie direkt, findet man drei Arten von Inhalten:
- Binärdateien – für unseren Zweck uninteressant
- Bilddateien – Grundrisse und Ähnliches
- JSON-Dateien – und die sind einen genauen Blick wert
In den JSON-Dateien steckt praktisch das komplette Datenmodell des Projekts, lesbar und mit jedem Editor zu öffnen. Für das Umbenennen sind drei davon relevant:
| Datei | Inhalt |
|---|---|
accessPoints.json |
Die Access Points mit ID, Name und dem Flag mine (eigener AP oder Fremdnetz) |
measuredRadios.json |
Verknüpft jeden AP mit den IDs seiner gemessenen Funkzellen |
accessPointMeasurements.json |
Die eigentlichen Messungen: BSSID, SSID, Kanal |
Die Idee war damit klar: Für jeden eigenen AP über die Messungen an seine BSSIDs kommen, daraus die MAC-Adresse des Geräts ableiten, in einer Namensliste nachschlagen, den Namen in accessPoints.json ersetzen und alles wieder einpacken.
Herausforderung 1: Drei Dateien, eine Wahrheit
Der Name des APs steht in einer Datei, die MAC-Adressen in einer anderen, und verbunden sind beide nur über IDs in einer dritten. Der Weg ist also immer: AP → Radios → Measurement-IDs → Messung mit BSSID. Das ist kein Hexenwerk, aber man muss das Datenmodell einmal verstanden haben. Meine erste Version hat das mit drei verschachtelten Schleifen gelöst – bei ein paar tausend Einträgen funktioniert das, eleganter ist ein Dictionary über die Measurement-IDs.
Bemerkenswert: Der Name Measured AP-xx:xx ist kein verlässlicher Hinweis auf das Gerät. Bei mir hieß zum Beispiel ein AP Measured AP-61:a0, obwohl alle seine BSSIDs mit 61:9x begannen. Wer sich auf den Ekahau-Namen verlässt, ordnet also im Zweifel falsch zu. Die Zuordnung muss über die tatsächlich gemessenen BSSIDs laufen.
Herausforderung 2: Von der BSSID zur Geräte-MAC
Ekahau sieht in der Luft nur die BSSIDs – also eine eigene MAC-Adresse pro SSID und Band. In meiner Namensliste stand dagegen die Basis-MAC des Access Points, so wie sie im Management-System geführt wird.
Bei OmniAccess Stellar lässt sich das zum Glück sauber ableiten: Die Basis-MAC endet auf 0, und die BSSIDs zählen im letzten Nibble hoch. Typischerweise sieht das so aus:
| Basis-MAC | BSSIDs 2,4 GHz | BSSIDs 5 GHz |
|---|---|---|
…:61:30 |
…:61:31 bis …:61:34 |
…:61:39 bis …:61:3c |
Ersetzt man das letzte Zeichen der BSSID durch eine 0, hat man die Basis-MAC. Wichtig: Das ist herstellerspezifisch. Andere Hersteller vergeben BSSIDs nach anderen Schemata – wer das Script für andere Hardware nutzen möchte, muss genau diese eine Funktion anpassen.
Herausforderung 3: Die Namensliste
Die Zuordnung MAC → Name kam aus einem Inventar-Export des Management-Systems. Der enthielt allerdings nicht nur APs, sondern auch alle Switches – insgesamt mehrere hundert Einträge. Das ist kein Problem, solange nach MAC gesucht wird. Beim Durchsehen fielen aber die üblichen Spuren gewachsener Daten auf:
- Tippfehler wie
Swtich-…oder uneinheitliche Schreibweisen wieAp-…stattAP-… - Platzhalter wie
Switch-xxx-Raum-xxx, die nie gepflegt wurden - MAC-Adressen in Groß- statt Kleinbuchstaben – mit einem einfachen Stringvergleich findet man diese Einträge nie
- Ein mehrzeiliges Feld im Export, das die CSV um eine kaputte Zeile erweitert hat
Die Konsequenz: MAC-Adressen vor dem Vergleich immer normalisieren (Kleinbuchstaben, einheitliche Trennzeichen), und die Liste einmal kritisch ansehen, bevor sie in einen Kundenreport einfließt. Das Script kann nur so gute Namen vergeben, wie in der Quelle stehen.
Herausforderung 4: Wenn Ekahau zwei APs zu einem macht
Der spannendste Fund kam erst bei der Auswertung: Ein einzelner AP in Ekahau hatte Messungen von zwei unterschiedlichen Basis-MACs. Ekahau hatte während des Surveys zwei physische Access Points zu einem zusammengefasst. Meine erste Script-Version hat das nicht bemerkt – sie hat für jede Messung neu nachgeschlagen, und der zuletzt gefundene Name hat gewonnen.
In der überarbeiteten Version sammelt das Script deshalb pro AP alle gefundenen Namen. Ist es genau einer, wird umbenannt. Sind es mehrere, bleibt der AP unverändert und das Script meldet einen Konflikt. Solche Fälle muss man ohnehin in Ekahau selbst auflösen, indem man den AP wieder aufteilt – das kann und sollte ein Script nicht stillschweigend entscheiden.
Herausforderung 5: Sauber wieder einpacken
Nach dem Ändern von accessPoints.json muss das Verzeichnis wieder zu einer .esx gezippt werden – mit genau der Struktur, die Ekahau erwartet, also ohne zusätzlichen Ordner im Archiv. Mit Pythons shutil.make_archive klappt das zuverlässig. Das Script schreibt das Ergebnis immer in eine neue Datei und lässt das Original unangetastet.
Das ist auch die wichtigste Regel beim Manipulieren von Projektdateien überhaupt: Immer mit einer Kopie arbeiten. Ekahau unterstützt das Bearbeiten der Datei von außen offiziell nicht, und eine kaputte Projektdatei nach einem langen Messtag ist das Letzte, was man haben möchte.
Das Ergebnis
Bei der Messung lief das Script über:
- über 200 eigene Access Points
- mehr als 2.000 gemessene BSSIDs auf 2,4 und 5 GHz
- den Großteil der APs hat es umbenannt, der Rest trug bereits den korrekten Namen
- einen AP, in dem Ekahau zwei Geräte zusammengefasst hatte und der in Ekahau selbst geprüft werden musste
Am Ende stand kein einziger Measured AP mehr im Projekt. Das Umbenennen selbst dauert Sekunden statt eines halben Tages Klickarbeit.
Ein nützliches Nebenprodukt ist die CSV-Datei, die das Script nebenbei schreibt: eine vollständige Liste aller BSSIDs mit AP-Name, Basis-MAC, SSID und Kanal. Die eignet sich hervorragend als Anhang zur Dokumentation und hilft später bei der Fehlersuche, wenn ein Client-Log nur eine BSSID ausspuckt.
Das Script
Hier die überarbeitete Version. Sie erwartet die Projektdatei und eine CSV mit den Spalten name und mac:
python3 ekahau_rename.py Projekt.esx new_names.csv
#!/usr/bin/env python3
"""Benennt 'Measured AP-xx:xx' in einem Ekahau-Projekt anhand einer MAC-Liste um."""
import csv, json, shutil, sys, zipfile
from pathlib import Path
def norm(mac):
return mac.strip().lower().replace("-", ":")
def base_mac(bssid):
# Stellar: Basis-MAC endet auf 0, die BSSIDs zählen im letzten Nibble hoch
return norm(bssid)[:-1] + "0"
esx = Path(sys.argv[1])
names = {norm(r["mac"]): r["name"].strip()
for r in csv.DictReader(open(sys.argv[2], encoding="utf-8-sig"))}
work = esx.with_suffix("")
with zipfile.ZipFile(esx) as z:
z.extractall(work)
load = lambda f: json.load(open(work / f, encoding="utf-8"))
aps = load("accessPoints.json")
rad = load("measuredRadios.json")["measuredRadios"]
meas = {m["id"]: m for m in load("accessPointMeasurements.json")["accessPointMeasurements"]}
with open("AP-Radio-Export.csv", "w", newline="", encoding="utf-8") as f:
out = csv.writer(f)
out.writerow(["AP-ID", "ALT", "NEU", "BASE-MAC", "BSSID", "SSID", "Kanal"])
for ap in aps["accessPoints"]:
if not ap.get("mine"):
continue
rows = [meas[i] for r in rad if r["accessPointId"] == ap["id"]
for i in r["accessPointMeasurementIds"] if i in meas]
hits = {names[base_mac(m["mac"])] for m in rows if base_mac(m["mac"]) in names}
old = ap["name"]
if len(hits) == 1:
ap["name"] = hits.pop()
elif len(hits) > 1:
print(f"KONFLIKT {old}: {sorted(hits)} – Ekahau hat mehrere APs zusammengefasst")
for m in rows:
out.writerow([ap["id"], old, ap["name"], base_mac(m["mac"]), m["mac"],
m.get("ssid", ""), m.get("channelByCenterFrequencyDefinedNarrowChannels")])
json.dump(aps, open(work / "accessPoints.json", "w", encoding="utf-8"), indent=2, ensure_ascii=False)
new = shutil.make_archive(f"{work}_renamed", "zip", work)
shutil.move(new, f"{work}_renamed.esx")
shutil.rmtree(work)
Ausgegeben werden Projekt_renamed.esx und AP-Radio-Export.csv. Zusätzliche Pakete braucht das Script nicht, die Python-Standardbibliothek reicht.
Fazit
Ekahau-Projektdateien sind offener, als man denkt. Wer einmal in die JSON-Dateien geschaut hat, findet dort viel mehr als nur AP-Namen – und kann wiederkehrende Fleißarbeit automatisieren. Die eigentliche Arbeit steckte aber weniger im Code als in den Daten: herstellerspezifische BSSID-Schemata, gewachsene Inventarlisten und ein Survey-Tool, das gelegentlich zwei Geräte für eines hält.
Spannend finde ich übrigens auch den LiDAR-basierten JustGo-Survey, mit dem sich ohne vorhandenen Grundriss messen lässt – aber das ist ein Thema für einen eigenen Artikel.
Hinweis: Es handelt sich um ein privates Projekt, das weder mit Ekahau noch mit Alcatel-Lucent Enterprise oder meinem Arbeitgeber SPIE verbunden ist. Das Bearbeiten von Projektdateien wird vom Hersteller nicht unterstützt. Nutzung auf eigene Verantwortung – bitte immer nur mit einer Kopie der Projektdatei arbeiten.