Start a wardriving session.
(self, interfaces=None, gps_port=None, device_name=None)
| 1511 | sec_rows = [ |
| 1512 | ('Open (no encryption)', sec['open'], '#dc2626'), |
| 1513 | ('Enhanced Open (OWE)', sec['owe'], '#0891b2'), |
| 1514 | ('WEP (broken)', sec['wep'], '#ea580c'), |
| 1515 | ('WPA (legacy)', sec['wpa'], '#ca8a04'), |
| 1516 | ('WPA2', sec['wpa2'], '#2563eb'), |
| 1517 | ('WPA3 / SAE', sec['wpa3'], '#16a34a'), |
| 1518 | ('Other / unknown', sec['other'], '#64748b'), |
| 1519 | ] |
| 1520 | sec_html = '' |
| 1521 | for label, count, color in sec_rows: |
| 1522 | pct = (count / total * 100) if total else 0 |
| 1523 | sec_html += ( |
| 1524 | f'<div class="bar-row"><span class="bar-lbl">{esc(label)}</span>' |
| 1525 | f'<span class="bar-track"><span class="bar-fill" style="width:{pct:.1f}%;background:{color}"></span></span>' |
| 1526 | f'<span class="bar-num">{count} <span class="muted">({pct:.0f}%)</span></span></div>' |
| 1527 | ) |
| 1528 | |
| 1529 | # --- Band bars --- |
| 1530 | band_rows = [('2.4 GHz', d['bands'].get('2.4GHz', 0), '#f59e0b'), |
| 1531 | ('5 GHz', d['bands'].get('5GHz', 0), '#3b82f6'), |
| 1532 | ('6 GHz', d['bands'].get('6GHz', 0), '#8b5cf6')] |
| 1533 | band_html = '' |
| 1534 | for label, count, color in band_rows: |
| 1535 | pct = (count / total * 100) if total else 0 |
| 1536 | band_html += ( |
| 1537 | f'<div class="bar-row"><span class="bar-lbl">{esc(label)}</span>' |
| 1538 | f'<span class="bar-track"><span class="bar-fill" style="width:{pct:.1f}%;background:{color}"></span></span>' |
| 1539 | f'<span class="bar-num">{count} <span class="muted">({pct:.0f}%)</span></span></div>' |
| 1540 | ) |
| 1541 | |
| 1542 | # --- Channel usage (top 14 by count) --- |
| 1543 | chan_items = sorted(d['channels'].items(), key=lambda kv: (-kv[1], kv[0]))[:14] |
| 1544 | chan_max = max((c for _, c in chan_items), default=1) |
| 1545 | chan_html = '' |
| 1546 | for ch, cnt in sorted(chan_items, key=lambda kv: kv[0]): |
| 1547 | h = (cnt / chan_max * 100) if chan_max else 0 |
| 1548 | chan_html += ( |
| 1549 | f'<div class="chan"><span class="chan-bar" style="height:{h:.0f}%" ' |
| 1550 | f'title="Channel {ch}: {cnt}"></span><span class="chan-num">{cnt}</span>' |
| 1551 | f'<span class="chan-lbl">{ch}</span></div>' |
| 1552 | ) |
| 1553 | if not chan_html: |
| 1554 | chan_html = '<p class="muted">No channel data.</p>' |
| 1555 | |
| 1556 | def net_table(rows, cols): |
| 1557 | if not rows: |
| 1558 | return '<p class="muted">None.</p>' |
| 1559 | head = ''.join(f'<th>{esc(c[0])}</th>' for c in cols) |
| 1560 | body = '' |
| 1561 | for r in rows: |
| 1562 | cells = ''.join(f'<td>{esc(str(c[1](r)))}</td>' for c in cols) |
| 1563 | body += f'<tr>{cells}</tr>' |
| 1564 | return f'<table><thead><tr>{head}</tr></thead><tbody>{body}</tbody></table>' |
| 1565 | |
| 1566 | net_cols = [ |
| 1567 | ('SSID', lambda r: r.get('ssid') or '<hidden>'), |
| 1568 | ('BSSID', lambda r: r.get('bssid') or ''), |
| 1569 | ('Security', lambda r: r.get('security') or 'Open'), |
| 1570 | ('Band', lambda r: r.get('band') or ''), |
no test coverage detected