diff --git a/README.md b/README.md
index df43597..1338d30 100644
--- a/README.md
+++ b/README.md
@@ -10,10 +10,12 @@ artifact, regenerated from NetBox and committed so history shows what the
| file | what it is |
| --- | --- |
-| `export_topology.py` | Run inside the NetBox Django shell. Walks every field/distribution switch's cable path (through patch panels and splice modules) and dumps the resulting device graph as JSON. Read-only. |
-| `topo_full.json` | Latest exported snapshot: devices (id, name, role, site, status) + edges (physical links, one per cable). |
+| `export_topology.py` | Run inside the NetBox Django shell. Walks every field/distribution switch's cable path (through patch panels and splice modules), then pulls every real `Cable` among the devices it found so each edge carries the exact port name on both ends. Read-only. |
+| `topo_full.json` | Latest exported snapshot: devices (id, name, role, site, status) + edges (`{a, a_port, b, b_port}`, one per cable). |
| `generate_drawio.py` | Pure-Python renderer: `topo_full.json` → `Field-Switch-Topology.drawio`. No NetBox access needed. |
| `Field-Switch-Topology.drawio` | The diagram. Open at [app.diagrams.net](https://app.diagrams.net) (or the desktop app, or the VS Code draw.io extension) -- free, no license needed. |
+| `render_png.js` + `render_host.html` | Renders the `.drawio` through a real draw.io instance (embed.diagrams.net, driven headlessly with Puppeteer) to produce `Field-Switch-Topology.png` -- an actual render, not an approximation. Needs `npm install puppeteer` and network access. |
+| `Field-Switch-Topology.png` | Static preview of the diagram, for anyone who wants a quick look without opening draw.io (viewing it in the Gitea file browser, a PR diff, etc.). |
Two distribution switches (`fche-sw17a`, `dmo-lob-sw35a`) anchor two
fabrics; everything downstream is field switches daisy-chained through
@@ -22,12 +24,18 @@ both fabrics for a documented reason (spare/inventory, decommissioning, or
planned/not-yet-installed) -- those are called out in their own box on the
diagram rather than silently dropped.
+Every connector is labeled with the port at each end (e.g. `9 ↔ 1`), and
+switches/patch panels look the part: switches get draw.io's built-in network
+switch icon, patch panels get a small rack-unit look (header strip + port
+ticks) rather than a plain box.
+
Connectors in the `.drawio` file are glued by shape id (draw.io's native
`source`/`target` reference on each edge) -- drag a switch or panel in the
-app and its links follow. That's different from this project's earlier
-Visio (`.vsdx`) attempt, where glue had to be hand-written as cell formulas
-because the format has no simpler mechanism; draw.io does this natively,
-which is a big part of why this repo uses it instead.
+app and its links follow, port-tick decorations included since they're
+parented to their panel's body cell. That's different from this project's
+earlier Visio (`.vsdx`) attempt, where glue had to be hand-written as cell
+formulas because the format has no simpler mechanism; draw.io does this
+natively, which is a big part of why this repo uses it instead.
## Regenerating
@@ -38,12 +46,16 @@ docker exec netbox-netbox-1 python3 /opt/netbox/netbox/manage.py shell \
-c "$(cat topology/export_topology.py)" 2>/dev/null > topology/topo_full.json
python3 topology/generate_drawio.py topology/topo_full.json topology/Field-Switch-Topology.drawio
+
+# optional -- refreshes the PNG preview to match; first run: npm install puppeteer
+node topology/render_png.js topology/Field-Switch-Topology.drawio topology/Field-Switch-Topology.png
```
-Commit both files together. The diff on `topo_full.json` is the real
-changelog -- it shows exactly which device or cable changed between two
-snapshots; the `.drawio` diff is mostly noise (coordinates) but is kept in
-sync so the rendered diagram never drifts from what NetBox actually says.
+Commit all three (`topo_full.json`, `.drawio`, `.png`) together. The diff on
+`topo_full.json` is the real changelog -- it shows exactly which device or
+cable changed between two snapshots; the `.drawio`/`.png` diffs are mostly
+noise (coordinates, pixels) but are kept in sync so the rendered diagram
+never drifts from what NetBox actually says.
## Gotchas learned the hard way
diff --git a/topology/Field-Switch-Topology.drawio b/topology/Field-Switch-Topology.drawio
index 2f48ed5..b804f08 100644
--- a/topology/Field-Switch-Topology.drawio
+++ b/topology/Field-Switch-Topology.drawio
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/topology/Field-Switch-Topology.png b/topology/Field-Switch-Topology.png
new file mode 100644
index 0000000..b489a86
Binary files /dev/null and b/topology/Field-Switch-Topology.png differ
diff --git a/topology/export_topology.py b/topology/export_topology.py
index 335334b..aaa21d9 100644
--- a/topology/export_topology.py
+++ b/topology/export_topology.py
@@ -12,8 +12,18 @@ The script only reads data -- it never modifies NetBox. If a switch's
connected_endpoints comes back empty when it clearly shouldn't, that is a
NetBox cable-path bug, not a bug in this script -- see the "gotchas" section
of the repo README before assuming the physical link doesn't exist.
+
+Two passes:
+ 1. Walk every switch's cable path (Interface.trace()) to find every
+ device -- patch panel, splice module, other switch -- it physically
+ touches. This is just to build the device set; NetBox's cached path
+ is what can go stale (see README), which is why pass 2 doesn't rely
+ on it for the edges themselves.
+ 2. Pull every real Cable among those devices directly, so each edge
+ carries the exact port name on both ends (what generate_drawio.py
+ labels connectors with) instead of just "these two are connected."
"""
-from dcim.models import Device, Interface
+from dcim.models import Device, Interface, Cable
import json
switch_ids = set(
@@ -27,24 +37,38 @@ ifaces = Interface.objects.filter(
).select_related("device")
touched_devices = set(switch_ids)
-edges = set() # (device_id_a, device_id_b), deduplicated, any two adjacent devices on a physical chain
-
for i in ifaces:
path = i._path
if not path or not path.is_active:
continue
- chain = []
for hop in i.trace():
for item in hop:
for t in item:
dev = getattr(t, "device", None)
if dev:
- if not chain or chain[-1] != dev.id:
- chain.append(dev.id)
touched_devices.add(dev.id)
- for a, b in zip(chain, chain[1:]):
- if a != b:
- edges.add(tuple(sorted((a, b))))
+
+cables = Cable.objects.filter(
+ terminations__termination_type__model__in=["interface", "frontport", "rearport"],
+).distinct()
+
+edges = {}
+for c in cables:
+ terms = list(c.terminations.all())
+ a_side = [t for t in terms if t.cable_end == "A"]
+ b_side = [t for t in terms if t.cable_end == "B"]
+ for ta in a_side:
+ for tb in b_side:
+ da = getattr(ta.termination, "device", None)
+ db = getattr(tb.termination, "device", None)
+ if not da or not db:
+ continue
+ if da.id in touched_devices and db.id in touched_devices and da.id != db.id:
+ key = tuple(sorted((da.id, db.id)))
+ edges[key] = {
+ "a": da.id, "a_port": ta.termination.name,
+ "b": db.id, "b_port": tb.termination.name,
+ }
devs = Device.objects.filter(id__in=touched_devices).select_related(
"site", "role", "device_type"
@@ -63,6 +87,6 @@ out = {
}
for d in devs
],
- "edges": [[a, b] for a, b in sorted(edges)],
+ "edges": list(edges.values()),
}
print(json.dumps(out))
diff --git a/topology/generate_drawio.py b/topology/generate_drawio.py
index 2d2af82..ea332e6 100644
--- a/topology/generate_drawio.py
+++ b/topology/generate_drawio.py
@@ -5,7 +5,8 @@ Render topo_full.json (produced by export_topology.py) as a draw.io
Edges reference shapes by id (source=/target=), which is how draw.io's
native glue works -- no hand-rolled formulas needed: dragging a shape in
-the app keeps every attached line attached.
+the app keeps every attached line attached. Patch panel port ticks/accent
+strips are parented to the panel's body cell, so they move with it too.
Usage:
python3 generate_drawio.py [topo_full.json] [Field-Switch-Topology.drawio]
@@ -19,13 +20,13 @@ OUT_PATH = sys.argv[2] if len(sys.argv) > 2 else "Field-Switch-Topology.drawio"
data = json.load(open(IN_PATH))
devs = {d["id"]: d for d in data["devices"]}
-edges = data["edges"]
+edges = data["edges"] # [{a, a_port, b, b_port}, ...]
# ---------------- components ----------------
adj = {d: [] for d in devs}
-for a, b in edges:
- adj[a].append(b)
- adj[b].append(a)
+for e in edges:
+ adj[e["a"]].append(e["b"])
+ adj[e["b"]].append(e["a"])
parent = {d: d for d in devs}
def find(x):
@@ -37,8 +38,8 @@ def union(a, b):
ra, rb = find(a), find(b)
if ra != rb:
parent[ra] = rb
-for a, b in edges:
- union(a, b)
+for e in edges:
+ union(e["a"], e["b"])
from collections import defaultdict
comps = defaultdict(list)
for d in devs:
@@ -74,9 +75,9 @@ depth_dmo, yslot_dmo = layout_tree(7, dmo_comp)
# ---------------- geometry constants (px) ----------------
SWITCH_ICON = 46 # icon box; label renders below it
SWITCH_LABEL_H = 30
-PANEL_W, PANEL_H = 130, 34
-COL_W = 190
-ROW_H = 82
+PANEL_W, PANEL_H = 132, 42
+COL_W = 200
+ROW_H = 86
PAD = 40
LABEL_STRIP = 34
SECTION_GAP = 70
@@ -137,7 +138,7 @@ STYLE = {
"spare": dict(fill="#F1F1F2", stroke="#9AA0AA", font="#5B6478", dashed=1, bold=0),
"decom": dict(fill="#FBE6E2", stroke="#C04B34", font="#8A3423", dashed=1, bold=0),
"planned": dict(fill="#FDF1CF", stroke="#B8862C", font="#7A5C00", dashed=1, bold=0),
- "panel": dict(fill="#EEF0F2", stroke="#8C97A6", font="#495566", dashed=0, bold=0),
+ "panel": dict(fill="#3A4457", stroke="#242C38", font="#FFFFFF", dashed=0, bold=0),
}
def style_for(dev):
@@ -166,9 +167,9 @@ def vertex(cell_id, value, x, y, w, h, style, parent="1"):
f''
)
-def edge(cell_id, source, target, style):
+def edge(cell_id, source, target, style, value=""):
add_cell(
- f''
)
@@ -198,10 +199,29 @@ add_cell(
f''
)
-# node shapes -- switches get a real network-switch icon (label rendered below it by
-# draw.io's own verticalLabelPosition, not baked into the geometry), patch panels get
-# a plain labeled box.
-node_style = {}
+# ---------------- node shapes ----------------
+# Switches: a real network-switch icon, hostname caption below it.
+# Patch panels: a small rack-style unit -- dark body, a lighter header strip,
+# and a row of port ticks along the bottom -- built from cells parented to
+# the panel's own body cell, so the whole unit moves together when dragged.
+def panel_shape(did, x, y, w, h, name, st):
+ body_id = f"n{did}"
+ vertex(body_id, name, x, y, w, h,
+ f'rounded=1;arcSize=10;whiteSpace=wrap;html=1;fillColor={st["fill"]};strokeColor={st["stroke"]};'
+ f'fontColor={st["font"]};fontSize=9;verticalAlign=bottom;spacingBottom=5;')
+ # header strip (lighter steel), relative to the body
+ vertex(f"{body_id}-strip", "", 5, 4, w - 10, 5,
+ 'rounded=1;arcSize=40;fillColor=#8C97A6;strokeColor=none;', parent=body_id)
+ # port ticks along the top-middle, evenly spaced
+ n_ticks = 6
+ tick_w, tick_h = 10, 7
+ usable = w - 16
+ gap = usable / n_ticks
+ for k in range(n_ticks):
+ tx = 8 + gap * k + (gap - tick_w) / 2
+ vertex(f"{body_id}-t{k}", "", tx, 15, tick_w, tick_h,
+ 'rounded=0;fillColor=#2A313D;strokeColor=#5B6577;strokeWidth=0.75;', parent=body_id)
+
for did, dev in devs.items():
if did not in pos:
continue
@@ -209,11 +229,7 @@ for did, dev in devs.items():
st = style_for(dev)
dashed = "dashed=1;dashPattern=4 3;" if st["dashed"] else ""
if dev["role"] == "patch-panel":
- style = (
- f'rounded=0;whiteSpace=wrap;html=1;fillColor={st["fill"]};strokeColor={st["stroke"]};'
- f'fontColor={st["font"]};fontSize=9;{dashed}'
- )
- vertex(f"n{did}", dev["name"], x, y, w, h, style)
+ panel_shape(did, x, y, w, h, dev["name"], st)
else:
style = (
'shape=mxgraph.networks.switch;html=1;verticalLabelPosition=bottom;verticalAlign=top;'
@@ -222,18 +238,23 @@ for did, dev in devs.items():
)
vertex(f"n{did}", dev["name"], x, y, SWITCH_ICON, SWITCH_ICON, style)
-# connectors -- glued natively via source/target, orthogonal routing
-for idx, (a, b) in enumerate(edges):
+# ---------------- connectors, labeled with the port at each end ----------------
+for idx, e in enumerate(edges):
+ a, b = e["a"], e["b"]
if a in pos and b in pos:
+ label = f'{e["a_port"]} ↔ {e["b_port"]}'
edge(f"e{idx}", f"n{a}", f"n{b}",
"edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#9AA6B8;strokeWidth=1.2;"
- "jettySize=auto;orthogonalLoop=1;")
+ "startArrow=none;endArrow=none;"
+ "jettySize=auto;orthogonalLoop=1;fontSize=7.5;fontColor=#7C8AA6;"
+ "labelBackgroundColor=#F7F8FBcc;align=center;verticalAlign=middle;",
+ value=label)
# legend
legend_items = [
("#2F5FAD", "#173D6E", "Distribution switch"),
("#F3F5FA", "#7C8AA6", "Field switch"),
- ("#EEF0F2", "#8C97A6", "Patch panel / splice module"),
+ ("#3A4457", "#242C38", "Patch panel / splice module"),
("#F1F1F2", "#9AA0AA", "Spare / decommissioned / planned"),
]
lx = MARGIN
diff --git a/topology/render_host.html b/topology/render_host.html
new file mode 100644
index 0000000..cb92783
--- /dev/null
+++ b/topology/render_host.html
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/topology/render_png.js b/topology/render_png.js
new file mode 100644
index 0000000..6f0e55b
--- /dev/null
+++ b/topology/render_png.js
@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+/**
+ * Render a .drawio file to PNG via a real draw.io instance (embed.diagrams.net),
+ * driven headlessly with Puppeteer -- this is an actual render, not an
+ * approximation, so switch icons, patch panel styling, and port labels all
+ * come out exactly as they'd look opening the file in the app.
+ *
+ * Needs `npm install puppeteer` first (downloads its own Chromium; not the
+ * system browser). Needs network access to embed.diagrams.net.
+ *
+ * Usage:
+ * node render_png.js Field-Switch-Topology.drawio Field-Switch-Topology.png
+ */
+const puppeteer = require('puppeteer');
+const fs = require('fs');
+const path = require('path');
+
+const xmlPath = process.argv[2];
+const outPath = process.argv[3];
+if (!xmlPath || !outPath) {
+ console.error('usage: node render_png.js ');
+ process.exit(1);
+}
+const xml = fs.readFileSync(xmlPath, 'utf8');
+
+(async () => {
+ const browser = await puppeteer.launch({
+ headless: 'new',
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
+ });
+ const page = await browser.newPage();
+ await page.setViewport({ width: 1700, height: 1300 });
+ page.on('pageerror', (err) => console.error('PAGE ERROR:', err));
+
+ await page.evaluateOnNewDocument((x) => { window.__XML__ = x; }, xml);
+ await page.goto('file://' + path.join(__dirname, 'render_host.html'), {
+ waitUntil: 'load',
+ timeout: 30000,
+ });
+
+ let dataUrl = null;
+ const deadline = Date.now() + 60000;
+ while (Date.now() < deadline) {
+ dataUrl = await page.evaluate(() => window.__RESULT__);
+ const err = await page.evaluate(() => window.__ERR__);
+ if (err) throw new Error('draw.io export error: ' + err);
+ if (dataUrl) break;
+ await new Promise((r) => setTimeout(r, 500));
+ }
+ if (!dataUrl) throw new Error('timed out waiting for draw.io to export (network to embed.diagrams.net?)');
+
+ const base64 = dataUrl.split(',')[1];
+ fs.writeFileSync(outPath, Buffer.from(base64, 'base64'));
+ console.log('wrote', outPath, fs.statSync(outPath).size, 'bytes');
+ await browser.close();
+})().catch((e) => { console.error(e); process.exit(1); });
diff --git a/topology/topo_full.json b/topology/topo_full.json
index 885ac67..5a93c54 100644
--- a/topology/topo_full.json
+++ b/topology/topo_full.json
@@ -1 +1 @@
-{"devices": [{"id": 57, "name": "afl-104", "role": "patch-panel", "role_name": "Patch Panel", "site": "Highway 104 Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 92, "name": "afl-cr13", "role": "patch-panel", "role_name": "Patch Panel", "site": "County Road 13 Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 85, "name": "afl-lem", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lemler Substation", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 58, "name": "afl-mag", "role": "patch-panel", "role_name": "Patch Panel", "site": "Magnolia Springs Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 82, "name": "afl-swf", "role": "patch-panel", "role_name": "Patch Panel", "site": "Southwest Foley Substation", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 42, "name": "bs-104-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Highway 104 Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 93, "name": "bs-cr13-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "County Road 13 Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 40, "name": "bs-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 38, "name": "bs-lil-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lillian Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 43, "name": "bs-mag-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Magnolia Springs Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 41, "name": "bs-nwt-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "North Water Tank", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 39, "name": "bs-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 7, "name": "dmo-lob-sw35a", "role": "dist-switch", "role_name": "Distribution Switch", "site": "DMO-LOB", "status": "active", "device_type": "S4112F-ON"}, {"id": 2, "name": "FCHE-PP-32B", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "LS Series 288-Port LC UPC"}, {"id": 4, "name": "FCHE-PP17D", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "FIS 48-Port LC UPC"}, {"id": 3, "name": "FCHE-PP34A", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "FIS 144-Port LC UPC 12-Slot"}, {"id": 1, "name": "fche-sw17a", "role": "dist-switch", "role_name": "Distribution Switch", "site": "FCHE Cable Headend", "status": "active", "device_type": "ProCurve 5412zl"}, {"id": 63, "name": "fis-nwt", "role": "patch-panel", "role_name": "Patch Panel", "site": "North Water Tank", "status": "active", "device_type": "F1-DW2X"}, {"id": 11, "name": "gs-nff-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "North Foley Field Gas Regulator", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 19, "name": "ls-avls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Aaronville Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 14, "name": "ls-bsls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Bay Street Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 20, "name": "ls-clls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cornerstone Lodge Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 10, "name": "ls-guls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Gulftel Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 25, "name": "ls-mals-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Marlin Place Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 23, "name": "ls-mmls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Majestic Manor Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 26, "name": "ls-mpls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "McSwain Place Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 9, "name": "ls-pals-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Peachtree Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 21, "name": "ls-rols-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "River Oaks Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 24, "name": "ls-spls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "South Pointe Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 22, "name": "ls-vhls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Villages at Hickory Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 76, "name": "primex-ebert", "role": "patch-panel", "role_name": "Patch Panel", "site": "Ebert Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 79, "name": "primex-holk", "role": "patch-panel", "role_name": "Patch Panel", "site": "Holk Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 80, "name": "primex-lem", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lemler Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 81, "name": "primex-lil", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lillian Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 83, "name": "primex-wpb", "role": "patch-panel", "role_name": "Patch Panel", "site": "West Perdido Beach Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 33, "name": "rec-10000-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Bay St @ Azalea Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 34, "name": "rec-10008-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cedar @ Michigan Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 17, "name": "rec-10016-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Underwood Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 28, "name": "rec-10018-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Summerdale Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 18, "name": "rec-10136-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "WHEP Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 15, "name": "rec-10160-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Coca Cola Plant Hwy 104 Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 32, "name": "rec-10660-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Old Foley Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 16, "name": "rec-13032-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Peachtree Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 30, "name": "rec-50094-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Randall Ave @ Hwy 98 Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 31, "name": "rec-52882-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Pollard Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 87, "name": "sec-holk-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Holk Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 88, "name": "sec-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 89, "name": "sec-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 45, "name": "spare-163", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 46, "name": "spare-164", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 47, "name": "spare-165", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 48, "name": "spare-166", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 36, "name": "sub-ebert-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Ebert Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 13, "name": "sub-fair-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Fairhope Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 29, "name": "sub-holk-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Holk Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 49, "name": "sub-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 35, "name": "sub-lem-sw01-decommissioned", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "decommissioning", "device_type": "2930F"}, {"id": 8, "name": "sub-lil-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lillian Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 27, "name": "sub-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 37, "name": "sub-wpb-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "West Perdido Beach Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 90, "name": "wtr-cwt-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cypress Water Tank", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 12, "name": "wtr-ecc-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Elberta Cross Connect", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 44, "name": "wtr-w12-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Well 12", "status": "planned", "device_type": "IES3110-8TFP-R"}, {"id": 51, "name": "xpak-10000", "role": "patch-panel", "role_name": "Patch Panel", "site": "Bay St @ Azalea Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 53, "name": "xpak-10008", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cedar @ Michigan Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 72, "name": "xpak-10016", "role": "patch-panel", "role_name": "Patch Panel", "site": "Underwood Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 71, "name": "xpak-10018", "role": "patch-panel", "role_name": "Patch Panel", "site": "Summerdale Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 74, "name": "xpak-10136", "role": "patch-panel", "role_name": "Patch Panel", "site": "WHEP Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 54, "name": "xpak-10160", "role": "patch-panel", "role_name": "Patch Panel", "site": "Coca Cola Plant Hwy 104 Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 64, "name": "xpak-10660", "role": "patch-panel", "role_name": "Patch Panel", "site": "Old Foley Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 66, "name": "xpak-13032", "role": "patch-panel", "role_name": "Patch Panel", "site": "Peachtree Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 68, "name": "xpak-50094", "role": "patch-panel", "role_name": "Patch Panel", "site": "Randall Ave @ Hwy 98 Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 67, "name": "xpak-52882", "role": "patch-panel", "role_name": "Patch Panel", "site": "Pollard Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 50, "name": "xpak-avls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Aaronville Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 52, "name": "xpak-bsls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Bay Street Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 55, "name": "xpak-clls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cornerstone Lodge Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 91, "name": "xpak-cwt", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cypress Water Tank", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 56, "name": "xpak-guls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Gulftel Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 60, "name": "xpak-mals", "role": "patch-panel", "role_name": "Patch Panel", "site": "Marlin Place Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 59, "name": "xpak-mmls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Majestic Manor Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 61, "name": "xpak-mpls", "role": "patch-panel", "role_name": "Patch Panel", "site": "McSwain Place Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 62, "name": "xpak-nff", "role": "patch-panel", "role_name": "Patch Panel", "site": "North Foley Field Gas Regulator", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 65, "name": "xpak-pals", "role": "patch-panel", "role_name": "Patch Panel", "site": "Peachtree Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 69, "name": "xpak-rols", "role": "patch-panel", "role_name": "Patch Panel", "site": "River Oaks Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 70, "name": "xpak-spls", "role": "patch-panel", "role_name": "Patch Panel", "site": "South Pointe Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 73, "name": "xpak-vhls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Villages at Hickory Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}], "edges": [[1, 2], [1, 3], [1, 4], [2, 52], [2, 60], [2, 70], [2, 79], [2, 85], [2, 91], [3, 54], [3, 62], [3, 63], [3, 65], [3, 66], [4, 12], [4, 64], [4, 76], [7, 13], [7, 67], [7, 68], [8, 81], [9, 65], [10, 56], [11, 62], [14, 52], [15, 54], [16, 66], [17, 72], [18, 74], [19, 50], [20, 55], [21, 69], [22, 73], [23, 59], [24, 70], [25, 60], [26, 61], [27, 82], [28, 71], [29, 79], [30, 68], [31, 67], [32, 64], [33, 51], [34, 53], [36, 76], [37, 83], [38, 81], [39, 82], [40, 85], [41, 63], [42, 57], [43, 58], [49, 80], [50, 52], [50, 69], [51, 53], [51, 79], [53, 80], [55, 61], [56, 65], [57, 63], [57, 92], [58, 82], [59, 61], [59, 73], [66, 72], [69, 73], [71, 74], [72, 74], [76, 83], [79, 80], [79, 87], [80, 82], [80, 88], [81, 83], [82, 85], [82, 89], [90, 91], [92, 93]]}
+{"devices": [{"id": 57, "name": "afl-104", "role": "patch-panel", "role_name": "Patch Panel", "site": "Highway 104 Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 92, "name": "afl-cr13", "role": "patch-panel", "role_name": "Patch Panel", "site": "County Road 13 Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 85, "name": "afl-lem", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lemler Substation", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 58, "name": "afl-mag", "role": "patch-panel", "role_name": "Patch Panel", "site": "Magnolia Springs Base Station", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 82, "name": "afl-swf", "role": "patch-panel", "role_name": "Patch Panel", "site": "Southwest Foley Substation", "status": "active", "device_type": "AFL 12-Port Splice Module"}, {"id": 42, "name": "bs-104-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Highway 104 Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 93, "name": "bs-cr13-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "County Road 13 Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 40, "name": "bs-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 38, "name": "bs-lil-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lillian Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 43, "name": "bs-mag-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Magnolia Springs Base Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 41, "name": "bs-nwt-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "North Water Tank", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 39, "name": "bs-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 7, "name": "dmo-lob-sw35a", "role": "dist-switch", "role_name": "Distribution Switch", "site": "DMO-LOB", "status": "active", "device_type": "S4112F-ON"}, {"id": 2, "name": "FCHE-PP-32B", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "LS Series 288-Port LC UPC"}, {"id": 4, "name": "FCHE-PP17D", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "FIS 48-Port LC UPC"}, {"id": 3, "name": "FCHE-PP34A", "role": "patch-panel", "role_name": "Patch Panel", "site": "FCHE Cable Headend", "status": "active", "device_type": "FIS 144-Port LC UPC 12-Slot"}, {"id": 1, "name": "fche-sw17a", "role": "dist-switch", "role_name": "Distribution Switch", "site": "FCHE Cable Headend", "status": "active", "device_type": "ProCurve 5412zl"}, {"id": 63, "name": "fis-nwt", "role": "patch-panel", "role_name": "Patch Panel", "site": "North Water Tank", "status": "active", "device_type": "F1-DW2X"}, {"id": 11, "name": "gs-nff-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "North Foley Field Gas Regulator", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 19, "name": "ls-avls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Aaronville Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 14, "name": "ls-bsls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Bay Street Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 20, "name": "ls-clls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cornerstone Lodge Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 10, "name": "ls-guls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Gulftel Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 25, "name": "ls-mals-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Marlin Place Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 23, "name": "ls-mmls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Majestic Manor Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 26, "name": "ls-mpls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "McSwain Place Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 9, "name": "ls-pals-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Peachtree Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 21, "name": "ls-rols-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "River Oaks Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 24, "name": "ls-spls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "South Pointe Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 22, "name": "ls-vhls-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Villages at Hickory Lift Station", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 76, "name": "primex-ebert", "role": "patch-panel", "role_name": "Patch Panel", "site": "Ebert Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 79, "name": "primex-holk", "role": "patch-panel", "role_name": "Patch Panel", "site": "Holk Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 80, "name": "primex-lem", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lemler Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 81, "name": "primex-lil", "role": "patch-panel", "role_name": "Patch Panel", "site": "Lillian Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 83, "name": "primex-wpb", "role": "patch-panel", "role_name": "Patch Panel", "site": "West Perdido Beach Substation", "status": "active", "device_type": "Primex 12-Port LC Panel"}, {"id": 33, "name": "rec-10000-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Bay St @ Azalea Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 34, "name": "rec-10008-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cedar @ Michigan Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 17, "name": "rec-10016-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Underwood Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 28, "name": "rec-10018-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Summerdale Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 18, "name": "rec-10136-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "WHEP Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 15, "name": "rec-10160-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Coca Cola Plant Hwy 104 Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 32, "name": "rec-10660-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Old Foley Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 16, "name": "rec-13032-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Peachtree Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 30, "name": "rec-50094-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Randall Ave @ Hwy 98 Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 31, "name": "rec-52882-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Pollard Rd Recloser", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 87, "name": "sec-holk-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Holk Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 88, "name": "sec-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 89, "name": "sec-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 45, "name": "spare-163", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 46, "name": "spare-164", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 47, "name": "spare-165", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 48, "name": "spare-166", "role": "field-switch", "role_name": "Field Switch", "site": "FCHE Cable Headend", "status": "inventory", "device_type": "IES3220-8T4F"}, {"id": 36, "name": "sub-ebert-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Ebert Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 13, "name": "sub-fair-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Fairhope Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 29, "name": "sub-holk-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Holk Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 49, "name": "sub-lem-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 35, "name": "sub-lem-sw01-decommissioned", "role": "field-switch", "role_name": "Field Switch", "site": "Lemler Substation", "status": "decommissioning", "device_type": "2930F"}, {"id": 8, "name": "sub-lil-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Lillian Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 27, "name": "sub-swf-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Southwest Foley Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 37, "name": "sub-wpb-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "West Perdido Beach Substation", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 90, "name": "wtr-cwt-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Cypress Water Tank", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 12, "name": "wtr-ecc-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Elberta Cross Connect", "status": "active", "device_type": "IES3110-8TFP-R"}, {"id": 44, "name": "wtr-w12-sw01", "role": "field-switch", "role_name": "Field Switch", "site": "Well 12", "status": "planned", "device_type": "IES3110-8TFP-R"}, {"id": 51, "name": "xpak-10000", "role": "patch-panel", "role_name": "Patch Panel", "site": "Bay St @ Azalea Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 53, "name": "xpak-10008", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cedar @ Michigan Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 72, "name": "xpak-10016", "role": "patch-panel", "role_name": "Patch Panel", "site": "Underwood Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 71, "name": "xpak-10018", "role": "patch-panel", "role_name": "Patch Panel", "site": "Summerdale Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 74, "name": "xpak-10136", "role": "patch-panel", "role_name": "Patch Panel", "site": "WHEP Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 54, "name": "xpak-10160", "role": "patch-panel", "role_name": "Patch Panel", "site": "Coca Cola Plant Hwy 104 Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 64, "name": "xpak-10660", "role": "patch-panel", "role_name": "Patch Panel", "site": "Old Foley Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 66, "name": "xpak-13032", "role": "patch-panel", "role_name": "Patch Panel", "site": "Peachtree Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 68, "name": "xpak-50094", "role": "patch-panel", "role_name": "Patch Panel", "site": "Randall Ave @ Hwy 98 Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 67, "name": "xpak-52882", "role": "patch-panel", "role_name": "Patch Panel", "site": "Pollard Rd Recloser", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 50, "name": "xpak-avls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Aaronville Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 52, "name": "xpak-bsls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Bay Street Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 55, "name": "xpak-clls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cornerstone Lodge Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 91, "name": "xpak-cwt", "role": "patch-panel", "role_name": "Patch Panel", "site": "Cypress Water Tank", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 56, "name": "xpak-guls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Gulftel Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 60, "name": "xpak-mals", "role": "patch-panel", "role_name": "Patch Panel", "site": "Marlin Place Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 59, "name": "xpak-mmls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Majestic Manor Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 61, "name": "xpak-mpls", "role": "patch-panel", "role_name": "Patch Panel", "site": "McSwain Place Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 62, "name": "xpak-nff", "role": "patch-panel", "role_name": "Patch Panel", "site": "North Foley Field Gas Regulator", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 65, "name": "xpak-pals", "role": "patch-panel", "role_name": "Patch Panel", "site": "Peachtree Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 69, "name": "xpak-rols", "role": "patch-panel", "role_name": "Patch Panel", "site": "River Oaks Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 70, "name": "xpak-spls", "role": "patch-panel", "role_name": "Patch Panel", "site": "South Pointe Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}, {"id": 73, "name": "xpak-vhls", "role": "patch-panel", "role_name": "Patch Panel", "site": "Villages at Hickory Lift Station", "status": "active", "device_type": "xPAK 6-Port SC/APC"}], "edges": [{"a": 7, "a_port": "4", "b": 13, "b_port": "9"}, {"a": 41, "a_port": "Gi1/10", "b": 63, "b_port": "P2:6"}, {"a": 63, "a_port": "P2:8", "b": 3, "b_port": "55"}, {"a": 1, "a_port": "E3", "b": 3, "b_port": "51"}, {"a": 19, "a_port": "10", "b": 50, "b_port": "2"}, {"a": 14, "a_port": "10", "b": 52, "b_port": "2"}, {"a": 20, "a_port": "9", "b": 55, "b_port": "1"}, {"a": 10, "a_port": "9", "b": 56, "b_port": "1"}, {"a": 25, "a_port": "9", "b": 60, "b_port": "1"}, {"a": 23, "a_port": "10", "b": 59, "b_port": "2"}, {"a": 26, "a_port": "10", "b": 61, "b_port": "2"}, {"a": 9, "a_port": "10", "b": 65, "b_port": "2"}, {"a": 21, "a_port": "10", "b": 69, "b_port": "2"}, {"a": 24, "a_port": "9", "b": 70, "b_port": "1"}, {"a": 22, "a_port": "10", "b": 73, "b_port": "2"}, {"a": 52, "a_port": "2", "b": 50, "b_port": "1"}, {"a": 50, "a_port": "2", "b": 69, "b_port": "1"}, {"a": 69, "a_port": "2", "b": 73, "b_port": "1"}, {"a": 73, "a_port": "2", "b": 59, "b_port": "1"}, {"a": 59, "a_port": "2", "b": 61, "b_port": "1"}, {"a": 61, "a_port": "2", "b": 55, "b_port": "1"}, {"a": 65, "a_port": "2", "b": 56, "b_port": "1"}, {"a": 33, "a_port": "10", "b": 51, "b_port": "2"}, {"a": 34, "a_port": "10", "b": 53, "b_port": "2"}, {"a": 17, "a_port": "10", "b": 72, "b_port": "2"}, {"a": 28, "a_port": "9", "b": 71, "b_port": "1"}, {"a": 18, "a_port": "10", "b": 74, "b_port": "2"}, {"a": 15, "a_port": "9", "b": 54, "b_port": "1"}, {"a": 32, "a_port": "9", "b": 64, "b_port": "1"}, {"a": 16, "a_port": "10", "b": 66, "b_port": "2"}, {"a": 30, "a_port": "9", "b": 68, "b_port": "1"}, {"a": 31, "a_port": "9", "b": 67, "b_port": "1"}, {"a": 7, "a_port": "11", "b": 67, "b_port": "1"}, {"a": 7, "a_port": "1", "b": 68, "b_port": "1"}, {"a": 66, "a_port": "2", "b": 72, "b_port": "1"}, {"a": 72, "a_port": "2", "b": 74, "b_port": "1"}, {"a": 74, "a_port": "2", "b": 71, "b_port": "1"}, {"a": 51, "a_port": "2", "b": 53, "b_port": "1"}, {"a": 1, "a_port": "E12", "b": 2, "b_port": "7"}, {"a": 1, "a_port": "E2", "b": 4, "b_port": "10"}, {"a": 4, "a_port": "31", "b": 64, "b_port": "1"}, {"a": 4, "a_port": "27", "b": 12, "b_port": "9"}, {"a": 76, "a_port": "6", "b": 36, "b_port": "10"}, {"a": 4, "a_port": "12", "b": 76, "b_port": "5"}, {"a": 76, "a_port": "8", "b": 83, "b_port": "3"}, {"a": 83, "a_port": "4", "b": 37, "b_port": "10"}, {"a": 83, "a_port": "6", "b": 81, "b_port": "12"}, {"a": 81, "a_port": "10", "b": 8, "b_port": "9"}, {"a": 2, "a_port": "49", "b": 70, "b_port": "1"}, {"a": 2, "a_port": "3", "b": 60, "b_port": "1"}, {"a": 85, "a_port": "2", "b": 40, "b_port": "Gi1/10"}, {"a": 2, "a_port": "15", "b": 85, "b_port": "1"}, {"a": 85, "a_port": "2", "b": 82, "b_port": "3"}, {"a": 82, "a_port": "6", "b": 39, "b_port": "10"}, {"a": 82, "a_port": "6", "b": 58, "b_port": "1"}, {"a": 58, "a_port": "1", "b": 43, "b_port": "Gi1/9"}, {"a": 2, "a_port": "4", "b": 52, "b_port": "1"}, {"a": 3, "a_port": "96", "b": 54, "b_port": "1"}, {"a": 3, "a_port": "1", "b": 66, "b_port": "1"}, {"a": 3, "a_port": "53", "b": 62, "b_port": "1"}, {"a": 62, "a_port": "1", "b": 11, "b_port": "9"}, {"a": 3, "a_port": "51", "b": 65, "b_port": "1"}, {"a": 2, "a_port": "14", "b": 79, "b_port": "5"}, {"a": 79, "a_port": "4", "b": 29, "b_port": "10"}, {"a": 79, "a_port": "4", "b": 51, "b_port": "1"}, {"a": 82, "a_port": "1", "b": 27, "b_port": "9"}, {"a": 79, "a_port": "6", "b": 87, "b_port": "10"}, {"a": 80, "a_port": "4", "b": 88, "b_port": "10"}, {"a": 79, "a_port": "6", "b": 80, "b_port": "3"}, {"a": 80, "a_port": "2", "b": 82, "b_port": "1"}, {"a": 82, "a_port": "2", "b": 89, "b_port": "9"}, {"a": 53, "a_port": "2", "b": 80, "b_port": "1"}, {"a": 80, "a_port": "2", "b": 49, "b_port": "10"}, {"a": 81, "a_port": "12", "b": 38, "b_port": "Gi1/9"}, {"a": 2, "a_port": "7", "b": 91, "b_port": "1"}, {"a": 90, "a_port": "9", "b": 91, "b_port": "1"}, {"a": 42, "a_port": "Gi1/10", "b": 57, "b_port": "2"}, {"a": 57, "a_port": "1", "b": 63, "b_port": "P2:6"}, {"a": 57, "a_port": "2", "b": 92, "b_port": "1"}, {"a": 93, "a_port": "Gi1/9", "b": 92, "b_port": "1"}]}