9.7 KiB
Mk7 "PineAP Open Access Point" Card — Design
Goal: Rebuild the Open AP tab (#/pineap/open, views.pineap_open) to visually and functionally match the genuine Mark 7 Pineapple's /PineAP/open page: a single "PineAP Open Access Point" card with Open AP network settings and filter notices. The current grouped-toggles "PineAP settings" card is removed.
Reference: Verified against the real Mk7 firmware bundle served at http://172.16.42.1:1471/ (main.ce5a318adf590e170f6d.js), component app-open-wifi-view (selector), template root QAt.
1. Frontend — views.pineap_open
Rendered inside pineapShell(root, '#/pineap/open'). A single .pineap-title-card (matching the clone's existing Mk7 card styling) containing, top to bottom:
- Title: "PineAP Open Access Point" (
.pineap-card-title). - Subtitle: "The Open SSID is advertised without encryption. When client association is enabled, " followed by a filter-dependent sentence selected by the SSID/client filter modes:
- ssid=allow ∧ client=allow → "any client in the filter configuration may connect to any SSID in the filter configuration."
- ssid=deny ∧ client=allow → "any client not in the filter configuration may connect to any SSID in the filter configuration."
- ssid=allow ∧ client=deny → "any client in the filter configuration may connect to any SSID not in the filter configuration."
- ssid=deny ∧ client=deny → "any client not in the filter configuration may connect to any SSID not in the filter configuration."
- ("filter configuration" links to
#/pineap/filtering.)
- Fields (two
.rows; labels above inputs per the clone'slabel { display:block }style):- Open SSID — text input.
- BSSID — text input.
- Channel —
<select>of channels 1–11, values1..11. - Current Country —
<select>of the Mk7 82-entry country list (see §3).
- Switches (existing
.switchslider CSS):- Hidden — bound to the Open AP
hiddenstate. - Respond to all probe requests (impersonate all networks) — bound to the PineAP
karmastate (the Mk7 binds this exact toggle toPineAPSettings.karma). The daemon exposes no readable karma/mimic state (verified:get_confighas nomimic), so the toggle is session-tracked (module-level flag, default off, updated on Save) — consistent with the Overview'skarmaOnhandling. Save still POSTsmimic.
- Hidden — bound to the Open AP
- Info line (below the switches, small muted text): "The Open access point will be " + ("hidden" if hidden else "advertised") + "." plus, when karma is on, the filter sentence from the subtitle's logic. The Mk7's ", and SSIDs from the Spoofed SSID Pool will be advertised" clause is omitted: the daemon exposes no readable broadcast/advertise state (pre-existing limitation; the Overview's advertise toggle has the same issue).
- Filter notice boxes (stacked, below the info line), mirroring the Mk7's infobox conditions and actions:
- If SSID filter fetched ∧ Open SSID not in SSID-filter allow list ∧ mode=allow → error box:
The open SSID "<ssid>" is not included in the filter allow list, clients will not be able to connect.+ Add Allowed button →POST /api/pineap/filters/ssid {action:'add', value: ssid}then reload. - If SSID filter fetched ∧ Open SSID in SSID-filter list ∧ mode=deny → box: open SSID is blocked by the filter deny list + Remove Filter button →
POST /api/pineap/filters/ssid {action:'delete', value: ssid}then reload. - If mode=allow ∧ SSID filter list non-empty ∧ karma → info box: "Remember to add SSIDs you wish to impersonate to the PineAP SSID filter (Deny)." + Change Filters link →
#/pineap/filtering. - If client filter fetched ∧ client mode=allow ∧ client list empty → error box: "The PineAP Client filter is set to allow mode. When using this mode, " + the mode-dependent sentence + Change Mode button (→
POST /api/pineap/filters/client {action:'set_mode', mode:'deny'}, then reload) + Change Filters link.
- If SSID filter fetched ∧ Open SSID not in SSID-filter allow list ∧ mode=allow → error box:
- Save button (
.btn) + muted note "Applying reconfigures the radio — you may be disconnected briefly." - Save payload:
POST /api/pineap/wifi/set_apwith{open: {ssid, bssid, hidden, enabled, channel, country}}whereenabledis the value loaded fromget_ap(preserved; the Mk7 card has no Enabled control), plusPOST /api/pineap/mimic {enable: <karma switch>}. All viaPromise.allSettled. - Feedback via
App.toast('Open AP saved')/App.toast(..., 'error').
Old card removed: the "PineAP" grouped settings card (Enable PineAP / Karma / Capture SSIDs / Advertise / Logging toggles + footer MAC line + batched saveCfg) is deleted. Enable PineAP and Karma are covered by the Overview mode bar; SSID-pool capture/advertise by Overview Quick Settings; logging by Evil WPA / Logging. Nothing else in the app references the removed code.
2. Backend — server.py
Extend the two existing handlers; no new routes, no daemon changes.
h_pineap_wifi_get_ap (currently ~line 1479)
The open object gains fields; reads remain UCI-based (_uci_wifi_iface('wlan0open'), radio config via _uci_wifi_iface('radio0') or equivalent):
ssid—open_cfg.get('ssid') or ''.bssid—open_cfg.get('macaddr') or ''(the Open AP interface MAC; changed from the ssidpool bssid). Verified: the only consumer ofopen.bssidis the old Open tab footer, which is being removed; the Overview uses onlyopen.enabled.hidden—open_cfg.get('hidden') == '1'.channel— radio0 channel (2.4GHz radio hosting the open AP), as int if numeric, elseNone.country— radio0 country code or''.enabled,targetunchanged (enabled=open_cfg.get('disabled') == '0';target= pool target).
h_pineap_wifi_set_ap (currently ~line 1505)
The open branch gains support:
- Pass
bssid(→macaddr),hidden,channelthrough to the daemonPUT /api/settings/wifi/set_apopen config (the daemon persists these towireless.wlan0open, verified on-device: it writesmacaddr,hidden,channel,encryption). - Country and radio channel are applied directly: if
open.countrypresent,uci set wireless.radio0.country=<code>; ifopen.channelpresent,uci set wireless.radio0.channel=<n>; thenuci commit wireless+wifi reload. (The daemon's iface-level channel write is inert for the actual radio — verified.) Only write/reload when the value changed. enabledsemantics unchanged (preserve existing state when not supplied).
Karma
"Respond to all probe requests" is saved by the frontend via the existing POST /api/pineap/mimic {enable}; no backend change.
3. Data lists (frontend constants)
- Channels (2.4GHz): 1..11 (labels "Channel N (24NN MHz)" matching Mk7; values 1..11).
- Countries: the Mk7's 82-entry list (US United States, DZ Algeria, AR Argentina, AU Australia, AT Austria, BH Bahrain, BM Bermuda, BO Bolivia, BR Brazil, BG Bulgaria, CA Canada, CL Chile, CN China, CO Colombia, CR Costa Rica, CS Cyprus, CZ Czech Republic, DK Denmark, DO Dominican Republic, EC Ecuador, EG Egypt, SV El Salvador, EE Estonia, FI Finland, FR France, DE Germany, GR Greece, GT Guatemala, HN Honduras, HK Hong Kong, IS Iceland, IN India, ID Indonesia, IE Ireland, PK Islamic Republic of Pakistan, IL Israel, IT Italy, JM Jamaica, JO Jordan, KE Kenya, KW Kuwait, LB Lebanon, LI Liechtenstein, LT Lithuania, LU Luxembourg, MU Mauritius, MX Mexico, MA Morocco, NL Netherlands, NZ New Zealand, NO Norway, OM Oman, PA Panama, PE Peru, PH Philippines, PL Poland, PT Portuagal, PR Puerto Rico, QA Qatar, KR Republic of Korea (South Korea), RO Romania, RU Russia, SA Saudi Arabia, SG Singapore, SI Slovenia, SK Slovak Republic, ZA South Africa, ES Spain, LK Sri Lanka, SE Sweden, CH Switzerland, TW Taiwan, TH Thailand, TT Trinidad and Tobago, TN Tunisia, TR Turkey, UA Ukraine, AE United Arab Emirates, GB United Kingdom, UY Uraguay, VE Venezuela, VN Vietnam).
4. Filter state data (frontend)
- SSID filter:
GET /api/pineap/filters/ssid→{mode, entries}(existing handler). Modeallow/deny. - Client filter:
GET /api/pineap/filters/client→{mode, entries}. - Mutations use the existing
action-based API (matchingviews.pineap_filtering):- Add Allowed / Remove Filter:
POST /api/pineap/filters/ssidwith{action:'add'|'delete', value: ssid}. - Change Mode:
POST /api/pineap/filters/clientwith{action:'set_mode', mode:'deny'}.
- Add Allowed / Remove Filter:
5. Error handling & UX
- All load fetches use
.catch(() => ({ data: {} }))(existing pattern); the card still renders with empty fields if a fetch fails. - Save =
Promise.allSettled([set_ap, mimic]); toast "Open AP saved" only if all fulfilled, else "Some settings failed" (error); always re-runload(). - Add Allowed / Remove Filter / Change Mode: fire, toast, re-run
load(); failures toast 'Failed' (existing pattern).
6. Testing
- Backend unit tests (
tests/test_pineap_proxy.py): extend the existing mocked-UCI tests —test_wifi_get_ap_reads_uci_wireless: mockuci show wireless.wlan0openwithssid/macaddr/hidden, anduci show wireless.radio0withchannel/country; assert the newopenfields.test_wifi_set_ap_builds_configs: open config now carriesbssid/channel; country write triggersuci set wireless.radio0.country(mockdevice_run, assert theuci set+uci commit+wifi reloadcalls) only when country changed.- Full unittest loop stays green.
- JS: delimiter-balance checker (
C:\Users\root\AppData\Local\Temp\opencode\js_balance.py); no node available. - Deploy + on-device: deploy via
scripts/deploy.ps1; restart webui; curl save-path round-trip: set Open SSID/hidden viawifi/set_ap, verifyuci show wireless.wlan0open, restore original values. Visual check againsthttp://172.16.42.1:1471/#/PineAP/open.