Remove one scanning interface so the KEY1 AP can own it. hostapd needs the radio in AP mode; if the interface stayed in the scan set, _prepare_interface would force it back to managed and collapse the AP. Removing it from self.interfaces stops the scan loop from touching
(self, prefer=None)
| 1631 | |
| 1632 | from report_common import page_shell |
| 1633 | |
| 1634 | subtitle_html = ( |
| 1635 | f'<div class="sub">Session <strong>{esc(self.session_id)}</strong> · Device {dn}</div>' |
| 1636 | f'<div class="sub">Coverage: {_fmt_dt(d["first_seen"])} → {_fmt_dt(d["last_seen"])}</div>' |
| 1637 | ) |
| 1638 | concern_note = ( |
| 1639 | f'<div class="note bad">{exposed} network(s) offer no meaningful encryption. ' |
| 1640 | 'Devices joining these are exposed to eavesdropping and interception.</div>' |
| 1641 | ) if exposed else '<p class="muted">No open or WEP networks were observed.</p>' |
| 1642 | cam_section = f'<h2>Cameras & surveillance devices</h2>{cam_tbl}' if d['cameras'] else '' |
| 1643 | |
| 1644 | body_html = f""" |
| 1645 | <h2>Executive summary</h2> |
| 1646 | <div class="grid4"> |
| 1647 | {stat_card("Wi-Fi networks", total)} |
| 1648 | {stat_card("Open + WEP", exposed, f"{exposed_pct:.0f}% of networks")} |
| 1649 | {stat_card("WPA3 / SAE", sec['wpa3'])} |
| 1650 | {stat_card("Cameras", len(d['cameras']))} |
| 1651 | </div> |
| 1652 | <p class="sub" style="margin-top:12px">{esc(grade_note)}</p> |
| 1653 | |
| 1654 | <h2>Security posture</h2> |
| 1655 | {sec_html} |